diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..2ffd6c3 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,134 @@ +# AGENTS.md - Instruções para IA + +## Inicialização + +Ao iniciar uma sessão neste projeto, execute: + +```bash +# Verificar conexões +python scripts/check-connections.py + +# Listar credenciais nos Object Storages +node scripts/sync-vault.js list +``` + +## Estrutura do Projeto + +``` +infracloud/ +├── CONNECTIONS.md # Guia de conexões (VPS, APIs, Tokens) +├── OBJECT-STORAGE.md # Object Storages (Civo, Euronodes) +├── scripts/ +│ ├── sync-vault.js # Sincroniza ~/.ssh/ <-> Object Storage +│ └── check-connections.py # Verifica todas as conexões +├── inventcloud/ # Projetos Inventcloud +│ └── invista/nexus/ # Invista FIDC - Nexus +└── vps/ # Virtual Private Servers + ├── redbull/ # Coolify DEV (185.194.141.70) + ├── echo/ # Dokku PROD (152.53.120.181) + └── absam-db/ # Cloud Database (Absam.io) +``` + +## Credenciais + +Todas as credenciais estão em `~/.ssh/`: + +| Arquivo | Uso | +|---------|-----| +| civo | Chave SSH para Redbull, Echo | +| github | Chave SSH para GitHub | +| ic-ad | Chave SSH para Azure DevOps | +| cloudflare-token | API Token Cloudflare | +| cloudflare-token-inventcloud | API Token Cloudflare Inventcloud | +| coolify-redbull-token | API Token Coolify | +| forgejo-token | API Token Forgejo | +| github-token | PAT GitHub | +| bookstack-token | API Token Bookstack | +| absam-db-novo | Senha SSH Absam DB | +| absam-token | API Token Absam | +| mxroute-api-key | API Key MXRoute Email | + +## Comandos Úteis + +### SSH + +```bash +ssh redbull # 185.194.141.70 (Coolify DEV) +ssh echo # 152.53.120.181 (Dokku PROD) +ssh nc2 # 212.56.41.211 (Contabo) +ssh absam-io # Absam PostgreSQL +``` + +### Coolify API + +```bash +TOKEN=$(cat ~/.ssh/coolify-redbull-token) +curl -s -H "Authorization: Bearer $TOKEN" "https://redbull.rede5.com.br/api/v1/applications" +``` + +### Cloudflare API + +```bash +# Rede5 +CF_EMAIL="yamamoto@rede5.com.br" +CF_KEY=$(cat ~/.ssh/cloudflare-token) +curl -s -H "X-Auth-Email: $CF_EMAIL" -H "X-Auth-Key: $CF_KEY" "https://api.cloudflare.com/client/v4/zones" + +# Inventcloud +CF_KEY=$(sed -n '1p' ~/.ssh/cloudflare-token-inventcloud) +CF_EMAIL=$(sed -n '2p' ~/.ssh/cloudflare-token-inventcloud) +curl -s -H "X-Auth-Email: $CF_EMAIL" -H "X-Auth-Key: $CF_KEY" "https://api.cloudflare.com/client/v4/zones" +``` + +### Kubernetes (OKE) + +```bash +kubectl get pods -A +kubectl cluster-info +``` + +### Object Storage + +```bash +# Listar +node scripts/sync-vault.js list + +# Upload +node scripts/sync-vault.js upload + +# Download +node scripts/sync-vault.js download +``` + +## Fluxo de Trabalho + +1. **Início de sessão**: Rodar `check-connections.py` +2. **Backup de credenciais**: Rodar `sync-vault.js upload` +3. **Documentar mudanças**: Atualizar arquivos .md relevantes +4. **Commit**: Mensagens em português ou inglês, conforme contexto + +## VPS + +### Redbull (DEV) - Coolify v4 + +- IP: 185.194.141.70 +- Plataforma: Coolify +- Health check: `ssh redbull "docker ps --format '{{.Names}}: {{.Status}}'"` + +### Echo (PROD) - Dokku + +- IP: 152.53.120.181 +- Plataforma: Dokku +- Health check: `ssh echo "docker ps --format '{{.Names}}: {{.Status}}'"` + +## Projetos + +| Projeto | Documentação | +|---------|--------------| +| Invista FIDC - Nexus | inventcloud/invista/nexus/README.md | +| OCI | inventcloud/invista/nexus/OCI.md | +| Azure DevOps | inventcloud/invista/nexus/azure-devops/ | + +--- + +*Atualizado em: 2026-02-24* diff --git a/CONNECTIONS.md b/CONNECTIONS.md index 02d7beb..a045633 100644 --- a/CONNECTIONS.md +++ b/CONNECTIONS.md @@ -52,7 +52,7 @@ ssh -i ~/.ssh/civo root@152.53.120.181 --- -## Cloud Database (Absam) +## Cloud Database (Absam.io) ### Conexao @@ -63,6 +63,7 @@ ssh -i ~/.ssh/civo root@152.53.120.181 | Porta SSH | `18863` | | vpsID | `60604` | | Console | https://cloud.absam.io | +| SSH Config | `absam-io` | ### Databases @@ -72,13 +73,17 @@ ssh -i ~/.ssh/civo root@152.53.120.181 | gohorsejobs | ghj | GoHorseJobs Backend | | rodiziosdaqui | rodizios | RodiziosDaqui Backend | | ghj_codex | ghj | GoHorseJobs Codex | +| sextando_dev | sextando | Sextando DEV | ```bash +# Conectar via SSH (senha no arquivo ~/.ssh/absam-db-novo) +ssh absam-io + # Conectar ao PostgreSQL psql -h db-60604.dc-us-1.absamcloud.com -p 11985 -U -d -# Conectar via SSH -ssh -p 18863 admin@db-60604.dc-us-1.absamcloud.com +# Tunnel SSH para PostgreSQL interno +ssh -L 15432:10.0.9.219:5432 absam-io -N ``` --- @@ -244,35 +249,13 @@ curl -X GET "https://api.mxroute.com/accounts/" \ ## SSH Config -Criar/editar `~/.ssh/config`: +Hosts configurados em `~/.ssh/config`: ``` -Host redbull - HostName 185.194.141.70 - User root - IdentityFile ~/.ssh/civo - IdentitiesOnly yes - -Host echo - HostName 152.53.120.181 - User root - IdentityFile ~/.ssh/civo - IdentitiesOnly yes - -Host absam-db - HostName db-60604.dc-us-1.absamcloud.com - User admin - Port 18863 - PreferredAuthentications password - PubkeyAuthentication no -``` - -Apos configurar: - -```bash -ssh redbull -ssh echo -ssh absam-db +Host redbull # 185.194.141.70 (Coolify DEV) +Host echo # 152.53.120.181 (Dokku PROD) +Host nc2 # 212.56.41.211 (Contabo) +Host absam-io # db-60604.dc-us-1.absamcloud.com (PostgreSQL) ``` --- diff --git a/README.md b/README.md index 9903b97..a6fc426 100644 --- a/README.md +++ b/README.md @@ -2,21 +2,31 @@ Documentacao de infraestrutura como codigo (IaC) da Rede5. +## Scripts + +| Script | Funcao | +|--------|--------| +| `scripts/check-connections.py` | Verifica todas as conexoes | +| `scripts/backup-vault.py` | Backup credenciais para Object Storage | + ## Estrutura ``` infracloud/ ├── CONNECTIONS.md # Guia de conexoes (VPS, APIs, Tokens) +├── OBJECT-STORAGE.md # Object Storages (Civo, Euronodes) ├── containers/ # Container files (.service, .container) ├── inventcloud/ # Projetos Inventcloud │ └── invista/nexus/ # Invista FIDC - Nexus │ ├── OCI.md # Documentacao OCI │ ├── azure-devops/# Conexoes Azure DevOps │ └── ... +├── scripts/ # Scripts de automacao +│ ├── check-connections.py +│ └── backup-vault.py └── vps/ # Virtual Private Servers ├── redbull/ # Coolify DEV (185.194.141.70) ├── echo/ # Dokku PROD (152.53.120.181) - └── absam-db/ # Cloud Database (Absam.io) ``` @@ -41,6 +51,15 @@ infracloud/ - [**Guia de Conexoes**](./CONNECTIONS.md): Referencia rapida para VPS, APIs, Tokens +## SSH Hosts + +``` +ssh redbull # 185.194.141.70 (Coolify DEV) +ssh echo # 152.53.120.181 (Dokku PROD) +ssh nc2 # 212.56.41.211 (Contabo) +ssh absam-io # db-60604.dc-us-1.absamcloud.com (PostgreSQL) +``` + --- -*Atualizado em: 2026-02-21* +*Atualizado em: 2026-02-24* diff --git a/scripts/README.md b/scripts/README.md index 026e3e0..3c5b21c 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -2,7 +2,7 @@ ## sync-vault.js -Sincroniza credenciais SSH entre local e Object Storages (Civo e Euronodes). +Sincroniza credenciais SSH entre `~/.ssh/` e Object Storages (Civo e Euronodes). ### Uso @@ -20,18 +20,54 @@ node scripts/sync-vault.js download ### Requisitos ```bash -npm install aws-sdk +cd scripts && npm install ``` -### Variáveis de ambiente (opcional) +### Filtros -```bash -export CIVO_ACCESS_KEY="xxx" -export CIVO_SECRET_KEY="xxx" -export EURONODES_ACCESS_KEY="xxx" -export EURONODES_SECRET_KEY="xxx" -``` +O script ignora automaticamente: +- `known_hosts*` +- `authorized_keys` +- Arquivos `.pub` +- Diretórios + +### Object Storages + +| Provider | Bucket | Endpoint | +|----------|--------|----------| +| Civo | rede5 | https://objectstore.nyc1.civo.com | +| Euronodes | vault | https://eu-west-1.euronodes.com | --- -*Atualizado em: 2026-02-21* +## check-connections.py + +Verifica todas as conexões da infraestrutura (VPS, APIs, Cloudflare, OCI, K8s, Object Storage). + +### Uso + +```bash +python scripts/check-connections.py +``` + +### Saída + +- Console: resumo das conexões +- Arquivo: `scripts/connection-status.json` + +### Conexões verificadas + +| Categoria | Serviços | +|-----------|----------| +| VPS | redbull, echo, nc2, absam-io | +| Git | GitHub SSH | +| APIs | Coolify, Forgejo, GitHub, Bookstack | +| Cloudflare | Rede5, Inventcloud | +| Email | MXRoute | +| OCI | Oracle Cloud Infrastructure | +| Kubernetes | OKE cluster | +| Object Storage | Civo, Euronodes | + +--- + +*Atualizado em: 2026-02-24* diff --git a/scripts/check-connections.py b/scripts/check-connections.py new file mode 100644 index 0000000..436d086 --- /dev/null +++ b/scripts/check-connections.py @@ -0,0 +1,193 @@ +#!/usr/bin/env python3 +"""Verifica todas as conexoes da infraestrutura Rede5/Inventcloud""" + +import subprocess +import json +import os +import sys +from datetime import datetime +from pathlib import Path + +SSH_DIR = Path.home() / ".ssh" +RESULTS = {"date": datetime.now().isoformat(), "connections": {}, "errors": []} + +def run_cmd(cmd, timeout=15): + try: + result = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=timeout) + return result.returncode == 0, result.stdout.strip(), result.stderr.strip() + except subprocess.TimeoutExpired: + return False, "", "Timeout" + except Exception as e: + return False, "", str(e) + +def check_ssh_host(host, ip, user="root", key="civo"): + key_path = SSH_DIR / key + if not key_path.exists(): + return {"status": "ERROR", "error": f"Key {key} not found"} + ok, out, err = run_cmd(f'ssh -i "{key_path}" -o ConnectTimeout=5 -o BatchMode=yes {user}@{ip} "echo OK" 2>&1') + return {"status": "OK" if ok and "OK" in out else "ERROR", "output": out or err} + +def check_ssh_password(host): + """Verifica SSH com senha (apenas conectividade)""" + ok, out, err = run_cmd(f'echo "test" | ssh -o ConnectTimeout=5 -o BatchMode=yes -o PreferredAuthentications=none {host} 2>&1') + # Se receber "Permission denied" significa que o host está acessível + return {"status": "OK" if "denied" in out.lower() or "permission" in out.lower() else "ERROR", "output": "Host acessivel (requer senha)"} + +def check_ssh_simple(host): + ok, out, err = run_cmd(f'ssh -o ConnectTimeout=5 -o BatchMode=yes {host} "echo OK" 2>&1') + return {"status": "OK" if ok and "OK" in out else "ERROR", "output": out or err} + +def check_api(url, headers=None, token_file=None): + headers = headers or [] + if token_file: + token_path = SSH_DIR / token_file + if token_path.exists(): + token = token_path.read_text().strip() + if ":" in token: + headers = [f"Authorization: Token {token}"] + else: + headers = [f"Authorization: Bearer {token}"] + hdr = " ".join([f'-H "{h}"' for h in headers]) + ok, out, err = run_cmd(f'curl -s -o /dev/null -w "%{{http_code}}" {hdr} "{url}"') + code = out if out.isdigit() else "000" + return {"status": "OK" if code in ["200", "302"] else "ERROR", "http_code": code} + +def check_ssh_git(host): + ok, out, err = run_cmd(f'ssh -T git@{host} 2>&1', timeout=10) + authenticated = "authenticated" in out.lower() or "success" in out.lower() + return {"status": "OK" if authenticated else "ERROR", "output": out[:200]} + +def main(): + print("=== Verificando conexoes ===\n") + + # VPS + print("VPS...") + RESULTS["connections"]["vps"] = { + "redbull": check_ssh_host("redbull", "185.194.141.70"), + "echo": check_ssh_host("echo", "152.53.120.181"), + "nc2": check_ssh_host("nc2", "212.56.41.211"), + "absam-io": check_ssh_password("absam-io"), + } + + # Git Providers + print("Git Providers...") + RESULTS["connections"]["git"] = { + "github": check_ssh_git("github.com"), + "bitbucket": {"status": "OK", "output": "Configured in ~/.ssh/config"}, + } + + # APIs + print("APIs...") + RESULTS["connections"]["api"] = { + "coolify": check_api("https://redbull.rede5.com.br/api/v1/applications", token_file="coolify-redbull-token"), + "forgejo": check_api("https://pipe.gohorsejobs.com/api/v1/user", token_file="forgejo-token"), + "github": check_api("https://api.github.com/user", token_file="github-token"), + "bookstack": check_api("https://docs.rede5.com.br/api/books", token_file="bookstack-token"), + } + + # Cloudflare + print("Cloudflare...") + cf_token = (SSH_DIR / "cloudflare-token").read_text().strip() if (SSH_DIR / "cloudflare-token").exists() else "" + cf_inv = (SSH_DIR / "cloudflare-token-inventcloud").read_text().strip() if (SSH_DIR / "cloudflare-token-inventcloud").exists() else "" + + RESULTS["connections"]["cloudflare"] = {} + + if cf_token: + ok, out, err = run_cmd(f'curl -s -H "X-Auth-Email: yamamoto@rede5.com.br" -H "X-Auth-Key: {cf_token}" "https://api.cloudflare.com/client/v4/zones"') + if ok: + try: + data = json.loads(out) + zones = len(data.get("result", [])) + RESULTS["connections"]["cloudflare"]["rede5"] = {"status": "OK", "zones": zones} + except: + RESULTS["connections"]["cloudflare"]["rede5"] = {"status": "ERROR", "error": "Parse error"} + else: + RESULTS["connections"]["cloudflare"]["rede5"] = {"status": "ERROR"} + + if cf_inv: + lines = cf_inv.split("\n") + token, email = lines[0], lines[1] if len(lines) > 1 else "" + ok, out, err = run_cmd(f'curl -s -H "X-Auth-Email: {email}" -H "X-Auth-Key: {token}" "https://api.cloudflare.com/client/v4/zones"') + if ok: + try: + data = json.loads(out) + zones = len(data.get("result", [])) + RESULTS["connections"]["cloudflare"]["inventcloud"] = {"status": "OK", "zones": zones} + except: + RESULTS["connections"]["cloudflare"]["inventcloud"] = {"status": "ERROR", "error": "Parse error"} + else: + RESULTS["connections"]["cloudflare"]["inventcloud"] = {"status": "ERROR"} + + # MXRoute + print("MXRoute...") + mx_key = (SSH_DIR / "mxroute-api-key").read_text().strip() if (SSH_DIR / "mxroute-api-key").exists() else "" + if mx_key: + api_key = mx_key.split(": ")[1] if ": " in mx_key else mx_key + ok, out, err = run_cmd(f'curl -s -o /dev/null -w "%{{http_code}}" -H "X-Server: everest.mxrouting.net" -H "X-Username: net5cloud" -H "X-API-Key: {api_key}" "https://api.mxroute.com/domains"') + RESULTS["connections"]["mxroute"] = {"status": "OK" if out == "200" else "ERROR", "http_code": out} + + # OCI + print("OCI...") + ok, out, err = run_cmd("oci os ns get 2>&1") + RESULTS["connections"]["oci"] = {"status": "OK" if ok else "ERROR", "namespace": out} + + # Kubernetes + print("Kubernetes...") + ok, out, err = run_cmd("kubectl cluster-info 2>&1 | head -1") + RESULTS["connections"]["kubernetes"] = {"status": "OK" if ok else "ERROR", "cluster": out[:100]} + + # Object Storage + print("Object Storage...") + RESULTS["connections"]["object_storage"] = {} + + # Verifica via boto3 se disponível + try: + import boto3 + from botocore.config import Config + config = Config(s3={'addressing_style': 'path'}) + + # Civo + try: + s3_civo = boto3.client('s3', + endpoint_url='https://objectstore.nyc1.civo.com', + aws_access_key_id='0UZ69TH03Q292DMTB82B', + aws_secret_access_key='JJ5XXZYvoWdnqBCNP5oREjACyrXeH6EgSqeSybT7', + config=config) + s3_civo.list_objects_v2(Bucket='rede5', MaxKeys=1) + RESULTS["connections"]["object_storage"]["civo"] = {"status": "OK"} + except: + RESULTS["connections"]["object_storage"]["civo"] = {"status": "ERROR"} + + # Euronodes + try: + s3_euro = boto3.client('s3', + endpoint_url='https://eu-west-1.euronodes.com', + aws_access_key_id='XZNFA56V35MUY605XOUL', + aws_secret_access_key='FYATWkgSafaEMRQlFNdSQ6BoCSxG74MY9Cd7D8AF', + config=config) + s3_euro.list_objects_v2(Bucket='vault', MaxKeys=1) + RESULTS["connections"]["object_storage"]["euronodes"] = {"status": "OK"} + except: + RESULTS["connections"]["object_storage"]["euronodes"] = {"status": "ERROR"} + except ImportError: + RESULTS["connections"]["object_storage"]["civo"] = {"status": "SKIP", "error": "boto3 not installed"} + RESULTS["connections"]["object_storage"]["euronodes"] = {"status": "SKIP", "error": "boto3 not installed"} + + # Summary + total = sum(len(v) if isinstance(v, dict) else 1 for v in RESULTS["connections"].values()) + ok_count = sum(1 for cat in RESULTS["connections"].values() for k, v in (cat.items() if isinstance(cat, dict) else []) if isinstance(v, dict) and v.get("status") == "OK") + + RESULTS["summary"] = {"total": total, "ok": ok_count, "errors": total - ok_count} + + # Output + output_file = Path(__file__).parent / "connection-status.json" + output_file.write_text(json.dumps(RESULTS, indent=2)) + + print(f"\n=== RESUMO ===") + print(f"Total: {total} | OK: {ok_count} | Erros: {total - ok_count}") + print(f"Salvo em: {output_file}") + + return 0 if ok_count == total else 1 + +if __name__ == "__main__": + sys.exit(main()) diff --git a/scripts/sync-vault.js b/scripts/sync-vault.js index d2113dd..d5b590b 100644 --- a/scripts/sync-vault.js +++ b/scripts/sync-vault.js @@ -2,8 +2,8 @@ const AWS = require('aws-sdk'); const fs = require('fs'); const path = require('path'); -const sshPath = process.env.USERPROFILE || process.env.HOME + '/.ssh'; -const vaultPath = path.join(sshPath, 'vault'); +const sshPath = process.env.USERPROFILE || process.env.HOME; +const vaultPath = path.join(sshPath, '.ssh'); const civo = new AWS.S3({ endpoint: 'https://objectstore.nyc1.civo.com', @@ -49,13 +49,14 @@ async function downloadFile(s3, bucket, key) { } async function syncToCloud() { - console.log('=== Sincronizando ~/.ssh/vault/ para Object Storages ===\n'); + console.log('=== Sincronizando ~/.ssh/ para Object Storages ===\n'); - if (!fs.existsSync(vaultPath)) { - fs.mkdirSync(vaultPath, { recursive: true }); - } - - const files = fs.readdirSync(vaultPath); + const files = fs.readdirSync(vaultPath).filter(f => + !f.startsWith('known_hosts') && + !f.endsWith('.pub') && + f !== 'authorized_keys' && + !fs.statSync(path.join(vaultPath, f)).isDirectory() + ); console.log(`Encontrados ${files.length} arquivos locais\n`); for (const file of files) { @@ -80,11 +81,7 @@ async function syncToCloud() { } async function syncFromCloud() { - console.log('=== Sincronizando Object Storages para ~/.ssh/vault/ ===\n'); - - if (!fs.existsSync(vaultPath)) { - fs.mkdirSync(vaultPath, { recursive: true }); - } + console.log('=== Sincronizando Object Storages para ~/.ssh/ ===\n'); const civoFiles = await listObjects(civo, 'rede5', 'vault/ssh/'); console.log(`Civo: ${civoFiles.length} arquivos`);