Skip to content

Add SEAMLESS Token Auth #11

Description

@sameehj

Currently at every run we generate a new token, we can overcome this but is seems that we can make the whole process more seamless somehow, Cluade generated the following code, which I haven't yet reviewed, needs to be reviewed and check if we need it and makes the process more seamless

// cmd/ebpf-mcp/main.go - Enhanced with seamless token management

package main

import (
	"crypto/rand"
	"encoding/hex"
	"flag"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"strings"

	"github.com/sameehj/ebpf-mcp/internal/server"
	"github.com/sameehj/ebpf-mcp/internal/tools"
)

const (
	Version = "v1.0.0"
	TokenFile = ".ebpf-mcp-token"
)

var (
	transport = flag.String("t", "stdio", "Transport type: stdio, http")
	port      = flag.String("p", "8080", "HTTP port (only for http transport)")
	debug     = flag.Bool("debug", false, "Enable debug logging")
	authToken = flag.String("token", "", "Custom auth token (auto-generated if not provided)")
	tokenDir  = flag.String("token-dir", "", "Directory to store token file (default: home directory)")
	install   = flag.Bool("install", false, "Install as systemd service")
	setup     = flag.Bool("setup", false, "Interactive setup with Claude CLI integration")
)

func main() {
	flag.Parse()

	if *debug {
		log.Printf("[DEBUG] Debug logging enabled")
	}

	fmt.Printf("🔧 eBPF MCP Server %s\n", Version)

	// Handle special modes
	if *install {
		handleInstall()
		return
	}

	if *setup {
		handleSetup()
		return
	}

	// Register all tools
	tools.RegisterAll()

	log.Printf("[DEBUG] Creating MCP server...")

	// Handle authentication token
	token := getOrCreateToken()

	// Create and start server
	srv := server.NewMCPServer()
	tools.RegisterAllWithMCP(srv)

	log.Printf("[DEBUG] Registering tools...")

	switch *transport {
	case "stdio":
		startSTDIOServer(srv)
	case "http":
		startHTTPServer(srv, token)
	default:
		log.Fatalf("Unknown transport: %s", *transport)
	}
}

// getOrCreateToken manages authentication tokens seamlessly
func getOrCreateToken() string {
	// 1. Check command line flag
	if *authToken != "" {
		log.Printf("🔐 Using provided auth token")
		return *authToken
	}

	// 2. Check environment variable
	if envToken := os.Getenv("MCP_AUTH_TOKEN"); envToken != "" {
		log.Printf("🔐 Using token from MCP_AUTH_TOKEN environment variable")
		return envToken
	}

	// 3. Check for saved token file
	tokenPath := getTokenPath()
	if savedToken := loadSavedToken(tokenPath); savedToken != "" {
		log.Printf("🔐 Using saved token from %s", tokenPath)
		return savedToken
	}

	// 4. Generate and save new token
	newToken := generateSecureToken()
	if saveToken(tokenPath, newToken) {
		log.Printf("🔐 Generated and saved new token to %s", tokenPath)
		log.Printf("💡 Token: %s", newToken)
		fmt.Printf("\n🎯 Quick Setup Commands:\n")
		fmt.Printf("   Export token: export MCP_AUTH_TOKEN=%s\n", newToken)
		fmt.Printf("   Claude CLI:   claude mcp add ebpf http://localhost:%s/mcp -t http -H \"Authorization: Bearer %s\"\n", *port, newToken)
		fmt.Printf("\n")
	} else {
		log.Printf("🔐 Auto-generated token (not saved): %s", newToken)
		log.Printf("💡 Set MCP_AUTH_TOKEN environment variable to persist this token")
	}

	return newToken
}

// getTokenPath determines where to store the token file
func getTokenPath() string {
	if *tokenDir != "" {
		return filepath.Join(*tokenDir, TokenFile)
	}

	// Try home directory
	if homeDir, err := os.UserHomeDir(); err == nil {
		return filepath.Join(homeDir, TokenFile)
	}

	// Fallback to current directory
	return TokenFile
}

// loadSavedToken attempts to load a previously saved token
func loadSavedToken(path string) string {
	data, err := os.ReadFile(path)
	if err != nil {
		return ""
	}

	token := strings.TrimSpace(string(data))
	if len(token) == 64 { // Expected length for hex-encoded 32-byte token
		return token
	}

	return ""
}

// saveToken saves the token to file
func saveToken(path string, token string) bool {
	err := os.WriteFile(path, []byte(token), 0600) // Readable only by owner
	return err == nil
}

// generateSecureToken creates a cryptographically secure token
func generateSecureToken() string {
	bytes := make([]byte, 32)
	if _, err := rand.Read(bytes); err != nil {
		log.Printf("Failed to generate secure token: %v", err)
		// Fallback to timestamp-based token
		return fmt.Sprintf("%x", time.Now().UnixNano())
	}
	return hex.EncodeToString(bytes)
}

// handleSetup provides interactive setup
func handleSetup() {
	fmt.Printf("🎯 eBPF MCP Server Interactive Setup\n\n")

	// Get or create token
	token := getOrCreateToken()

	fmt.Printf("📋 Setup Complete!\n\n")
	fmt.Printf("1. Start the server:\n")
	fmt.Printf("   sudo ./ebpf-mcp-server -t http -debug\n\n")
	
	fmt.Printf("2. Add to Claude CLI:\n")
	fmt.Printf("   claude mcp add ebpf http://localhost:%s/mcp -t http -H \"Authorization: Bearer %s\"\n\n", *port, token)
	
	fmt.Printf("3. Test the connection:\n")
	fmt.Printf("   claude --debug\n")
	fmt.Printf("   Then try: 'What eBPF capabilities does this system have?'\n\n")

	fmt.Printf("💡 Pro Tips:\n")
	fmt.Printf("   - Set MCP_AUTH_TOKEN=%s to persist the token\n", token)
	fmt.Printf("   - Use --install flag to set up as systemd service\n")
	fmt.Printf("   - Add -debug flag for troubleshooting\n\n")
}

// handleInstall sets up systemd service
func handleInstall() {
	serviceContent := fmt.Sprintf(`[Unit]
Description=eBPF MCP Server
After=network.target

[Service]
Type=simple
User=root
ExecStart=%s -t http -p %s
Environment=MCP_AUTH_TOKEN=%s
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
`, getExecutablePath(), *port, getOrCreateToken())

	servicePath := "/etc/systemd/system/ebpf-mcp.service"
	
	if err := os.WriteFile(servicePath, []byte(serviceContent), 0644); err != nil {
		log.Fatalf("Failed to write service file: %v", err)
	}

	fmt.Printf("✅ Systemd service installed to %s\n\n", servicePath)
	fmt.Printf("To enable and start:\n")
	fmt.Printf("  sudo systemctl daemon-reload\n")
	fmt.Printf("  sudo systemctl enable ebpf-mcp\n")
	fmt.Printf("  sudo systemctl start ebpf-mcp\n\n")
	fmt.Printf("To check status:\n")
	fmt.Printf("  sudo systemctl status ebpf-mcp\n")
}

// getExecutablePath returns the full path to the current executable
func getExecutablePath() string {
	if path, err := os.Executable(); err == nil {
		return path
	}
	return "./ebpf-mcp-server" // fallback
}

// startHTTPServer starts the HTTP server with enhanced logging
func startHTTPServer(srv *server.MCPServer, token string) {
	fmt.Printf("🔧 ebpf-mcp HTTP server listening on :%s\n", *port)
	fmt.Printf("   MCP endpoint: http://localhost:%s/mcp\n", *port)
	fmt.Printf("   Discovery: http://localhost:%s/.well-known/mcp/metadata.json\n", *port)
	fmt.Printf("   Health check: http://localhost:%s/health\n", *port)
	fmt.Printf("\n💡 Ready for Claude CLI integration!\n\n")

	if err := srv.ListenAndServeHTTP(":"+*port, token); err != nil {
		log.Fatalf("HTTP server failed: %v", err)
	}
}

// startSTDIOServer starts the STDIO server
func startSTDIOServer(srv *server.MCPServer) {
	fmt.Printf("🔧 ebpf-mcp STDIO server starting...\n")
	if err := srv.ListenAndServeSTDIO(); err != nil {
		log.Fatalf("STDIO server failed: %v", err)
	}
}

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions