up
Some checks failed
LNM Migration CI / build-runner (push) Has been cancelled
Ledger OpenAPI CI / deprecation-check (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Airgap Sealed CI Smoke / sealed-smoke (push) Has been cancelled
Ledger Packs CI / build-pack (push) Has been cancelled
Export Center CI / export-ci (push) Has been cancelled
Ledger OpenAPI CI / validate-oas (push) Has been cancelled
Ledger OpenAPI CI / check-wellknown (push) Has been cancelled
Ledger Packs CI / verify-pack (push) Has been cancelled
LNM Migration CI / validate-metrics (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Some checks failed
LNM Migration CI / build-runner (push) Has been cancelled
Ledger OpenAPI CI / deprecation-check (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Airgap Sealed CI Smoke / sealed-smoke (push) Has been cancelled
Ledger Packs CI / build-pack (push) Has been cancelled
Export Center CI / export-ci (push) Has been cancelled
Ledger OpenAPI CI / validate-oas (push) Has been cancelled
Ledger OpenAPI CI / check-wellknown (push) Has been cancelled
Ledger Packs CI / verify-pack (push) Has been cancelled
LNM Migration CI / validate-metrics (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
This commit is contained in:
329
docs/modules/zastava/kit/validate-paths.sh
Normal file
329
docs/modules/zastava/kit/validate-paths.sh
Normal file
@@ -0,0 +1,329 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# validate-paths.sh - Validates offline kit path structure
|
||||
#
|
||||
# Usage: ./validate-paths.sh [--combined] [kit_directory]
|
||||
#
|
||||
# Options:
|
||||
# --combined Expect combined runtime format (combined.runtime.ndjson)
|
||||
# kit_directory Path to kit directory (default: parent of this script)
|
||||
#
|
||||
# Exit codes:
|
||||
# 0 - All validations passed
|
||||
# 1 - Missing required files or directories
|
||||
# 2 - Invalid file format
|
||||
# 3 - Usage error
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
COMBINED_FORMAT=false
|
||||
KIT_DIR=""
|
||||
|
||||
# Parse arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--combined)
|
||||
COMBINED_FORMAT=true
|
||||
shift
|
||||
;;
|
||||
--help|-h)
|
||||
echo "Usage: $0 [--combined] [kit_directory]"
|
||||
echo ""
|
||||
echo "Validates offline kit path structure and file formats."
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " --combined Expect combined runtime format"
|
||||
echo " kit_directory Path to kit directory (default: parent of this script)"
|
||||
exit 0
|
||||
;;
|
||||
-*)
|
||||
echo "Unknown option: $1" >&2
|
||||
exit 3
|
||||
;;
|
||||
*)
|
||||
KIT_DIR="$1"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Default to parent directory if not specified
|
||||
if [[ -z "$KIT_DIR" ]]; then
|
||||
KIT_DIR="${SCRIPT_DIR}/.."
|
||||
fi
|
||||
|
||||
# Resolve to absolute path
|
||||
KIT_DIR="$(cd "$KIT_DIR" && pwd)"
|
||||
|
||||
echo "Validating kit at: $KIT_DIR"
|
||||
|
||||
ERRORS=0
|
||||
|
||||
# Helper functions
|
||||
check_file() {
|
||||
local file="$1"
|
||||
local required="${2:-true}"
|
||||
local path="$KIT_DIR/$file"
|
||||
|
||||
if [[ -f "$path" ]]; then
|
||||
echo " [OK] $file"
|
||||
return 0
|
||||
elif [[ "$required" == "true" ]]; then
|
||||
echo " [MISSING] $file (required)" >&2
|
||||
ERRORS=$((ERRORS + 1))
|
||||
return 1
|
||||
else
|
||||
echo " [SKIP] $file (optional)"
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
check_dir() {
|
||||
local dir="$1"
|
||||
local required="${2:-true}"
|
||||
local path="$KIT_DIR/$dir"
|
||||
|
||||
if [[ -d "$path" ]]; then
|
||||
echo " [OK] $dir/"
|
||||
return 0
|
||||
elif [[ "$required" == "true" ]]; then
|
||||
echo " [MISSING] $dir/ (required)" >&2
|
||||
ERRORS=$((ERRORS + 1))
|
||||
return 1
|
||||
else
|
||||
echo " [SKIP] $dir/ (optional)"
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
validate_json() {
|
||||
local file="$1"
|
||||
local path="$KIT_DIR/$file"
|
||||
|
||||
if [[ ! -f "$path" ]]; then
|
||||
return 0 # Skip if file doesn't exist (handled by check_file)
|
||||
fi
|
||||
|
||||
if command -v python3 >/dev/null 2>&1; then
|
||||
if python3 -c "import json; json.load(open('$path'))" 2>/dev/null; then
|
||||
echo " [VALID JSON] $file"
|
||||
return 0
|
||||
else
|
||||
echo " [INVALID JSON] $file" >&2
|
||||
ERRORS=$((ERRORS + 1))
|
||||
return 1
|
||||
fi
|
||||
elif command -v jq >/dev/null 2>&1; then
|
||||
if jq empty "$path" 2>/dev/null; then
|
||||
echo " [VALID JSON] $file"
|
||||
return 0
|
||||
else
|
||||
echo " [INVALID JSON] $file" >&2
|
||||
ERRORS=$((ERRORS + 1))
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
echo " [SKIP] $file (no JSON validator available)"
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
validate_ndjson() {
|
||||
local file="$1"
|
||||
local path="$KIT_DIR/$file"
|
||||
|
||||
if [[ ! -f "$path" ]]; then
|
||||
return 0 # Skip if file doesn't exist
|
||||
fi
|
||||
|
||||
if command -v python3 >/dev/null 2>&1; then
|
||||
local result
|
||||
result=$(python3 -c "
|
||||
import json, sys
|
||||
path = '$path'
|
||||
errors = 0
|
||||
with open(path, 'r') as f:
|
||||
for i, line in enumerate(f, 1):
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
try:
|
||||
json.loads(line)
|
||||
except json.JSONDecodeError as e:
|
||||
print(f'Line {i}: {e}', file=sys.stderr)
|
||||
errors += 1
|
||||
if errors >= 5:
|
||||
print('(truncated after 5 errors)', file=sys.stderr)
|
||||
break
|
||||
sys.exit(0 if errors == 0 else 1)
|
||||
" 2>&1)
|
||||
if [[ $? -eq 0 ]]; then
|
||||
echo " [VALID NDJSON] $file"
|
||||
return 0
|
||||
else
|
||||
echo " [INVALID NDJSON] $file" >&2
|
||||
echo "$result" >&2
|
||||
ERRORS=$((ERRORS + 1))
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
echo " [SKIP] $file (python3 required for NDJSON validation)"
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# Directory Structure Validation
|
||||
# =============================================================================
|
||||
echo ""
|
||||
echo "=== Checking directory structure ==="
|
||||
check_dir "schemas"
|
||||
check_dir "exports"
|
||||
check_dir "kit"
|
||||
|
||||
# =============================================================================
|
||||
# Core Files Validation
|
||||
# =============================================================================
|
||||
echo ""
|
||||
echo "=== Checking core files ==="
|
||||
check_file "thresholds.yaml"
|
||||
check_file "thresholds.yaml.dsse"
|
||||
check_file "SHA256SUMS"
|
||||
|
||||
# =============================================================================
|
||||
# Schema Files Validation
|
||||
# =============================================================================
|
||||
echo ""
|
||||
echo "=== Checking schema files ==="
|
||||
check_file "schemas/observer_event.schema.json"
|
||||
check_file "schemas/observer_event.schema.json.dsse"
|
||||
check_file "schemas/webhook_admission.schema.json"
|
||||
check_file "schemas/webhook_admission.schema.json.dsse"
|
||||
|
||||
# =============================================================================
|
||||
# Kit Files Validation
|
||||
# =============================================================================
|
||||
echo ""
|
||||
echo "=== Checking kit files ==="
|
||||
check_file "kit/ed25519.pub"
|
||||
check_file "kit/verify.sh"
|
||||
check_file "kit/zastava-kit.tzst" false # Optional - may not be in source tree
|
||||
check_file "kit/zastava-kit.tzst.dsse" false
|
||||
|
||||
# =============================================================================
|
||||
# Export Files Validation
|
||||
# =============================================================================
|
||||
echo ""
|
||||
echo "=== Checking export files ==="
|
||||
|
||||
if [[ "$COMBINED_FORMAT" == "true" ]]; then
|
||||
# Combined format
|
||||
echo "(Combined format mode)"
|
||||
check_file "exports/combined.runtime.ndjson"
|
||||
check_file "exports/combined.runtime.ndjson.dsse"
|
||||
# Legacy files are optional in combined mode
|
||||
check_file "exports/observer_events.ndjson" false
|
||||
check_file "exports/webhook_admissions.ndjson" false
|
||||
else
|
||||
# Legacy format
|
||||
echo "(Legacy format mode)"
|
||||
check_file "exports/observer_events.ndjson"
|
||||
check_file "exports/observer_events.ndjson.dsse"
|
||||
check_file "exports/webhook_admissions.ndjson"
|
||||
check_file "exports/webhook_admissions.ndjson.dsse"
|
||||
# Combined is optional in legacy mode
|
||||
check_file "exports/combined.runtime.ndjson" false
|
||||
fi
|
||||
|
||||
# =============================================================================
|
||||
# JSON/NDJSON Format Validation
|
||||
# =============================================================================
|
||||
echo ""
|
||||
echo "=== Validating file formats ==="
|
||||
|
||||
validate_json "schemas/observer_event.schema.json"
|
||||
validate_json "schemas/webhook_admission.schema.json"
|
||||
|
||||
if [[ "$COMBINED_FORMAT" == "true" ]] && [[ -f "$KIT_DIR/exports/combined.runtime.ndjson" ]]; then
|
||||
validate_ndjson "exports/combined.runtime.ndjson"
|
||||
else
|
||||
if [[ -f "$KIT_DIR/exports/observer_events.ndjson" ]]; then
|
||||
validate_ndjson "exports/observer_events.ndjson"
|
||||
fi
|
||||
if [[ -f "$KIT_DIR/exports/webhook_admissions.ndjson" ]]; then
|
||||
validate_ndjson "exports/webhook_admissions.ndjson"
|
||||
fi
|
||||
fi
|
||||
|
||||
# =============================================================================
|
||||
# Combined Format Structure Validation
|
||||
# =============================================================================
|
||||
if [[ "$COMBINED_FORMAT" == "true" ]] && [[ -f "$KIT_DIR/exports/combined.runtime.ndjson" ]]; then
|
||||
echo ""
|
||||
echo "=== Validating combined format structure ==="
|
||||
|
||||
if command -v python3 >/dev/null 2>&1; then
|
||||
python3 - "$KIT_DIR/exports/combined.runtime.ndjson" <<'PYTHON'
|
||||
import json
|
||||
import sys
|
||||
|
||||
path = sys.argv[1]
|
||||
errors = []
|
||||
has_header = False
|
||||
has_footer = False
|
||||
record_types = set()
|
||||
|
||||
with open(path, 'r') as f:
|
||||
for i, line in enumerate(f, 1):
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
try:
|
||||
record = json.loads(line)
|
||||
rtype = record.get("type", "unknown")
|
||||
record_types.add(rtype)
|
||||
|
||||
if rtype == "combined.header":
|
||||
if has_header:
|
||||
errors.append(f"Line {i}: duplicate header")
|
||||
has_header = True
|
||||
if i != 1:
|
||||
errors.append(f"Line {i}: header should be first record")
|
||||
elif rtype == "combined.footer":
|
||||
has_footer = True
|
||||
except json.JSONDecodeError as e:
|
||||
errors.append(f"Line {i}: {e}")
|
||||
|
||||
if not has_header:
|
||||
errors.append("Missing combined.header record")
|
||||
if not has_footer:
|
||||
errors.append("Missing combined.footer record")
|
||||
|
||||
if errors:
|
||||
for e in errors:
|
||||
print(f" [ERROR] {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
print(f" [OK] Header and footer present")
|
||||
print(f" [OK] Record types: {', '.join(sorted(record_types))}")
|
||||
PYTHON
|
||||
if [[ $? -ne 0 ]]; then
|
||||
ERRORS=$((ERRORS + 1))
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# =============================================================================
|
||||
# Summary
|
||||
# =============================================================================
|
||||
echo ""
|
||||
echo "=== Validation Summary ==="
|
||||
|
||||
if [[ $ERRORS -eq 0 ]]; then
|
||||
echo "All validations passed!"
|
||||
exit 0
|
||||
else
|
||||
echo "$ERRORS validation error(s) found" >&2
|
||||
exit 1
|
||||
fi
|
||||
@@ -52,8 +52,22 @@ targets = [
|
||||
("webhook exports", root / "exports" / "webhook_admissions.ndjson", root / "exports" / "webhook_admissions.ndjson.dsse", "application/vnd.stellaops.zastava.webhook-admissions+ndjson;version=1"),
|
||||
]
|
||||
|
||||
# Combined runtime format (optional - may not exist in all kits)
|
||||
combined_targets = [
|
||||
("combined runtime", root / "exports" / "combined.runtime.ndjson", root / "exports" / "combined.runtime.ndjson.dsse", "application/vnd.stellaops.combined.runtime+ndjson;version=1"),
|
||||
]
|
||||
|
||||
for name, payload_path, envelope_path, ptype in targets:
|
||||
verify(name, payload_path, envelope_path, ptype)
|
||||
|
||||
# Verify combined format if present
|
||||
for name, payload_path, envelope_path, ptype in combined_targets:
|
||||
if payload_path.exists() and envelope_path.exists():
|
||||
verify(name, payload_path, envelope_path, ptype)
|
||||
elif payload_path.exists() or envelope_path.exists():
|
||||
print(f"WARNING: {name} - incomplete (payload and envelope must both exist)")
|
||||
else:
|
||||
print(f"SKIP: {name} (not present)")
|
||||
PY
|
||||
|
||||
echo "OK: SHA256 + DSSE signatures verified"
|
||||
|
||||
318
docs/modules/zastava/operations/docker-socket-permissions.md
Normal file
318
docs/modules/zastava/operations/docker-socket-permissions.md
Normal file
@@ -0,0 +1,318 @@
|
||||
# Docker Socket Permissions and Security
|
||||
|
||||
This document covers the security considerations and configuration options for Docker socket access in Zastava Agent deployments.
|
||||
|
||||
## Overview
|
||||
|
||||
The Zastava Agent requires read access to the Docker socket (`/var/run/docker.sock`) to:
|
||||
|
||||
1. **Monitor container lifecycle events** - Start, stop, pause, die, etc.
|
||||
2. **Inspect running containers** - Image digest, labels, environment variables
|
||||
3. **Collect runtime evidence** - Loaded libraries, process information
|
||||
|
||||
## Default Configuration
|
||||
|
||||
By default, the agent runs as:
|
||||
- **User:** `zastava-agent` (system user)
|
||||
- **Group:** `docker` (grants socket access)
|
||||
- **Socket:** `/var/run/docker.sock`
|
||||
|
||||
```yaml
|
||||
# systemd service configuration
|
||||
User=zastava-agent
|
||||
Group=docker
|
||||
ReadWritePaths=/var/run/docker.sock
|
||||
```
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### Docker Socket Exposure Risks
|
||||
|
||||
The Docker socket provides significant privileges:
|
||||
|
||||
| Capability | Risk Level | Mitigation |
|
||||
|------------|-----------|------------|
|
||||
| List containers | Low | Required for operation |
|
||||
| Inspect containers | Low | Required for operation |
|
||||
| Read container logs | Medium | Agent does not use this |
|
||||
| Create containers | High | Agent does not use this |
|
||||
| Execute in containers | Critical | Agent does not use this |
|
||||
| Pull images | High | Agent does not use this |
|
||||
| Remove containers | High | Agent does not use this |
|
||||
|
||||
### Agent Behavior
|
||||
|
||||
The Zastava Agent performs **read-only operations**:
|
||||
|
||||
```go
|
||||
// Operations used by agent
|
||||
docker.ContainerList(...) // List running containers
|
||||
docker.ContainerInspect(...) // Get container details
|
||||
docker.Events(...) // Subscribe to lifecycle events
|
||||
```
|
||||
|
||||
The agent **does not** perform write operations such as creating, starting, stopping, or removing containers.
|
||||
|
||||
## Alternative Configurations
|
||||
|
||||
### Option 1: Docker API Proxy (Recommended for High-Security)
|
||||
|
||||
Deploy a Docker API proxy that restricts available operations:
|
||||
|
||||
```yaml
|
||||
# docker-proxy configuration example
|
||||
allowed_endpoints:
|
||||
- "GET /containers/json" # List containers
|
||||
- "GET /containers/*/json" # Inspect container
|
||||
- "GET /events" # Subscribe to events
|
||||
- "GET /_ping" # Health check
|
||||
```
|
||||
|
||||
Example proxy: [Tecnativa/docker-socket-proxy](https://github.com/Tecnativa/docker-socket-proxy)
|
||||
|
||||
```bash
|
||||
# Deploy proxy
|
||||
docker run -d \
|
||||
--name docker-proxy \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock:ro \
|
||||
-e CONTAINERS=1 \
|
||||
-e EVENTS=1 \
|
||||
-p 2375:2375 \
|
||||
tecnativa/docker-socket-proxy
|
||||
```
|
||||
|
||||
Configure agent to use proxy:
|
||||
```env
|
||||
ZASTAVA_AGENT__DockerEndpoint=tcp://localhost:2375
|
||||
```
|
||||
|
||||
### Option 2: Unix Socket with ACLs
|
||||
|
||||
Use filesystem ACLs for fine-grained access:
|
||||
|
||||
```bash
|
||||
# Install ACL support
|
||||
sudo apt-get install acl
|
||||
|
||||
# Set ACL for zastava-agent user
|
||||
sudo setfacl -m u:zastava-agent:rw /var/run/docker.sock
|
||||
|
||||
# Verify ACL
|
||||
getfacl /var/run/docker.sock
|
||||
```
|
||||
|
||||
This allows removing the user from the `docker` group while maintaining socket access.
|
||||
|
||||
### Option 3: SELinux/AppArmor Policies
|
||||
|
||||
#### SELinux Policy
|
||||
|
||||
```te
|
||||
# zastava-agent.te
|
||||
module zastava_agent 1.0;
|
||||
|
||||
require {
|
||||
type docker_var_run_t;
|
||||
type zastava_agent_t;
|
||||
class sock_file { read write };
|
||||
}
|
||||
|
||||
# Allow read/write to Docker socket
|
||||
allow zastava_agent_t docker_var_run_t:sock_file { read write getattr };
|
||||
```
|
||||
|
||||
#### AppArmor Profile
|
||||
|
||||
```apparmor
|
||||
# /etc/apparmor.d/zastava-agent
|
||||
profile zastava-agent /opt/stellaops/zastava-agent/StellaOps.Zastava.Agent {
|
||||
# Docker socket access
|
||||
/var/run/docker.sock rw,
|
||||
|
||||
# Deny network access except to scanner backend
|
||||
network inet stream,
|
||||
network inet6 stream,
|
||||
|
||||
# Read-only system access
|
||||
/etc/stellaops/* r,
|
||||
/opt/stellaops/zastava-agent/** mr,
|
||||
|
||||
# Data directory
|
||||
/var/lib/zastava-agent/** rw,
|
||||
}
|
||||
```
|
||||
|
||||
### Option 4: Rootless Docker
|
||||
|
||||
For maximum isolation, use rootless Docker:
|
||||
|
||||
```bash
|
||||
# Install rootless Docker
|
||||
dockerd-rootless-setuptool.sh install
|
||||
|
||||
# Configure agent to use rootless socket
|
||||
export ZASTAVA_AGENT__DockerEndpoint=unix:///run/user/1000/docker.sock
|
||||
```
|
||||
|
||||
Note: Rootless Docker has some limitations with networking and storage drivers.
|
||||
|
||||
## Log Paths
|
||||
|
||||
### Agent Logs
|
||||
|
||||
| Component | Log Location |
|
||||
|-----------|--------------|
|
||||
| Agent stdout/stderr | `journalctl -u zastava-agent` |
|
||||
| Runtime events | `/var/lib/zastava-agent/runtime-events/*.ndjson` |
|
||||
| Health check | Agent stdout (structured JSON) |
|
||||
|
||||
### Log Configuration
|
||||
|
||||
```env
|
||||
# Set log level
|
||||
Serilog__MinimumLevel__Default=Information
|
||||
|
||||
# Available levels: Verbose, Debug, Information, Warning, Error, Fatal
|
||||
```
|
||||
|
||||
### Log Rotation
|
||||
|
||||
Event buffer files are automatically rotated:
|
||||
|
||||
```yaml
|
||||
# Default settings
|
||||
event_buffer:
|
||||
max_file_size_mb: 10
|
||||
max_total_size_mb: 100
|
||||
retention_hours: 24
|
||||
```
|
||||
|
||||
## Health Check Configuration
|
||||
|
||||
The agent exposes HTTP health endpoints:
|
||||
|
||||
| Endpoint | Port | Description |
|
||||
|----------|------|-------------|
|
||||
| `/healthz` | 8080 | Liveness probe |
|
||||
| `/readyz` | 8080 | Readiness probe |
|
||||
| `/livez` | 8080 | Alias for liveness |
|
||||
|
||||
### Health Check Port
|
||||
|
||||
Configure via environment variable:
|
||||
|
||||
```env
|
||||
ZASTAVA_AGENT__HealthCheck__Port=8080
|
||||
```
|
||||
|
||||
### Health Check Behavior
|
||||
|
||||
**Liveness (`/healthz`):**
|
||||
- Returns 200 if agent process is running
|
||||
- Returns 503 if critical subsystems failed
|
||||
|
||||
**Readiness (`/readyz`):**
|
||||
- Returns 200 if agent can process events
|
||||
- Returns 503 if:
|
||||
- Docker socket is unreachable
|
||||
- Event buffer is not writable
|
||||
- Backend connection failed
|
||||
|
||||
### Prometheus Metrics
|
||||
|
||||
Health metrics are exposed at `/metrics`:
|
||||
|
||||
```
|
||||
# HELP zastava_agent_docker_connected Docker connectivity status
|
||||
# TYPE zastava_agent_docker_connected gauge
|
||||
zastava_agent_docker_connected 1
|
||||
|
||||
# HELP zastava_agent_buffer_writable Event buffer writability
|
||||
# TYPE zastava_agent_buffer_writable gauge
|
||||
zastava_agent_buffer_writable 1
|
||||
|
||||
# HELP zastava_agent_events_buffered Number of events in buffer
|
||||
# TYPE zastava_agent_events_buffered gauge
|
||||
zastava_agent_events_buffered 42
|
||||
```
|
||||
|
||||
## Monitoring Recommendations
|
||||
|
||||
### Alerting Rules
|
||||
|
||||
```yaml
|
||||
groups:
|
||||
- name: zastava-agent
|
||||
rules:
|
||||
- alert: ZastavaAgentDown
|
||||
expr: up{job="zastava-agent"} == 0
|
||||
for: 5m
|
||||
annotations:
|
||||
summary: "Zastava Agent is down on {{ $labels.instance }}"
|
||||
|
||||
- alert: ZastavaDockerDisconnected
|
||||
expr: zastava_agent_docker_connected == 0
|
||||
for: 1m
|
||||
annotations:
|
||||
summary: "Zastava Agent lost Docker connectivity"
|
||||
|
||||
- alert: ZastavaBufferNotWritable
|
||||
expr: zastava_agent_buffer_writable == 0
|
||||
for: 1m
|
||||
severity: critical
|
||||
annotations:
|
||||
summary: "Zastava event buffer is not writable"
|
||||
```
|
||||
|
||||
### Grafana Dashboard
|
||||
|
||||
Import the Zastava monitoring dashboard from:
|
||||
`docs/modules/zastava/operations/dashboards/zastava-observability.json`
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Cannot Access Docker Socket
|
||||
|
||||
```bash
|
||||
# Check socket exists
|
||||
ls -la /var/run/docker.sock
|
||||
|
||||
# Check agent user groups
|
||||
id zastava-agent
|
||||
|
||||
# Check Docker daemon is running
|
||||
systemctl status docker
|
||||
|
||||
# Test socket access manually
|
||||
sudo -u zastava-agent docker ps
|
||||
```
|
||||
|
||||
### Permission Denied Errors
|
||||
|
||||
```bash
|
||||
# Add user to docker group (if not using ACLs)
|
||||
sudo usermod -aG docker zastava-agent
|
||||
|
||||
# Restart agent
|
||||
sudo systemctl restart zastava-agent
|
||||
```
|
||||
|
||||
### Events Not Being Received
|
||||
|
||||
```bash
|
||||
# Check Docker events stream
|
||||
docker events --since 1m
|
||||
|
||||
# Verify agent can see events
|
||||
journalctl -u zastava-agent | grep -i "event"
|
||||
|
||||
# Check event buffer
|
||||
ls -la /var/lib/zastava-agent/runtime-events/
|
||||
```
|
||||
|
||||
## References
|
||||
|
||||
- [Docker Engine Security](https://docs.docker.com/engine/security/)
|
||||
- [Docker Socket Security](https://docs.docker.com/engine/security/protect-access/)
|
||||
- [Rootless Docker](https://docs.docker.com/engine/security/rootless/)
|
||||
- [docker-socket-proxy](https://github.com/Tecnativa/docker-socket-proxy)
|
||||
367
docs/modules/zastava/operations/windows.md
Normal file
367
docs/modules/zastava/operations/windows.md
Normal file
@@ -0,0 +1,367 @@
|
||||
# Windows Container Deployment Guide
|
||||
|
||||
This guide covers deploying and operating the Zastava Agent for Windows container monitoring.
|
||||
|
||||
## Overview
|
||||
|
||||
The Zastava Agent supports Windows container runtime monitoring via:
|
||||
|
||||
1. **Docker Desktop for Windows** - Docker API over named pipe
|
||||
2. **Docker Engine on Windows Server** - Native Windows containers
|
||||
3. **Windows Server Core containers** - Server-class workloads
|
||||
|
||||
## System Requirements
|
||||
|
||||
### Minimum Requirements
|
||||
|
||||
| Component | Requirement |
|
||||
|-----------|-------------|
|
||||
| Operating System | Windows Server 2019 or later |
|
||||
| Container Runtime | Docker Engine 20.10+ or Docker Desktop 4.x |
|
||||
| .NET Runtime | .NET 10.0 or later |
|
||||
| Memory | 512 MB minimum, 1 GB recommended |
|
||||
| Disk Space | 100 MB for agent + event buffer space |
|
||||
|
||||
### Supported Windows Versions
|
||||
|
||||
| Windows Version | Container Types | Status |
|
||||
|-----------------|-----------------|--------|
|
||||
| Windows Server 2022 | Windows Server Core, Nano Server | Full Support |
|
||||
| Windows Server 2019 | Windows Server Core, Nano Server | Full Support |
|
||||
| Windows 11 | Windows/Linux containers (via WSL2) | Supported |
|
||||
| Windows 10 | Windows/Linux containers (via WSL2) | Supported |
|
||||
|
||||
## Installation
|
||||
|
||||
### Option 1: PowerShell Installation Script
|
||||
|
||||
```powershell
|
||||
# Download and run installer
|
||||
Invoke-WebRequest -Uri "https://releases.stellaops.org/zastava-agent/latest/Install-ZastavaAgent.ps1" -OutFile "$env:TEMP\Install-ZastavaAgent.ps1"
|
||||
|
||||
# Install with required parameters
|
||||
& "$env:TEMP\Install-ZastavaAgent.ps1" `
|
||||
-Tenant "your-tenant" `
|
||||
-ScannerBackendUrl "https://scanner.internal" `
|
||||
-InstallPath "C:\Program Files\StellaOps\Zastava"
|
||||
```
|
||||
|
||||
### Option 2: Manual Installation
|
||||
|
||||
1. **Download the agent:**
|
||||
|
||||
```powershell
|
||||
$version = "latest"
|
||||
$arch = if ([System.Environment]::Is64BitOperatingSystem) { "x64" } else { "x86" }
|
||||
$url = "https://releases.stellaops.org/zastava-agent/$version/zastava-agent-win-$arch.zip"
|
||||
|
||||
Invoke-WebRequest -Uri $url -OutFile "C:\temp\zastava-agent.zip"
|
||||
```
|
||||
|
||||
2. **Extract and install:**
|
||||
|
||||
```powershell
|
||||
$installPath = "C:\Program Files\StellaOps\Zastava"
|
||||
New-Item -ItemType Directory -Path $installPath -Force
|
||||
Expand-Archive -Path "C:\temp\zastava-agent.zip" -DestinationPath $installPath
|
||||
```
|
||||
|
||||
3. **Create configuration file:**
|
||||
|
||||
```powershell
|
||||
@"
|
||||
# Zastava Agent Configuration
|
||||
ZASTAVA_TENANT=your-tenant
|
||||
ZASTAVA_AGENT__Backend__BaseAddress=https://scanner.internal
|
||||
ZASTAVA_AGENT__DockerEndpoint=npipe:////./pipe/docker_engine
|
||||
ZASTAVA_AGENT__EventBufferPath=C:\ProgramData\StellaOps\Zastava\runtime-events
|
||||
ZASTAVA_AGENT__HealthCheck__Port=8080
|
||||
"@ | Out-File -FilePath "$installPath\zastava-agent.env" -Encoding UTF8
|
||||
```
|
||||
|
||||
4. **Install as Windows Service:**
|
||||
|
||||
```powershell
|
||||
# Using NSSM (Non-Sucking Service Manager)
|
||||
nssm install ZastavaAgent "$installPath\StellaOps.Zastava.Agent.exe"
|
||||
nssm set ZastavaAgent AppDirectory "$installPath"
|
||||
nssm set ZastavaAgent AppEnvironmentExtra "+DOTNET_ENVIRONMENT=Production"
|
||||
nssm set ZastavaAgent DisplayName "StellaOps Zastava Agent"
|
||||
nssm set ZastavaAgent Description "Container Runtime Monitor for StellaOps"
|
||||
nssm set ZastavaAgent Start SERVICE_AUTO_START
|
||||
```
|
||||
|
||||
Alternatively, use the native `sc.exe`:
|
||||
|
||||
```powershell
|
||||
sc.exe create ZastavaAgent binPath= "$installPath\StellaOps.Zastava.Agent.exe" start= auto
|
||||
```
|
||||
|
||||
5. **Start the service:**
|
||||
|
||||
```powershell
|
||||
Start-Service ZastavaAgent
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
### Docker Named Pipe Access
|
||||
|
||||
The Windows agent connects to Docker via named pipe:
|
||||
|
||||
```
|
||||
npipe:////./pipe/docker_engine
|
||||
```
|
||||
|
||||
### Environment Variables
|
||||
|
||||
| Variable | Default | Description |
|
||||
|----------|---------|-------------|
|
||||
| `ZASTAVA_TENANT` | (required) | Tenant identifier |
|
||||
| `ZASTAVA_AGENT__Backend__BaseAddress` | (required) | Scanner backend URL |
|
||||
| `ZASTAVA_AGENT__DockerEndpoint` | `npipe:////./pipe/docker_engine` | Docker API endpoint |
|
||||
| `ZASTAVA_AGENT__EventBufferPath` | `%ProgramData%\StellaOps\Zastava\runtime-events` | Event buffer directory |
|
||||
| `ZASTAVA_AGENT__HealthCheck__Port` | `8080` | Health check HTTP port |
|
||||
|
||||
### Configuration File Location
|
||||
|
||||
```
|
||||
C:\Program Files\StellaOps\Zastava\zastava-agent.env
|
||||
```
|
||||
|
||||
## Docker Desktop Configuration
|
||||
|
||||
### Enable TCP/Named Pipe Access
|
||||
|
||||
1. Open Docker Desktop Settings
|
||||
2. Go to **Settings → General**
|
||||
3. Enable **Expose daemon on tcp://localhost:2375 without TLS** (for development only)
|
||||
4. Or use the named pipe (default): `npipe:////./pipe/docker_engine`
|
||||
|
||||
### Windows Containers Mode
|
||||
|
||||
Ensure Docker is in Windows containers mode:
|
||||
|
||||
```powershell
|
||||
# Check current mode
|
||||
docker info --format '{{.OSType}}'
|
||||
|
||||
# Should output: windows
|
||||
```
|
||||
|
||||
To switch to Windows containers:
|
||||
- Right-click Docker Desktop tray icon
|
||||
- Select "Switch to Windows containers..."
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### Named Pipe Permissions
|
||||
|
||||
The Docker named pipe requires membership in:
|
||||
- `docker-users` group (Docker Desktop)
|
||||
- `Administrators` group (Docker Engine)
|
||||
|
||||
```powershell
|
||||
# Add service account to docker-users group
|
||||
Add-LocalGroupMember -Group "docker-users" -Member "NT SERVICE\ZastavaAgent"
|
||||
```
|
||||
|
||||
### Windows Firewall
|
||||
|
||||
If health checks are accessed remotely:
|
||||
|
||||
```powershell
|
||||
New-NetFirewallRule `
|
||||
-DisplayName "Zastava Agent Health Check" `
|
||||
-Direction Inbound `
|
||||
-Protocol TCP `
|
||||
-LocalPort 8080 `
|
||||
-Action Allow
|
||||
```
|
||||
|
||||
### PE Library Hashing
|
||||
|
||||
The agent collects SHA-256 hashes of loaded DLLs from Windows containers:
|
||||
|
||||
- Portable Executable (PE) format parsing
|
||||
- Version information extraction
|
||||
- Digital signature verification (if signed)
|
||||
|
||||
## Health Monitoring
|
||||
|
||||
### Health Endpoints
|
||||
|
||||
| Endpoint | URL | Description |
|
||||
|----------|-----|-------------|
|
||||
| Liveness | `http://localhost:8080/healthz` | Agent is running |
|
||||
| Readiness | `http://localhost:8080/readyz` | Agent can process events |
|
||||
|
||||
### PowerShell Health Check
|
||||
|
||||
```powershell
|
||||
# Check agent health
|
||||
Invoke-RestMethod -Uri "http://localhost:8080/healthz"
|
||||
|
||||
# Check readiness
|
||||
Invoke-RestMethod -Uri "http://localhost:8080/readyz"
|
||||
```
|
||||
|
||||
### Windows Service Status
|
||||
|
||||
```powershell
|
||||
# Check service status
|
||||
Get-Service ZastavaAgent
|
||||
|
||||
# View service events
|
||||
Get-EventLog -LogName Application -Source ZastavaAgent -Newest 20
|
||||
```
|
||||
|
||||
## Logging
|
||||
|
||||
### Event Log
|
||||
|
||||
Agent logs are written to Windows Event Log:
|
||||
|
||||
- **Log:** Application
|
||||
- **Source:** ZastavaAgent
|
||||
|
||||
```powershell
|
||||
# View recent events
|
||||
Get-EventLog -LogName Application -Source ZastavaAgent -Newest 50
|
||||
|
||||
# Filter by level
|
||||
Get-EventLog -LogName Application -Source ZastavaAgent -EntryType Error,Warning
|
||||
```
|
||||
|
||||
### File Logging (Optional)
|
||||
|
||||
Enable file logging via configuration:
|
||||
|
||||
```
|
||||
Serilog__WriteTo__0__Name=File
|
||||
Serilog__WriteTo__0__Args__path=C:\ProgramData\StellaOps\Zastava\logs\agent-.log
|
||||
Serilog__WriteTo__0__Args__rollingInterval=Day
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Agent Won't Start
|
||||
|
||||
1. **Check Docker is running:**
|
||||
```powershell
|
||||
docker info
|
||||
```
|
||||
|
||||
2. **Verify named pipe exists:**
|
||||
```powershell
|
||||
Test-Path "\\.\pipe\docker_engine"
|
||||
```
|
||||
|
||||
3. **Check service account permissions:**
|
||||
```powershell
|
||||
whoami /groups
|
||||
```
|
||||
|
||||
4. **Review Event Log:**
|
||||
```powershell
|
||||
Get-EventLog -LogName Application -Source ZastavaAgent -Newest 10
|
||||
```
|
||||
|
||||
### Cannot Connect to Docker
|
||||
|
||||
1. **Test Docker API:**
|
||||
```powershell
|
||||
Invoke-RestMethod -Uri "http://localhost:2375/info" -Method Get
|
||||
# or for named pipe
|
||||
docker version
|
||||
```
|
||||
|
||||
2. **Verify Docker mode:**
|
||||
```powershell
|
||||
docker info --format '{{.OSType}}'
|
||||
# Should be "windows" for Windows containers
|
||||
```
|
||||
|
||||
3. **Check pipe permissions:**
|
||||
```powershell
|
||||
# List pipe ACL
|
||||
Get-Acl "\\.\pipe\docker_engine" | Format-List
|
||||
```
|
||||
|
||||
### Events Not Being Sent
|
||||
|
||||
1. **Check event buffer:**
|
||||
```powershell
|
||||
Get-ChildItem "C:\ProgramData\StellaOps\Zastava\runtime-events"
|
||||
```
|
||||
|
||||
2. **Verify backend connectivity:**
|
||||
```powershell
|
||||
Test-NetConnection -ComputerName scanner.internal -Port 443
|
||||
```
|
||||
|
||||
3. **Check readiness:**
|
||||
```powershell
|
||||
Invoke-RestMethod -Uri "http://localhost:8080/readyz"
|
||||
```
|
||||
|
||||
## Upgrade Procedure
|
||||
|
||||
1. **Stop the service:**
|
||||
```powershell
|
||||
Stop-Service ZastavaAgent
|
||||
```
|
||||
|
||||
2. **Backup configuration:**
|
||||
```powershell
|
||||
Copy-Item "C:\Program Files\StellaOps\Zastava\zastava-agent.env" "C:\temp\zastava-agent.env.bak"
|
||||
```
|
||||
|
||||
3. **Download and extract new version:**
|
||||
```powershell
|
||||
$version = "1.2.0"
|
||||
$url = "https://releases.stellaops.org/zastava-agent/$version/zastava-agent-win-x64.zip"
|
||||
Invoke-WebRequest -Uri $url -OutFile "C:\temp\zastava-agent.zip"
|
||||
Expand-Archive -Path "C:\temp\zastava-agent.zip" -DestinationPath "C:\Program Files\StellaOps\Zastava" -Force
|
||||
```
|
||||
|
||||
4. **Restore configuration:**
|
||||
```powershell
|
||||
Copy-Item "C:\temp\zastava-agent.env.bak" "C:\Program Files\StellaOps\Zastava\zastava-agent.env"
|
||||
```
|
||||
|
||||
5. **Start the service:**
|
||||
```powershell
|
||||
Start-Service ZastavaAgent
|
||||
```
|
||||
|
||||
6. **Verify health:**
|
||||
```powershell
|
||||
Invoke-RestMethod -Uri "http://localhost:8080/healthz"
|
||||
```
|
||||
|
||||
## Uninstallation
|
||||
|
||||
```powershell
|
||||
# Stop and remove service
|
||||
Stop-Service ZastavaAgent
|
||||
sc.exe delete ZastavaAgent
|
||||
|
||||
# Remove installation directory
|
||||
Remove-Item -Path "C:\Program Files\StellaOps\Zastava" -Recurse -Force
|
||||
|
||||
# Remove data directory
|
||||
Remove-Item -Path "C:\ProgramData\StellaOps\Zastava" -Recurse -Force
|
||||
```
|
||||
|
||||
## Known Limitations
|
||||
|
||||
1. **Hyper-V isolation only** - Process isolation containers have limited observability
|
||||
2. **Windows container logs** - Container stdout/stderr capture not yet implemented
|
||||
3. **WSL2 containers** - Linux containers on Windows require WSL2 mode, not directly supported
|
||||
|
||||
## References
|
||||
|
||||
- [Docker Desktop for Windows](https://docs.docker.com/desktop/windows/)
|
||||
- [Windows Server Containers](https://docs.microsoft.com/en-us/virtualization/windowscontainers/)
|
||||
- [Docker Engine on Windows Server](https://docs.docker.com/engine/install/windows/)
|
||||
Reference in New Issue
Block a user