Переглянути джерело

fix: resolve SSL certificate chain issues for gogs-mcp communication

The Gogs server serves a Let's Encrypt certificate with a missing E7
intermediate, causing SSL verification failures for both curl and
Node.js HTTPS in the client and gogs-mcp server.

Changes:
- Move Config.loadGlobal() before fetchRepositorySshUrl() in client
  so GOGS_ACCESS_TOKEN is available for the API call
- Add extra CA certificate bundle support to gogs-helper.ts (--cacert)
- Propagate .env values to process.env in server config.js so child
  processes (client, MCP servers) inherit NODE_EXTRA_CA_CERTS
- Update MCP config examples to include required env vars
  (GOGS_SERVER_URL, GOGS_ACCESS_TOKEN, NODE_EXTRA_CA_CERTS)
- Document SSL certificate chain fix in README.md and CLAUDE.md
- Update .env.example and global-env.example with NODE_EXTRA_CA_CERTS

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Claude 4 місяців тому
батько
коміт
2a7dd1c818
7 змінених файлів з 93 додано та 8 видалено
  1. 5 0
      .env.example
  2. 18 2
      CLAUDE.md
  3. 37 0
      README.md
  4. 13 2
      client/src/gogs-helper.ts
  5. 6 4
      client/src/index.ts
  6. 9 0
      examples/global-env.example
  7. 5 0
      src/config.js

+ 5 - 0
.env.example

@@ -54,6 +54,11 @@ SCRIPTS_PATH=/home/claude/agent-manager/scripts
 # Path to gogs-mcp server (used in headless Claude Code mode)
 GOGS_MCP_PATH=/data/gogs-mcp/dist/index.js
 
+# SSL Certificate Configuration
+# Extra CA certs for servers with incomplete certificate chains
+# See README.md "SSL Certificate Chain Fix" section for setup
+# NODE_EXTRA_CA_CERTS=/home/claude/.config/agent-manager/certs/extra-ca.pem
+
 # Legacy Command Configuration (deprecated - use commands.json instead)
 # These settings are maintained for backward compatibility
 # WEBHOOK_PUSH_COMMAND=echo "Push to {{branch}} by {{pusher}} in {{repo}}"

+ 18 - 2
CLAUDE.md

@@ -201,6 +201,13 @@ GOGS_MCP_PATH=/data/gogs-mcp/dist/index.js  # Path to gogs-mcp server for headle
 
 The `GOGS_MCP_PATH` environment variable configures the location of the gogs-mcp server used by Claude Code in headless mode for issue automation. The handle-issue.sh script dynamically generates the MCP configuration file (.mcp-gogs.json) using this path at runtime, making it environment-specific.
 
+**SSL Certificate Configuration:**
+```env
+NODE_EXTRA_CA_CERTS=/home/claude/.config/agent-manager/certs/extra-ca.pem
+```
+
+When the Gogs server uses a certificate with an incomplete chain (e.g., missing Let's Encrypt intermediate), Node.js and curl will fail SSL verification. The `NODE_EXTRA_CA_CERTS` variable points to a PEM bundle containing the missing intermediate and root certificates. The server propagates `.env` values to `process.env` so child processes (the TypeScript client, gogs-mcp) inherit them automatically. The client's `gogs-helper.ts` also uses the extra CA bundle with curl via `--cacert`.
+
 ### 2. Command Configuration (`commands.json`)
 
 Copy from `commands.json.example` and configure commands for each event type.
@@ -264,6 +271,8 @@ Copy from `commands.json.example` and configure commands for each event type.
 10. **Shell & Node Execution**: Commands can be shell scripts or Node.js scripts
 11. **Email Notifications**: Optional callback-based email notifications with condition matching
 12. **File Logging**: Automatic file logging with rotation (enabled by default for systemd services)
+13. **Environment Propagation**: Server propagates `.env` values to `process.env` so child processes inherit them (critical for `NODE_EXTRA_CA_CERTS`, `GOGS_MCP_PATH`, etc.)
+14. **SSL CA Bundle**: Extra CA certificates stored at `~/.config/agent-manager/certs/extra-ca.pem` for servers with incomplete certificate chains
 
 ## Extending with Callbacks
 
@@ -616,8 +625,12 @@ The client supports flexible MCP server configuration with three priority levels
     "gogs": {
       "type": "stdio",
       "command": "node",
-      "args": ["/data/gogs-mcp/dist/index.js"],
-      "env": {}
+      "args": ["${GOGS_MCP_PATH}"],
+      "env": {
+        "GOGS_SERVER_URL": "${GOGS_SERVER_URL}",
+        "GOGS_ACCESS_TOKEN": "${GOGS_ACCESS_TOKEN}",
+        "NODE_EXTRA_CA_CERTS": "${NODE_EXTRA_CA_CERTS}"
+      }
     },
     "custom-mcp": {
       "type": "stdio",
@@ -631,6 +644,8 @@ The client supports flexible MCP server configuration with three priority levels
 }
 ```
 
+**Important**: MCP servers are spawned as child processes by the Claude Agent SDK. The `env` section must explicitly include any environment variables the server needs (e.g., `GOGS_SERVER_URL`, `GOGS_ACCESS_TOKEN`, `NODE_EXTRA_CA_CERTS`). Use `${VAR_NAME}` syntax for substitution from the loaded environment.
+
 **Security**: Configuration files are stored outside repositories in `~/.config/agent-manager/mcp/` with restricted permissions to protect secrets like API keys and JWT tokens.
 
 See `examples/MCP-CONFIG.md` for detailed configuration guide and examples.
@@ -680,6 +695,7 @@ The client supports a two-tier environment variable configuration system to elim
 - `GOGS_ACCESS_TOKEN` or `GOGS_TOKEN` - Gogs API authentication token
 - `GOGS_BASE_URL` - Gogs server URL (e.g., `https://git.smartbotics.ai`)
 - `GOGS_MCP_PATH` - Path to gogs-mcp server (defaults to `/data/gogs-mcp/dist/index.js`)
+- `NODE_EXTRA_CA_CERTS` - Path to extra CA certificates PEM bundle (for incomplete SSL chains)
 
 **Setup Global Configuration**:
 ```bash

+ 37 - 0
README.md

@@ -104,6 +104,9 @@ WEBHOOK_SECRET=your-secret-here
 - `PORT` - The port number to listen on (default: `3000`)
 - `WEBHOOK_PATH` - The URL path for webhooks (default: `/webhook`)
 - `WEBHOOK_SECRET` - Optional secret for webhook signature verification (see Security section below)
+- `NODE_EXTRA_CA_CERTS` - Path to extra CA certificates PEM bundle for incomplete SSL chains (see Security section)
+
+**Note**: All `.env` values are propagated to `process.env` at startup, so child processes (client, MCP servers) inherit them automatically.
 
 #### Enable/Disable Events (`.env`)
 
@@ -245,6 +248,40 @@ This ensures:
 - **Integrity**: The payload hasn't been modified in transit
 - **Replay Protection**: Combined with Gogs' delivery ID tracking
 
+### SSL Certificate Chain Fix
+
+If your Gogs server uses a certificate with an incomplete chain (e.g., missing Let's Encrypt intermediate certificate), both `curl` and Node.js HTTPS requests will fail with SSL verification errors.
+
+#### Setup
+
+1. **Download the missing certificates**:
+```bash
+mkdir -p ~/.config/agent-manager/certs
+# Download Let's Encrypt E7 intermediate and ISRG Root X2
+curl -k -s "https://letsencrypt.org/certs/2024/e7.pem" -o ~/.config/agent-manager/certs/lets-encrypt-e7.pem
+curl -k -s "https://letsencrypt.org/certs/isrg-root-x2.pem" -o ~/.config/agent-manager/certs/isrg-root-x2.pem
+# Create combined bundle
+cat ~/.config/agent-manager/certs/lets-encrypt-e7.pem ~/.config/agent-manager/certs/isrg-root-x2.pem > ~/.config/agent-manager/certs/extra-ca.pem
+```
+
+2. **Add to `.env`**:
+```env
+NODE_EXTRA_CA_CERTS=/home/claude/.config/agent-manager/certs/extra-ca.pem
+```
+
+3. **Add to global config** (`~/.config/agent-manager/.env`):
+```env
+NODE_EXTRA_CA_CERTS=/home/claude/.config/agent-manager/certs/extra-ca.pem
+```
+
+#### How It Works
+
+- The server loads `.env` values into `process.env` at startup
+- Child processes (the TypeScript client, gogs-mcp server) inherit `NODE_EXTRA_CA_CERTS` from the parent environment
+- Node.js reads `NODE_EXTRA_CA_CERTS` at process startup and adds the certificates to its trust store
+- The client's `gogs-helper` also passes the CA bundle to `curl` via `--cacert`
+- MCP server configs in `~/.config/agent-manager/mcp/` should include `"NODE_EXTRA_CA_CERTS": "${NODE_EXTRA_CA_CERTS}"` in their `env` section
+
 ## Job Queue System
 
 The webhook server uses a persistent job queue to ensure reliable command execution. The queue supports two operating modes:

+ 13 - 2
client/src/gogs-helper.ts

@@ -8,6 +8,15 @@
 import { execSync } from 'child_process';
 import { existsSync, readFileSync } from 'fs';
 import { join } from 'path';
+import { homedir } from 'os';
+
+/**
+ * Get path to extra CA certificates bundle (for Let's Encrypt chain)
+ */
+function getExtraCaCertsPath(): string | null {
+  const certPath = join(homedir(), '.config', 'agent-manager', 'certs', 'extra-ca.pem');
+  return existsSync(certPath) ? certPath : null;
+}
 
 /**
  * Get Gogs API configuration from environment or MCP config
@@ -32,8 +41,10 @@ export async function fetchRepositorySshUrl(owner: string, repo: string): Promis
     const { baseUrl, token } = getGogsConfig();
     const url = `${baseUrl}/api/v1/repos/${owner}/${repo}`;
 
-    // Use curl to fetch repository info
-    const command = `curl -s -H "Authorization: token ${token}" "${url}"`;
+    // Build curl command with optional CA cert bundle
+    const caCertPath = getExtraCaCertsPath();
+    const caCertFlag = caCertPath ? `--cacert "${caCertPath}" ` : '';
+    const command = `curl -s ${caCertFlag}-H "Authorization: token ${token}" "${url}"`;
     const response = execSync(command, { encoding: 'utf-8' });
 
     const repoInfo = JSON.parse(response);

+ 6 - 4
client/src/index.ts

@@ -25,6 +25,10 @@ if (import.meta.url === `file://${process.argv[1]}`) {
   console.log('Setting up environment...');
   setupEnvironment();
 
+  // Load configuration early: global env vars are needed for Gogs API access and MCP config
+  console.log('Loading configuration...');
+  Config.loadGlobal();  // Load global .env from ~/.config/agent-manager/.env
+
   // Parse command line arguments
   const args = process.argv.slice(2);
 
@@ -96,10 +100,8 @@ if (import.meta.url === `file://${process.argv[1]}`) {
   process.chdir(repoPath);
   console.log(`Working directory: ${process.cwd()}`);
 
-  // Load configuration: global first, then repository-specific
-  console.log('Loading configuration...');
-  Config.loadGlobal();  // Load global .env from ~/.config/agent-manager/.env
-  Config.load(repoPath);  // Load repository-specific .env (overrides global)
+  // Load repository-specific .env (overrides global values)
+  Config.load(repoPath);
 
   // Generate MCP configuration for this repository
   const mcpConfig = Config.generateMcpConfig(repoPath, owner, repo);

+ 9 - 0
examples/global-env.example

@@ -11,8 +11,17 @@ GOGS_BASE_URL=https://git.smartbotics.ai
 # Optional: Path to gogs-mcp server (defaults to /data/gogs-mcp/dist/index.js)
 GOGS_MCP_PATH=/data/gogs-mcp/dist/index.js
 
+# SSL Certificate Configuration
+# Extra CA certs for servers with incomplete certificate chains (e.g., missing Let's Encrypt intermediate)
+# See README.md "SSL Certificate Chain Fix" section for setup instructions
+NODE_EXTRA_CA_CERTS=/home/claude/.config/agent-manager/certs/extra-ca.pem
+
+# Node Environment
+NODE_ENV=production
+
 # Notes:
 # 1. Create this file at: ~/.config/agent-manager/.env
 # 2. Copy this example and fill in your actual values
 # 3. Ensure proper permissions: chmod 600 ~/.config/agent-manager/.env
 # 4. Repository-specific .env files (in each cloned repo) can override these values
+# 5. For SSL cert setup, run: mkdir -p ~/.config/agent-manager/certs && see README.md

+ 5 - 0
src/config.js

@@ -41,6 +41,11 @@ export class Config {
           }
 
           this.env[key] = value;
+
+          // Also set in process.env if not already set, so child processes inherit these values
+          if (process.env[key] === undefined) {
+            process.env[key] = value;
+          }
         }
       });
     } catch (error) {