Session log: GuruRMM audit, installer system, infrastructure fixes

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-01 13:58:45 -07:00
parent a47a97219c
commit af71d317b0
10 changed files with 286 additions and 27 deletions

View File

@@ -4,6 +4,9 @@
**Status:** [OK] Complete - Production Ready
**Author:** Coding Agent (Claude Sonnet 4.5)
**Source Repo:** `azcomputerguru/gururmm` on git.azcomputerguru.com (active development, 53+ commits).
Note: A `guru-rmm` repo also exists but is a restructured copy with only 2 commits -- use `gururmm` as the primary reference.
---
## What Was Built
@@ -187,6 +190,10 @@ once_cell = "1.19"
Follow instructions in `commands_modifications.rs`:
> **Note:** `claude_task` is a NEW command type added by this integration. The existing
> GuruRMM command types are: `shell`, `powershell`, `python`, `script`. This step adds
> `claude_task` as an additional type in the command dispatcher.
1. Add module declaration: `mod claude;`
2. Add imports: `use crate::claude::{ClaudeExecutor, ClaudeTaskCommand};`
3. Create global executor: `static CLAUDE_EXECUTOR: Lazy<ClaudeExecutor> = ...`
@@ -243,7 +250,9 @@ Follow deployment process in `TESTING_AND_DEPLOYMENT.md`:
## Usage Example
Once deployed, Main Claude can invoke tasks on AD2:
Once deployed, Main Claude can invoke tasks on AD2. The curl command below creates the
command on the server via REST; the server then delivers it to the agent over WebSocket
(ServerMessage::Command):
```bash
curl -X POST "http://172.16.3.30:3001/api/agents/{AD2_AGENT_ID}/command" \
@@ -394,7 +403,7 @@ A: Check agent logs at `C:\Program Files\GuruRMM\logs\agent.log` for detailed er
1. **TESTING_AND_DEPLOYMENT.md** - Complete testing and troubleshooting guide
2. **README.md** - Full project documentation with examples
3. **Agent logs** - `C:\Program Files\GuruRMM\logs\agent.log`
4. **GuruRMM server logs** - `http://172.16.3.30:3001/logs`
4. **GuruRMM server logs** - Check server-side logs on disk (no `/logs` HTTP endpoint exists)
---

View File

@@ -70,6 +70,10 @@ Open your `agent/src/commands.rs` and make these changes:
```
### 3E: Update Command Dispatcher
> **Note:** `claude_task` is a NEW command type being added by this integration.
> The existing GuruRMM command types are: `shell`, `powershell`, `python`, `script`.
- [ ] Find your `match command_type` block
- [ ] Add new arm (before the `_` default case):
```rust
@@ -158,6 +162,10 @@ Open your `agent/src/commands.rs` and make these changes:
**Replace `{AD2_AGENT_ID}` with actual agent ID in all commands**
> The curl commands below create the command on the server via REST. The server then
> delivers the command to the agent over WebSocket (ServerMessage::Command) -- the agent
> does NOT poll for commands.
### Test 1: Simple Task
- [ ] Run:
```bash

View File

@@ -2,6 +2,9 @@
Production-ready enhancement for GuruRMM agent that enables Main Claude to remotely invoke Claude Code CLI on AD2 (Windows Server 2022) for automated task execution.
**Source Repo:** `azcomputerguru/gururmm` on git.azcomputerguru.com (active development, 53+ commits).
Note: A `guru-rmm` repo also exists but is a restructured copy with only 2 commits -- use `gururmm` as the primary reference.
---
## Features
@@ -90,6 +93,8 @@ use once_cell::sync::Lazy;
static CLAUDE_EXECUTOR: Lazy<ClaudeExecutor> = Lazy::new(|| ClaudeExecutor::new());
// In your command dispatcher
// Existing types: shell, powershell, python, script
// claude_task is a NEW type added by this integration
match command_type {
"shell" => execute_shell_command(&command).await,
"claude_task" => execute_claude_task(&command).await, // NEW
@@ -127,6 +132,11 @@ See `TESTING_AND_DEPLOYMENT.md` for complete deployment guide.
## Usage Examples
> **How commands work:** The curl examples below create the command on the server via
> the REST API (`POST /api/agents/:id/command`). The server then delivers the command
> to the agent over WebSocket (ServerMessage::Command). The agent does NOT poll for
> commands via REST.
### Example 1: Simple Task
```bash

View File

@@ -8,6 +8,9 @@ This guide covers testing and deployment of the Claude Code integration for the
## Prerequisites
**Source Repo:** `azcomputerguru/gururmm` on git.azcomputerguru.com (active development, 53+ commits).
Note: A `guru-rmm` repo also exists but is a restructured copy with only 2 commits -- use `gururmm` as the primary reference.
### On Development Machine
- Rust toolchain (1.70+)
- cargo installed
@@ -69,6 +72,14 @@ cargo fmt -- --check
## Integration Testing (On AD2 Server)
> **Note:** `claude_task` is a NEW command type added by this integration. The existing
> GuruRMM command types are: `shell`, `powershell`, `python`, `script`.
>
> **How commands reach the agent:** The curl commands below create the command on the
> server via the REST API (`POST /api/agents/:id/command`). The server then delivers the
> command to the connected agent over WebSocket (ServerMessage::Command). The agent does
> NOT poll for commands via REST.
### Test 1: Simple Task Execution
**Test Command via GuruRMM API:**
@@ -476,7 +487,7 @@ Monitor Claude task execution metrics:
```powershell
# Query GuruRMM API for task statistics
curl "http://172.16.3.30:3001/api/agents/{AD2_AGENT_ID}/stats"
curl "http://172.16.3.30:3001/api/agents/stats"
```
**Key metrics to watch:**
@@ -558,7 +569,7 @@ Prevents abuse:
For issues or questions:
1. Check agent logs: `C:\Program Files\GuruRMM\logs\agent.log`
2. Check GuruRMM server logs: `http://172.16.3.30:3001/logs`
2. Check GuruRMM server logs on disk (no `/logs` HTTP endpoint exists)
3. Review this documentation
4. Contact GuruRMM support team

View File

@@ -4,9 +4,11 @@ Check record counts in all ClaudeTools database tables
"""
import sys
from sqlalchemy import create_engine, text, inspect
from vault_utils import vault_get
# Database connection
DATABASE_URL = "mysql+pymysql://claudetools:CT_e8fcd5a3952030a79ed6debae6c954ed@172.16.3.30:3306/claudetools?charset=utf8mb4"
# Database connection - credentials from SOPS vault
_db_password = vault_get("projects/claudetools/database.sops.yaml", "credentials.password")
DATABASE_URL = f"mysql+pymysql://claudetools:{_db_password}@172.16.3.30:3306/claudetools?charset=utf8mb4"
def get_table_counts():
"""Get row counts for all tables"""

View File

@@ -4,10 +4,10 @@ Create a JWT token for ClaudeTools API access
"""
import jwt
from datetime import datetime, timedelta, timezone
from vault_utils import vault_get
# Get the JWT secret from the RMM server's .env file
# This should match what's in /opt/claudetools/.env on 172.16.3.30
JWT_SECRET = "NdwgH6jsGR1WfPdUwR3u9i1NwNx3QthhLHBsRCfFxcg="
# Get the JWT secret from the SOPS vault
JWT_SECRET = vault_get("projects/claudetools/api-auth.sops.yaml", "credentials.credential")
# Create token data
data = {

View File

@@ -8,11 +8,12 @@ Tests the newly created admin user credentials and verifies API access.
import requests
import json
from datetime import datetime
from vault_utils import vault_get
# Configuration
# Configuration - credentials from SOPS vault
API_BASE_URL = "http://172.16.3.30:3001"
EMAIL = "claude-api@azcomputerguru.com"
PASSWORD = "ClaudeAPI2026!@#"
EMAIL = vault_get("infrastructure/gururmm-server.sops.yaml", "credentials.gururmm-api.admin-email")
PASSWORD = vault_get("infrastructure/gururmm-server.sops.yaml", "credentials.gururmm-api.admin-password")
def print_header(title):
"""Print a formatted header."""
@@ -133,7 +134,7 @@ def main():
print_header("All Tests Passed!")
print("API Credentials:")
print(f" Email: {EMAIL}")
print(f" Password: {PASSWORD}")
print(f" Password: ********** (from vault)")
print(f" Base URL: {API_BASE_URL}")
print(f" Production URL: https://rmm-api.azcomputerguru.com")
print("\nStatus: READY FOR INTEGRATION")

View File

@@ -0,0 +1,34 @@
"""
Shared SOPS vault credential retrieval utility.
Usage:
from vault_utils import vault_get
password = vault_get("projects/claudetools/database.sops.yaml", "credentials.password")
"""
import subprocess
VAULT_SCRIPT = "D:/vault/scripts/vault.sh"
def vault_get(path, field):
"""Get a credential from the SOPS vault.
Args:
path: Vault entry path (e.g. "projects/claudetools/database.sops.yaml")
field: Dot-separated field path (e.g. "credentials.password")
Returns:
The decrypted field value as a string.
Raises:
RuntimeError: If the vault command fails.
"""
result = subprocess.run(
["bash", VAULT_SCRIPT, "get-field", path, field],
capture_output=True, text=True
)
if result.returncode != 0:
raise RuntimeError(f"Failed to get {field} from vault: {result.stderr.strip()}")
return result.stdout.strip()