mirror of
https://github.com/claude-did-this/claude-hub.git
synced 2026-02-14 19:30:02 +01:00
security: Implement comprehensive credential protection system
Replace environment variable credentials with secure file-based mounting to prevent runtime credential exposure that was causing security leaks. Key security improvements: - Docker secrets integration for credential mounting - Secure credential loader utility (secureCredentials.js) - Enhanced logging redaction for all credential types - Pre-commit security scanning with multiple tools - Automated security audit workflow and scripts - File-based credentials with proper 600 permissions Services updated: - githubController.js: Use secure credentials for webhook verification - claudeService.js: Use secure credentials for GitHub/Anthropic APIs - githubService.js: Use secure credentials for GitHub API calls - logger.js: Enhanced redaction patterns for all credential types New security infrastructure: - ./scripts/setup/setup-secure-credentials.sh: Setup script for secure credentials - ./scripts/security/credential-audit.sh: Comprehensive security audit - .github/workflows/security-audit.yml: Automated security scanning - docker-compose.yml: Updated to use Docker secrets by default - k8s/secrets.yaml: Kubernetes secrets configuration - systemd/claude-webhook.service: Systemd service configuration This eliminates credential exposure in: - Environment variables and process lists - Container logs and debug output - Git commits and PR comments - Runtime error messages 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
committed by
Cheffromspace
parent
57a3a211f7
commit
7e545475d0
59
.github/workflows/security-audit.yml
vendored
Normal file
59
.github/workflows/security-audit.yml
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
name: Security Audit
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main, develop ]
|
||||
pull_request:
|
||||
branches: [ main, develop ]
|
||||
schedule:
|
||||
# Run daily at 2 AM UTC
|
||||
- cron: '0 2 * * *'
|
||||
|
||||
jobs:
|
||||
security-audit:
|
||||
runs-on: ubuntu-latest
|
||||
name: Security Audit
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # Fetch full history for comprehensive scanning
|
||||
|
||||
- name: Run credential audit
|
||||
run: ./scripts/security/credential-audit.sh
|
||||
|
||||
- name: Run Gitleaks
|
||||
uses: gitleaks/gitleaks-action@v2
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Run Trivy vulnerability scanner
|
||||
uses: aquasecurity/trivy-action@master
|
||||
with:
|
||||
scan-type: 'fs'
|
||||
scan-ref: '.'
|
||||
format: 'sarif'
|
||||
output: 'trivy-results.sarif'
|
||||
|
||||
- name: Upload Trivy scan results
|
||||
uses: github/codeql-action/upload-sarif@v3
|
||||
if: always()
|
||||
with:
|
||||
sarif_file: 'trivy-results.sarif'
|
||||
|
||||
- name: Check for high-risk files
|
||||
run: |
|
||||
# Check for files that commonly contain secrets
|
||||
risk_files=$(find . -name "*.pem" -o -name "*.key" -o -name "*.p12" -o -name "*.pfx" -o -name "*secret*" -o -name "*password*" -o -name "*credential*" | grep -v node_modules || true)
|
||||
if [ ! -z "$risk_files" ]; then
|
||||
echo "⚠️ Found high-risk files that may contain secrets:"
|
||||
echo "$risk_files"
|
||||
echo "::warning::High-risk files detected. Please review for secrets."
|
||||
fi
|
||||
|
||||
- name: Audit npm packages
|
||||
run: |
|
||||
if [ -f "package.json" ]; then
|
||||
npm audit --audit-level=high
|
||||
fi
|
||||
25
.gitignore
vendored
25
.gitignore
vendored
@@ -3,10 +3,9 @@ node_modules/
|
||||
|
||||
# Environment variables
|
||||
.env
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.*
|
||||
!.env.example
|
||||
!.env.template
|
||||
|
||||
# Logs
|
||||
logs
|
||||
@@ -50,4 +49,20 @@ awscliv2.zip
|
||||
*.swo
|
||||
|
||||
# Pre-commit
|
||||
.pre-commit-cache/
|
||||
.pre-commit-cache/
|
||||
|
||||
# Security files
|
||||
*.pem
|
||||
*.key
|
||||
*.crt
|
||||
*.p12
|
||||
*.pfx
|
||||
|
||||
# Credential files
|
||||
credentials
|
||||
config
|
||||
auth.json
|
||||
service-account.json
|
||||
|
||||
# Docker secrets
|
||||
secrets/
|
||||
@@ -7,12 +7,33 @@ repos:
|
||||
- id: check-yaml
|
||||
- id: check-added-large-files
|
||||
- id: check-json
|
||||
- id: check-merge-conflict
|
||||
- id: check-executables-have-shebangs
|
||||
- id: check-shebang-scripts-are-executable
|
||||
- repo: https://github.com/Yelp/detect-secrets
|
||||
rev: v1.4.0
|
||||
hooks:
|
||||
- id: detect-secrets
|
||||
args: ['--baseline', '.secrets.baseline']
|
||||
exclude: node_modules/
|
||||
- repo: https://github.com/gitleaks/gitleaks
|
||||
rev: v8.18.1
|
||||
hooks:
|
||||
- id: gitleaks
|
||||
- id: gitleaks
|
||||
- repo: https://github.com/thoughtworks/talisman
|
||||
rev: v1.32.0
|
||||
hooks:
|
||||
- id: talisman-commit
|
||||
entry: cmd --githook pre-commit
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: env-file-check
|
||||
name: Check for .env files
|
||||
entry: bash -c 'if find . -name ".env*" -not -path "./node_modules/*" -not -name ".env.example" | grep -q .; then echo "Found .env files that may contain secrets"; exit 1; fi'
|
||||
language: system
|
||||
pass_filenames: false
|
||||
- id: credential-scan
|
||||
name: Scan for hardcoded credentials
|
||||
entry: bash -c 'if grep -r "sk-\|ghp_\|AKIA\|xox[boas]\|AIza[0-9A-Za-z\\-_]\{35\}" --exclude-dir=node_modules --exclude-dir=.git .; then echo "Found potential hardcoded credentials"; exit 1; fi'
|
||||
language: system
|
||||
pass_filenames: false
|
||||
19
CLAUDE.md
19
CLAUDE.md
@@ -19,11 +19,13 @@ This repository contains a webhook service that integrates Claude with GitHub, a
|
||||
## Build & Run Commands
|
||||
|
||||
### Setup and Installation
|
||||
- Initial setup: `./scripts/setup.sh`
|
||||
- Start the server: `npm start`
|
||||
- Development mode with auto-restart: `npm run dev`
|
||||
- Start on specific port: `./start-api.sh` (uses port 3003)
|
||||
- Run tests: `npm test`
|
||||
- **Initial setup**: `./scripts/setup.sh`
|
||||
- **Setup secure credentials**: `./scripts/setup/setup-secure-credentials.sh`
|
||||
- **Start with Docker (recommended)**: `docker compose up -d`
|
||||
- **Start the server locally**: `npm start`
|
||||
- **Development mode with auto-restart**: `npm run dev`
|
||||
- **Start on specific port**: `./start-api.sh` (uses port 3003)
|
||||
- **Run tests**: `npm test`
|
||||
- Run specific test types:
|
||||
- Unit tests: `npm run test:unit`
|
||||
- Integration tests: `npm run test:integration`
|
||||
@@ -32,11 +34,12 @@ This repository contains a webhook service that integrates Claude with GitHub, a
|
||||
- Watch mode: `npm run test:watch`
|
||||
|
||||
### Docker Commands
|
||||
- **Start services**: `docker compose up -d` (uses secure credentials)
|
||||
- **Stop services**: `docker compose down`
|
||||
- **View logs**: `docker compose logs -f webhook`
|
||||
- **Restart**: `docker compose restart webhook`
|
||||
- Build Claude container: `./build-claude-container.sh`
|
||||
- Build Claude Code container: `./build-claudecode.sh`
|
||||
- Build and start with Docker Compose: `docker compose up -d`
|
||||
- Stop Docker Compose services: `docker compose down`
|
||||
- View logs: `docker compose logs -f webhook`
|
||||
- Update production image: `./update-production-image.sh`
|
||||
|
||||
### AWS Credential Management
|
||||
|
||||
24
README.md
24
README.md
@@ -27,31 +27,37 @@ For comprehensive documentation, see:
|
||||
- Stateless container execution mode for isolation and scalability
|
||||
- Optionally permit Claude to make code changes when requested
|
||||
|
||||
## Setup Guide
|
||||
## 🚀 Setup Guide
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Node.js 16 or higher
|
||||
- npm or yarn
|
||||
- Docker and Docker Compose
|
||||
- GitHub account with access to the repositories you want to use
|
||||
|
||||
### Step-by-Step Installation
|
||||
### Quick Setup
|
||||
|
||||
1. **Clone this repository**
|
||||
```
|
||||
```bash
|
||||
git clone https://github.com/yourusername/claude-github-webhook.git
|
||||
cd claude-github-webhook
|
||||
```
|
||||
|
||||
2. **Run the setup script**
|
||||
2. **Setup secure credentials**
|
||||
```bash
|
||||
./scripts/setup/setup-secure-credentials.sh
|
||||
```
|
||||
./scripts/setup/setup.sh
|
||||
This creates secure credential files with proper permissions.
|
||||
|
||||
3. **Start the service**
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
This will create necessary directories, copy the environment template, install dependencies, and set up pre-commit hooks for credential scanning.
|
||||
The service will be available at `http://localhost:8082`
|
||||
|
||||
3. **Configure Credentials**
|
||||
### Manual Configuration (Alternative)
|
||||
|
||||
Copy the `.env.example` file to `.env` and edit with your credentials:
|
||||
If you prefer to configure manually instead of using the setup script:
|
||||
```
|
||||
cp .env.example .env
|
||||
nano .env # or use your preferred editor
|
||||
|
||||
36
docker-compose.secrets.yml
Normal file
36
docker-compose.secrets.yml
Normal file
@@ -0,0 +1,36 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
webhook:
|
||||
build: .
|
||||
ports:
|
||||
- "3003:3002"
|
||||
secrets:
|
||||
- github_token
|
||||
- anthropic_api_key
|
||||
- webhook_secret
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- PORT=3002
|
||||
- AUTHORIZED_USERS=Cheffromspace
|
||||
- BOT_USERNAME=@MCPClaude
|
||||
- DEFAULT_GITHUB_OWNER=Cheffromspace
|
||||
- DEFAULT_GITHUB_USER=Cheffromspace
|
||||
- DEFAULT_BRANCH=main
|
||||
- CLAUDE_USE_CONTAINERS=1
|
||||
- CLAUDE_CONTAINER_IMAGE=claudecode:latest
|
||||
# Point to secret files instead of env vars
|
||||
- GITHUB_TOKEN_FILE=/run/secrets/github_token
|
||||
- ANTHROPIC_API_KEY_FILE=/run/secrets/anthropic_api_key
|
||||
- GITHUB_WEBHOOK_SECRET_FILE=/run/secrets/webhook_secret
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
restart: unless-stopped
|
||||
|
||||
secrets:
|
||||
github_token:
|
||||
file: ./secrets/github_token.txt
|
||||
anthropic_api_key:
|
||||
file: ./secrets/anthropic_api_key.txt
|
||||
webhook_secret:
|
||||
file: ./secrets/webhook_secret.txt
|
||||
@@ -8,8 +8,24 @@ services:
|
||||
- /app/node_modules
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- ${HOME}/.aws:/root/.aws:ro
|
||||
env_file:
|
||||
- .env
|
||||
secrets:
|
||||
- github_token
|
||||
- anthropic_api_key
|
||||
- webhook_secret
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- PORT=3002
|
||||
- AUTHORIZED_USERS=${AUTHORIZED_USERS:-Cheffromspace}
|
||||
- BOT_USERNAME=${BOT_USERNAME:-@MCPClaude}
|
||||
- DEFAULT_GITHUB_OWNER=${DEFAULT_GITHUB_OWNER:-Cheffromspace}
|
||||
- DEFAULT_GITHUB_USER=${DEFAULT_GITHUB_USER:-Cheffromspace}
|
||||
- DEFAULT_BRANCH=${DEFAULT_BRANCH:-main}
|
||||
- CLAUDE_USE_CONTAINERS=1
|
||||
- CLAUDE_CONTAINER_IMAGE=claudecode:latest
|
||||
# Point to secret files instead of env vars
|
||||
- GITHUB_TOKEN_FILE=/run/secrets/github_token
|
||||
- ANTHROPIC_API_KEY_FILE=/run/secrets/anthropic_api_key
|
||||
- GITHUB_WEBHOOK_SECRET_FILE=/run/secrets/webhook_secret
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:3002/health"]
|
||||
@@ -20,6 +36,14 @@ services:
|
||||
networks:
|
||||
- n8n_default
|
||||
|
||||
secrets:
|
||||
github_token:
|
||||
file: ./secrets/github_token.txt
|
||||
anthropic_api_key:
|
||||
file: ./secrets/anthropic_api_key.txt
|
||||
webhook_secret:
|
||||
file: ./secrets/webhook_secret.txt
|
||||
|
||||
networks:
|
||||
n8n_default:
|
||||
external: true
|
||||
56
k8s/secrets.yaml
Normal file
56
k8s/secrets.yaml
Normal file
@@ -0,0 +1,56 @@
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: claude-webhook-secrets
|
||||
namespace: default
|
||||
type: Opaque
|
||||
stringData:
|
||||
github-token: "YOUR_GITHUB_TOKEN_HERE"
|
||||
anthropic-api-key: "YOUR_ANTHROPIC_API_KEY_HERE"
|
||||
webhook-secret: "YOUR_WEBHOOK_SECRET_HERE"
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: claude-webhook
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: claude-webhook
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: claude-webhook
|
||||
spec:
|
||||
containers:
|
||||
- name: webhook
|
||||
image: claude-webhook:latest
|
||||
ports:
|
||||
- containerPort: 3002
|
||||
env:
|
||||
- name: NODE_ENV
|
||||
value: "production"
|
||||
- name: PORT
|
||||
value: "3002"
|
||||
- name: GITHUB_TOKEN_FILE
|
||||
value: "/etc/secrets/github-token"
|
||||
- name: ANTHROPIC_API_KEY_FILE
|
||||
value: "/etc/secrets/anthropic-api-key"
|
||||
- name: GITHUB_WEBHOOK_SECRET_FILE
|
||||
value: "/etc/secrets/webhook-secret"
|
||||
volumeMounts:
|
||||
- name: secrets-volume
|
||||
mountPath: /etc/secrets
|
||||
readOnly: true
|
||||
volumes:
|
||||
- name: secrets-volume
|
||||
secret:
|
||||
secretName: claude-webhook-secrets
|
||||
items:
|
||||
- key: github-token
|
||||
path: github-token
|
||||
- key: anthropic-api-key
|
||||
path: anthropic-api-key
|
||||
- key: webhook-secret
|
||||
path: webhook-secret
|
||||
131
scripts/security/credential-audit.sh
Executable file
131
scripts/security/credential-audit.sh
Executable file
@@ -0,0 +1,131 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Credential Security Audit Script
|
||||
# This script performs comprehensive credential scanning and security checks
|
||||
|
||||
set -e
|
||||
|
||||
echo "🔒 Starting Credential Security Audit..."
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Track issues found
|
||||
ISSUES_FOUND=0
|
||||
|
||||
# Function to report issues
|
||||
report_issue() {
|
||||
echo -e "${RED}❌ SECURITY ISSUE: $1${NC}"
|
||||
((ISSUES_FOUND++))
|
||||
}
|
||||
|
||||
report_warning() {
|
||||
echo -e "${YELLOW}⚠️ WARNING: $1${NC}"
|
||||
}
|
||||
|
||||
report_success() {
|
||||
echo -e "${GREEN}✅ $1${NC}"
|
||||
}
|
||||
|
||||
# 1. Check for .env files that shouldn't be committed
|
||||
echo "🔍 Checking for exposed .env files..."
|
||||
if find . -name ".env*" -not -path "./node_modules/*" -not -name ".env.example" -not -name ".env.template" | grep -q .; then
|
||||
find . -name ".env*" -not -path "./node_modules/*" -not -name ".env.example" -not -name ".env.template" | while read file; do
|
||||
report_issue "Found .env file that may contain secrets: $file"
|
||||
done
|
||||
else
|
||||
report_success "No exposed .env files found"
|
||||
fi
|
||||
|
||||
# 2. Scan for hardcoded API keys and tokens
|
||||
echo "🔍 Scanning for hardcoded credentials..."
|
||||
CREDENTIAL_PATTERNS=(
|
||||
"sk-[a-zA-Z0-9-_]{40,}" # Anthropic API keys
|
||||
"ghp_[a-zA-Z0-9]{36}" # GitHub personal access tokens
|
||||
"AKIA[0-9A-Z]{16}" # AWS access key IDs
|
||||
"xox[boas]-[0-9]{10,13}-[0-9]{10,13}-[0-9]{10,13}-[a-z0-9]{32}" # Slack tokens
|
||||
"AIza[0-9A-Za-z\\-_]{35}" # Google API keys
|
||||
)
|
||||
|
||||
for pattern in "${CREDENTIAL_PATTERNS[@]}"; do
|
||||
if grep -rE "$pattern" --exclude-dir=node_modules --exclude-dir=.git --exclude-dir=coverage --exclude="credential-audit.sh" . 2>/dev/null; then
|
||||
report_issue "Found potential hardcoded credentials matching pattern: $pattern"
|
||||
fi
|
||||
done
|
||||
|
||||
# 3. Check git history for leaked credentials (last 10 commits)
|
||||
echo "🔍 Checking recent git history for credentials..."
|
||||
for pattern in "${CREDENTIAL_PATTERNS[@]}"; do
|
||||
if git log --oneline -10 | xargs -I {} git show {} | grep -qE "$pattern" 2>/dev/null; then
|
||||
report_warning "Found potential credentials in git history (pattern: $pattern)"
|
||||
echo " Consider using 'git filter-branch' or 'BFG Repo-Cleaner' to remove them"
|
||||
fi
|
||||
done
|
||||
|
||||
# 4. Check file permissions
|
||||
echo "🔍 Checking file permissions..."
|
||||
if find . -name "*.key" -o -name "*.pem" -o -name "*.crt" -o -name ".env*" 2>/dev/null | xargs ls -la 2>/dev/null | grep -v "^-rw-------"; then
|
||||
report_warning "Found credential files with overly permissive permissions"
|
||||
echo " Consider running: chmod 600 on credential files"
|
||||
fi
|
||||
|
||||
# 5. Check for AWS credentials file
|
||||
if [ -f "$HOME/.aws/credentials" ]; then
|
||||
if [ "$(stat -c %a "$HOME/.aws/credentials" 2>/dev/null)" != "600" ]; then
|
||||
report_warning "AWS credentials file has overly permissive permissions"
|
||||
echo " Run: chmod 600 ~/.aws/credentials"
|
||||
else
|
||||
report_success "AWS credentials file has proper permissions"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 6. Verify .gitignore coverage
|
||||
echo "🔍 Checking .gitignore coverage..."
|
||||
SHOULD_BE_IGNORED=(
|
||||
".env"
|
||||
"*.key"
|
||||
"*.pem"
|
||||
"credentials"
|
||||
"config"
|
||||
"auth.json"
|
||||
)
|
||||
|
||||
for item in "${SHOULD_BE_IGNORED[@]}"; do
|
||||
if ! grep -q "$item" .gitignore 2>/dev/null; then
|
||||
report_warning ".gitignore missing pattern: $item"
|
||||
fi
|
||||
done
|
||||
|
||||
# 7. Check for pre-commit hooks
|
||||
echo "🔍 Checking security tools..."
|
||||
if [ ! -f ".pre-commit-config.yaml" ]; then
|
||||
report_issue "No pre-commit configuration found"
|
||||
else
|
||||
if grep -q "detect-secrets" .pre-commit-config.yaml && grep -q "gitleaks" .pre-commit-config.yaml; then
|
||||
report_success "Pre-commit security tools configured"
|
||||
else
|
||||
report_warning "Pre-commit missing security tools (detect-secrets, gitleaks)"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 8. Check environment variable exposure in logs/debug output
|
||||
echo "🔍 Checking for environment variable exposure..."
|
||||
if grep -r "process.env\|os.environ\|ENV\[" --include="*.js" --include="*.py" --include="*.log" --exclude-dir=node_modules . 2>/dev/null | grep -v "process.env.NODE_ENV" | head -5 | grep -q .; then
|
||||
report_warning "Found potential environment variable exposure in code/logs"
|
||||
echo " Review the following files for credential leaks:"
|
||||
grep -r "process.env\|os.environ\|ENV\[" --include="*.js" --include="*.py" --include="*.log" --exclude-dir=node_modules . 2>/dev/null | grep -v "process.env.NODE_ENV" | head -5
|
||||
fi
|
||||
|
||||
# Summary
|
||||
echo ""
|
||||
echo "📊 Security Audit Summary:"
|
||||
if [ $ISSUES_FOUND -eq 0 ]; then
|
||||
echo -e "${GREEN}✅ No critical security issues found!${NC}"
|
||||
exit 0
|
||||
else
|
||||
echo -e "${RED}❌ Found $ISSUES_FOUND security issue(s) that need attention${NC}"
|
||||
exit 1
|
||||
fi
|
||||
92
scripts/setup/setup-secure-credentials.sh
Executable file
92
scripts/setup/setup-secure-credentials.sh
Executable file
@@ -0,0 +1,92 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Setup Secure Credentials Script
|
||||
# Creates secure credential files with proper permissions
|
||||
|
||||
set -e
|
||||
|
||||
echo "🔐 Setting up secure credentials..."
|
||||
|
||||
# Create secrets directory
|
||||
SECRETS_DIR="./secrets"
|
||||
mkdir -p "$SECRETS_DIR"
|
||||
|
||||
# Set restrictive permissions on secrets directory
|
||||
chmod 700 "$SECRETS_DIR"
|
||||
|
||||
echo "📁 Created secrets directory: $SECRETS_DIR"
|
||||
|
||||
# Function to create secure credential file
|
||||
create_credential_file() {
|
||||
local filename="$1"
|
||||
local description="$2"
|
||||
local filepath="$SECRETS_DIR/$filename"
|
||||
|
||||
if [ -f "$filepath" ]; then
|
||||
echo "⚠️ $filepath already exists, skipping..."
|
||||
return
|
||||
fi
|
||||
|
||||
echo "🔑 Creating $description credential file..."
|
||||
read -s -p "Enter $description: " credential
|
||||
echo
|
||||
|
||||
# Write credential to file
|
||||
echo "$credential" > "$filepath"
|
||||
|
||||
# Set secure permissions (owner read-only)
|
||||
chmod 600 "$filepath"
|
||||
|
||||
echo "✅ Created $filepath with secure permissions"
|
||||
}
|
||||
|
||||
# Create credential files
|
||||
create_credential_file "github_token.txt" "GitHub Personal Access Token"
|
||||
create_credential_file "anthropic_api_key.txt" "Anthropic API Key"
|
||||
create_credential_file "webhook_secret.txt" "GitHub Webhook Secret"
|
||||
|
||||
# Create .env file without secrets
|
||||
cat > .env.secure << 'EOF'
|
||||
# Secure Configuration (no secrets in env vars)
|
||||
NODE_ENV=production
|
||||
PORT=3002
|
||||
|
||||
# Bot Configuration
|
||||
BOT_USERNAME=@MCPClaude
|
||||
DEFAULT_GITHUB_OWNER=Cheffromspace
|
||||
DEFAULT_GITHUB_USER=Cheffromspace
|
||||
DEFAULT_BRANCH=main
|
||||
|
||||
# Security Configuration
|
||||
AUTHORIZED_USERS=Cheffromspace
|
||||
|
||||
# Container Configuration
|
||||
CLAUDE_USE_CONTAINERS=1
|
||||
CLAUDE_CONTAINER_IMAGE=claudecode:latest
|
||||
|
||||
# Credential file paths (Docker secrets)
|
||||
GITHUB_TOKEN_FILE=/run/secrets/github_token
|
||||
ANTHROPIC_API_KEY_FILE=/run/secrets/anthropic_api_key
|
||||
GITHUB_WEBHOOK_SECRET_FILE=/run/secrets/webhook_secret
|
||||
EOF
|
||||
|
||||
echo "✅ Created .env.secure configuration file"
|
||||
|
||||
# Update .gitignore to exclude secrets
|
||||
if ! grep -q "secrets/" .gitignore 2>/dev/null; then
|
||||
echo "secrets/" >> .gitignore
|
||||
echo "✅ Added secrets/ to .gitignore"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "🎉 Secure credentials setup complete!"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo "1. Start with Docker secrets: docker compose -f docker-compose.secrets.yml up -d"
|
||||
echo "2. Or use local files: cp .env.secure .env && npm start"
|
||||
echo "3. Verify credentials are loaded: check application logs"
|
||||
echo ""
|
||||
echo "🔒 Security notes:"
|
||||
echo "- Credential files have 600 permissions (owner read-only)"
|
||||
echo "- secrets/ directory is added to .gitignore"
|
||||
echo "- Use Docker secrets in production for maximum security"
|
||||
@@ -8,6 +8,7 @@ const path = require('path');
|
||||
const { createLogger } = require('../utils/logger');
|
||||
// const awsCredentialProvider = require('../utils/awsCredentialProvider');
|
||||
const { sanitizeBotMentions } = require('../utils/sanitize');
|
||||
const secureCredentials = require('../utils/secureCredentials');
|
||||
|
||||
const logger = createLogger('claudeService');
|
||||
|
||||
@@ -43,8 +44,10 @@ async function processCommand({ repoFullName, issueNumber, command, isPullReques
|
||||
commandLength: command.length
|
||||
}, 'Processing command with Claude');
|
||||
|
||||
const githubToken = secureCredentials.get('GITHUB_TOKEN');
|
||||
|
||||
// In test mode, skip execution and return a mock response
|
||||
if (process.env.NODE_ENV === 'test' || !process.env.GITHUB_TOKEN.includes('ghp_')) {
|
||||
if (process.env.NODE_ENV === 'test' || !githubToken || !githubToken.includes('ghp_')) {
|
||||
logger.info({
|
||||
repo: repoFullName,
|
||||
issue: issueNumber
|
||||
@@ -126,8 +129,8 @@ Please complete this task fully and autonomously.`;
|
||||
IS_PULL_REQUEST: isPullRequest ? 'true' : 'false',
|
||||
BRANCH_NAME: branchName || '',
|
||||
COMMAND: fullPrompt,
|
||||
GITHUB_TOKEN: process.env.GITHUB_TOKEN,
|
||||
ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY
|
||||
GITHUB_TOKEN: githubToken,
|
||||
ANTHROPIC_API_KEY: secureCredentials.get('ANTHROPIC_API_KEY')
|
||||
};
|
||||
|
||||
// Build docker run command - properly escape values for shell
|
||||
@@ -271,8 +274,8 @@ Please complete this task fully and autonomously.`;
|
||||
|
||||
// Sensitive values to redact
|
||||
const sensitiveValues = [
|
||||
process.env.GITHUB_TOKEN,
|
||||
process.env.ANTHROPIC_API_KEY,
|
||||
githubToken,
|
||||
secureCredentials.get('ANTHROPIC_API_KEY'),
|
||||
envVars.AWS_ACCESS_KEY_ID,
|
||||
envVars.AWS_SECRET_ACCESS_KEY,
|
||||
envVars.AWS_SESSION_TOKEN
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
const axios = require('axios');
|
||||
const { createLogger } = require('../utils/logger');
|
||||
const secureCredentials = require('../utils/secureCredentials');
|
||||
|
||||
const logger = createLogger('githubService');
|
||||
|
||||
@@ -16,8 +17,10 @@ async function postComment({ repoOwner, repoName, issueNumber, body }) {
|
||||
bodyLength: body.length
|
||||
}, 'Posting comment to GitHub');
|
||||
|
||||
const githubToken = secureCredentials.get('GITHUB_TOKEN');
|
||||
|
||||
// In test mode, just log the comment instead of posting to GitHub
|
||||
if (process.env.NODE_ENV === 'test' || !process.env.GITHUB_TOKEN.includes('ghp_')) {
|
||||
if (process.env.NODE_ENV === 'test' || !githubToken || !githubToken.includes('ghp_')) {
|
||||
logger.info({
|
||||
repo: `${repoOwner}/${repoName}`,
|
||||
issue: issueNumber,
|
||||
|
||||
@@ -70,21 +70,37 @@ const logger = pino({
|
||||
'AWS_SECRET_ACCESS_KEY',
|
||||
'AWS_ACCESS_KEY_ID',
|
||||
'GITHUB_TOKEN',
|
||||
'GH_TOKEN',
|
||||
'ANTHROPIC_API_KEY',
|
||||
'*.AWS_SECRET_ACCESS_KEY',
|
||||
'*.AWS_ACCESS_KEY_ID',
|
||||
'*.GITHUB_TOKEN',
|
||||
'*.GH_TOKEN',
|
||||
'*.ANTHROPIC_API_KEY',
|
||||
'dockerCommand',
|
||||
'*.dockerCommand',
|
||||
'envVars.AWS_SECRET_ACCESS_KEY',
|
||||
'envVars.AWS_ACCESS_KEY_ID',
|
||||
'envVars.GITHUB_TOKEN',
|
||||
'envVars.GH_TOKEN',
|
||||
'envVars.ANTHROPIC_API_KEY',
|
||||
'env.AWS_SECRET_ACCESS_KEY',
|
||||
'env.AWS_ACCESS_KEY_ID',
|
||||
'env.GITHUB_TOKEN',
|
||||
'env.GH_TOKEN',
|
||||
'env.ANTHROPIC_API_KEY',
|
||||
'stderr',
|
||||
'*.stderr',
|
||||
'stdout',
|
||||
'*.stdout',
|
||||
'error.dockerCommand',
|
||||
'error.stderr',
|
||||
'error.stdout'
|
||||
'error.stdout',
|
||||
'process.env.GITHUB_TOKEN',
|
||||
'process.env.GH_TOKEN',
|
||||
'process.env.ANTHROPIC_API_KEY',
|
||||
'process.env.AWS_SECRET_ACCESS_KEY',
|
||||
'process.env.AWS_ACCESS_KEY_ID'
|
||||
],
|
||||
censor: '[REDACTED]'
|
||||
}
|
||||
|
||||
28
systemd/claude-webhook.service
Normal file
28
systemd/claude-webhook.service
Normal file
@@ -0,0 +1,28 @@
|
||||
[Unit]
|
||||
Description=Claude GitHub Webhook Service
|
||||
After=network.target
|
||||
Requires=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=node
|
||||
WorkingDirectory=/opt/claude-webhook
|
||||
ExecStart=/usr/bin/node src/index.js
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
Environment=NODE_ENV=production
|
||||
EnvironmentFile=/opt/claude-webhook/.env.secure
|
||||
|
||||
# Security settings
|
||||
NoNewPrivileges=true
|
||||
ProtectSystem=strict
|
||||
ProtectHome=true
|
||||
ReadWritePaths=/opt/claude-webhook/logs
|
||||
PrivateTmp=true
|
||||
|
||||
# Resource limits
|
||||
LimitNOFILE=4096
|
||||
LimitNPROC=256
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
Reference in New Issue
Block a user