ICP Platform - Linux VM Integration Guide
Complete guide for integrating ICP (Identity Control Plane) workload identity on Linux virtual machines and bare-metal servers.
Table of Contents
- Prerequisites
- Architecture Overview
- Installation
- Configuration
- Workload Registration
- SDK Integration
- Production Deployment
- Troubleshooting
- Best Practices
Prerequisites
System Requirements
- Operating System: Ubuntu 20.04+, Debian 11+, RHEL 8+, CentOS 8+, Amazon Linux 2
- Architecture: x86_64 (ARM64 support available)
- RAM: Minimum 512MB, Recommended 1GB
- Disk: 100MB for agent + 500MB for logs/cache
- Python: 3.8+ (for SDK integration)
Network Requirements
Outbound Connections Required:
- ICP Server: https://dev.api.authsec.dev/spiresvc (HTTPS)
- Package Repos: For installing dependencies
Inbound Connections:
- None (agent listens only on local Unix socket)
Credentials
You'll need:
- Tenant ID - Provided by AuthSec
- ICP Server URL - Your ICP Server endpoint
- Node ID - Unique identifier for this VM (e.g.,
vm-prod-web-01) - Join Token (optional) - For automatic attestation
Architecture Overview
Linux VM Deployment Architecture
┌──────────────────────────────────────────────────────────────────┐
│ Linux VM / Bare Metal │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ ICP Agent (systemd service) │ │
│ │ │ │
│ │ Process: /usr/local/bin/icp-agent │ │
│ │ Config: /etc/icp-agent/config.yaml │ │
│ │ Socket: /run/spire/sockets/agent.sock │ │
│ │ User: root (or icp-agent) │ │
│ │ Auto-start: systemd │ │
│ └──────────────────────┬─────────────────────────────────────┘ │
│ │ │
│ │ Unix Socket │
│ │ /run/spire/sockets/agent.sock │
│ ┌───────────────┴────────────────┐ │
│ │ │ │
│ ┌──────▼──────────────┐ ┌────────────▼─────────────┐ │
│ │ Workload 1 │ │ Workload 2 │ │
│ │ (Python App) │ │ (Go Service) │ │
│ │ │ │ │ │
│ │ User: app-user │ │ User: svc-user │ │
│ │ PID: 12345 │ │ PID: 67890 │ │
│ │ UID: 1000 │ │ UID: 1001 │ │
│ │ GID: 1000 │ │ GID: 1001 │ │
│ │ │ │ │ │
│ │ SDK Integration │ │ SDK Integration │ │
│ └─────────────────────┘ └──────────────────────────┘ │
│ │
└───────────────────────────────────────────────────────────────────┘
│
│ HTTPS + mTLS
▼
┌─────────────────────────────┐
│ ICP Server (SaaS) │
│ - Agent SVID Issuance │
│ - Workload SVID Issuance │
└─────────────────────────────┘
How Unix Attestation Works
The agent collects these selectors from running processes:
| Selector | Description | Example |
|---|---|---|
unix:uid | User ID | "1000" |
unix:gid | Group ID | "1000" |
unix:path | Executable path | "/usr/bin/python3" |
unix:sha256 | Binary SHA-256 hash | "abc123def456..." |
unix:process | Process name | "python3" |
unix:pid | Process ID | "12345" (not recommended - changes on restart) |
Note: Username and group name lookups are not included as selectors. Use numeric UID/GID instead.
Matching: Workload entries with selectors that are a subset of collected selectors will match.
Installation
Method 1: Quick Install Script
# Download and run installer
curl -fsSL https://install.authsec.ai/icp-agent.sh | sudo bash -s -- \
--tenant-id "your-tenant-id-here" \
--icp-server "https://dev.api.authsec.dev/spiresvc" \
--node-id "vm-prod-web-01"
The script will:
- Install dependencies (Python 3, systemd)
- Download ICP Agent binary
- Create systemd service
- Start the agent
- Enable auto-start on boot
Method 2: Manual Installation
Step 1: Install Dependencies
Ubuntu/Debian:
sudo apt-get update
sudo apt-get install -y python3 python3-pip git systemd
RHEL/CentOS:
sudo yum install -y python3 python3-pip git systemd
Step 2: Download ICP Agent
# Create installation directory
sudo mkdir -p /opt/icp-agent
cd /opt/icp-agent
# Clone repository
sudo git clone https://github.com/your-org/icp-agent.git .
# Install Python dependencies
sudo pip3 install -r requirements.txt
Step 3: Create Configuration
# Create config directory
sudo mkdir -p /etc/icp-agent
# Create config file
sudo tee /etc/icp-agent/config.yaml > /dev/null <<EOF
agent:
tenant_id: "your-tenant-id-here"
node_id: "vm-prod-web-01"
data_dir: "/var/lib/icp-agent"
socket_path: "/run/spire/sockets/agent.sock"
renewal_threshold: "6h"
icp_service:
address: "https://your-icp-server.example.com/spiresvc"
trust_bundle_path: "/etc/icp-agent/ca-bundle.pem"
timeout: 30
max_retries: 3
retry_backoff: 5
attestation:
type: "unix"
unix:
method: "procfs"
security:
cache_encryption_key: ""
cache_path: "/var/lib/icp-agent/cache/svid.cache"
logging:
level: "info"
format: "json"
file_path: "/var/log/icp-agent/agent.log"
health:
enabled: true
port: 8080
bind_address: "127.0.0.1"
EOF
Step 4: Create systemd Service
sudo tee /etc/systemd/system/icp-agent.service > /dev/null <<EOF
[Unit]
Description=ICP Agent - Workload Identity Service
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=root
Group=root
ExecStart=/opt/icp-agent/icp_agent/main.py -c /etc/icp-agent/config.yaml
Restart=on-failure
RestartSec=5s
LimitNOFILE=65536
# Security hardening
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/lib/icp-agent /var/log/icp-agent /run/spire
# Environment
Environment="PYTHONUNBUFFERED=1"
[Install]
WantedBy=multi-user.target
EOF
Step 5: Create Required Directories
# Create data and log directories
sudo mkdir -p /var/lib/icp-agent/cache
sudo mkdir -p /var/log/icp-agent
sudo mkdir -p /run/spire/sockets
# Set permissions
sudo chmod 755 /run/spire/sockets
sudo chmod 700 /var/lib/icp-agent
Step 6: Start Agent
# Reload systemd
sudo systemctl daemon-reload
# Start agent
sudo systemctl start icp-agent
# Enable auto-start on boot
sudo systemctl enable icp-agent
# Check status
sudo systemctl status icp-agent
Verification
# Check agent is running
sudo systemctl status icp-agent
# Check logs
sudo journalctl -u icp-agent -f
# Check socket exists
ls -l /run/spire/sockets/agent.sock
# Test health endpoint
curl http://localhost:8080/healthz
Expected output:
{"status": "healthy"}
Configuration
Configuration File Reference
File: /etc/icp-agent/config.yaml
# Agent Configuration
agent:
# Your tenant ID (provided by AuthSec)
tenant_id: "your-tenant-id-here"
# Unique node identifier (hostname, IP, or custom ID)
node_id: "vm-prod-web-01"
# Data directory for cache and state
data_dir: "/var/lib/icp-agent"
# Unix socket path for Workload API
socket_path: "/run/spire/sockets/agent.sock"
# When to renew agent SVID (before expiry)
renewal_threshold: "6h"
# ICP Server Configuration
icp_service:
# ICP Server URL
address: "https://dev.api.authsec.dev/spiresvc"
# Path to CA bundle (for verifying ICP Server)
trust_bundle_path: "/etc/icp-agent/ca-bundle.pem"
# Connection timeout (seconds)
timeout: 30
# Number of retries for failed requests
max_retries: 3
# Backoff time between retries (seconds)
retry_backoff: 5
# Attestation Configuration
attestation:
# Attestation type (unix, auto)
type: "unix"
# Unix-specific configuration
unix:
# Method for collecting process info (procfs, ps)
method: "procfs"
# Security Configuration
security:
# Optional: AES-256 key for encrypting cached SVIDs
cache_encryption_key: ""
# Path to SVID cache file
cache_path: "/var/lib/icp-agent/cache/svid.cache"
# Logging Configuration
logging:
# Log level (debug, info, warn, error)
level: "info"
# Log format (json, text)
format: "json"
# Log file path (empty = stdout only)
file_path: "/var/log/icp-agent/agent.log"
# Health Check Configuration
health:
# Enable HTTP health endpoint
enabled: true
# Health endpoint port
port: 8080
# Bind address (127.0.0.1 = localhost only)
bind_address: "127.0.0.1"
Environment Variable Overrides
You can override configuration using environment variables:
# Example: Override tenant ID
export ICP_AGENT_AGENT__TENANT_ID="your-tenant-id-here"
# Example: Override log level
export ICP_AGENT_LOGGING__LEVEL="debug"
# Example: Override ICP server URL
export ICP_AGENT_ICP_SERVICE__ADDRESS="https://dev.api.authsec.dev/spiresvc"
Workload Registration
Understanding Selectors
Before workloads can receive SVIDs, they must be registered with the ICP Server. Registration defines which processes should receive which SPIFFE IDs.
Common Selector Strategies
Strategy 1: User-Based (Recommended)
Best for applications running as dedicated system users:
# Create dedicated user for your application
sudo useradd -r -s /bin/false app-user
# Register workload for this user
export ICP_SERVER_URL="https://your-icp-server.example.com/spiresvc"
export TENANT_ID="your-tenant-id-here"
export NODE_ID="vm-prod-web-01"
curl -X POST "${ICP_SERVER_URL}/api/v1/workloads" \
-H "Content-Type: application/json" \
-d '{
"spiffe_id": "spiffe://your-spiffe-id",
"parent_id": "spiffe://your-parent-id",
"selectors": {
"unix:uid": "1000"
},
"ttl": 3600
}'
Strategy 2: UID/GID Combined
For stricter matching:
curl -X POST "${ICP_SERVER_URL}/api/v1/workloads" \
-H "Content-Type: application/json" \
-d '{
"spiffe_id": "spiffe://your-spiffe-id",
"parent_id": "spiffe://your-parent-id",
"selectors": {
"unix:uid": "1000",
"unix:gid": "1000"
},
"ttl": 3600
}'
Multi-Workload Scenarios
Example: Web Server + Database Client
# Register web server (runs as UID 33, typically www-data)
curl -X POST "${ICP_SERVER_URL}/api/v1/workloads" \
-H "Content-Type: application/json" \
-d '{
"spiffe_id": "spiffe://your-spiffe-id",
"parent_id": "spiffe://your-parent-id",
"selectors": {
"unix:uid": "33"
}
}'
# Register background worker (runs as UID 1001)
curl -X POST "${ICP_SERVER_URL}/api/v1/workloads" \
-H "Content-Type: application/json" \
-d '{
"spiffe_id": "spiffe://your-spiffe-id",
"parent_id": "spiffe://your-parent-id",
"selectors": {
"unix:uid": "1001"
}
}'
# Register database client (runs as UID 999, typically postgres)
curl -X POST "${ICP_SERVER_URL}/api/v1/workloads" \
-H "Content-Type: application/json" \
-d '{
"spiffe_id": "spiffe://your-spiffe-id",
"parent_id": "spiffe://your-parent-id",
"selectors": {
"unix:uid": "999"
}
}'
SDK Integration
Installation
Install the AuthSec SDK in your application:
# Install from GitHub
pip install git+https://github.com/authsec-ai/sdk-authsec.git
Quick Start Example
File: app.py
import asyncio
from authsec_sdk import QuickStartSVID
import httpx
async def main():
# Initialize SDK (automatic SVID fetching)
svid = await QuickStartSVID.initialize(
socket_path="/run/spire/sockets/agent.sock"
)
print(f"✅ Authenticated as: {svid.spiffe_id}")
print(f"📜 Certificate expires: {svid.expires_at}")
# Make mTLS request to another service
ssl_context = svid.create_ssl_context_for_client()
async with httpx.AsyncClient(verify=ssl_context) as client:
response = await client.post(
"https://api-service.example.com:8443/endpoint",
json={"data": "value"}
)
print(f"Response: {response.json()}")
if __name__ == "__main__":
asyncio.run(main())
Running Your Application
# Run as the user configured in workload registration
sudo -u app-user python3 app.py
Expected output:
✅ Authenticated as: spiffe://your-trust-domain.example.com/workload/web-app
📜 Certificate expires: 2025-12-08 11:00:00
Response: {'status': 'success'}
Server-Side mTLS (FastAPI Example)
File: server.py
from fastapi import FastAPI
import uvicorn
from authsec_sdk import QuickStartSVID
import asyncio
app = FastAPI()
# Global SVID instance
svid = None
@app.on_event("startup")
async def startup():
global svid
svid = await QuickStartSVID.initialize()
print(f"✅ Server authenticated as: {svid.spiffe_id}")
@app.post("/endpoint")
async def endpoint(data: dict):
return {"status": "success", "received": data}
if __name__ == "__main__":
# Note: uvicorn doesn't reload certs automatically
# For production, use a process manager that restarts on cert rotation
uvicorn.run(
app,
host="0.0.0.0",
port=8443,
ssl_certfile=svid.cert_file_path,
ssl_keyfile=svid.key_file_path,
ssl_ca_certs=svid.ca_file_path
)
Client-Side mTLS Example
from authsec_sdk import QuickStartSVID
import httpx
import asyncio
async def call_api():
# Initialize SVID
svid = await QuickStartSVID.initialize()
# Create fresh SSL context for each request (handles cert renewal)
ssl_context = svid.create_ssl_context_for_client()
async with httpx.AsyncClient(verify=ssl_context) as client:
response = await client.post(
"https://api-service.example.com:8443/endpoint",
json={"key": "value"}
)
return response.json()
# Call periodically
async def main():
while True:
result = await call_api()
print(f"API Response: {result}")
await asyncio.sleep(60)
if __name__ == "__main__":
asyncio.run(main())
Database mTLS Integration (PostgreSQL)
from authsec_sdk import QuickStartSVID
import asyncpg
import asyncio
async def connect_to_db():
# Get SVID
svid = await QuickStartSVID.initialize()
# Connect with mTLS
conn = await asyncpg.connect(
host='postgres.example.com',
port=5432,
database='mydb',
ssl=svid.create_ssl_context_for_client()
)
# Query
result = await conn.fetch('SELECT * FROM users LIMIT 10')
print(result)
await conn.close()
if __name__ == "__main__":
asyncio.run(connect_to_db())
PostgreSQL Configuration:
Edit /etc/postgresql/14/main/pg_hba.conf:
# Allow mTLS connections
hostssl all all 0.0.0.0/0 cert clientcert=verify-full
Edit /etc/postgresql/14/main/postgresql.conf:
ssl = on
ssl_cert_file = '/path/to/server.crt'
ssl_key_file = '/path/to/server.key'
ssl_ca_file = '/path/to/ca-bundle.pem'
Troubleshooting
Issue 1: Agent Won't Start
Symptoms:
sudo systemctl status icp-agent
● icp-agent.service - ICP Agent
Loaded: loaded
Active: failed
Solution:
# Check logs
sudo journalctl -u icp-agent -n 50
# Common fixes:
# 1. Check config file syntax
sudo python3 -c "import yaml; yaml.safe_load(open('/etc/icp-agent/config.yaml'))"
# 2. Check permissions
sudo chmod 600 /etc/icp-agent/config.yaml
sudo chmod 755 /run/spire/sockets
# 3. Check connectivity to ICP Server
curl -v https://your-icp-server.example.com/spiresvc/health
Issue 2: Workload Can't Get SVID
Symptoms:
Error: failed to fetch X509-SVID: rpc error: code = Unknown
Solution:
# 1. Check agent is running
sudo systemctl status icp-agent
# 2. Check socket exists and is accessible
ls -l /run/spire/sockets/agent.sock
# Should show: srwxrwxrwx
# 3. Check workload is registered
curl "${ICP_SERVER_URL}/api/v1/workloads?parent_id=spiffe://${TENANT_ID}/agent/${NODE_ID}"
# 4. Check selectors match
sudo -u app-user id
# Compare UID/GID with registered selectors
# 5. Enable debug logging
sudo sed -i 's/level: "info"/level: "debug"/' /etc/icp-agent/config.yaml
sudo systemctl restart icp-agent
sudo journalctl -u icp-agent -f
Issue 3: Certificate Expired
Symptoms:
Error: certificate has expired or is not yet valid
Solution:
# 1. Check system time
date
# 2. Sync time with NTP
sudo ntpdate pool.ntp.org
# 3. Restart agent to force renewal
sudo systemctl restart icp-agent
# 4. Check agent SVID expiry
sudo cat /var/lib/icp-agent/cache/svid.cache | jq .expires_at
Issue 4: Permission Denied
Symptoms:
Error: connect: permission denied
Solution:
# 1. Check socket permissions
ls -l /run/spire/sockets/agent.sock
# 2. Fix permissions
sudo chmod 777 /run/spire/sockets/agent.sock
# 3. Add user to socket group (if using group permissions)
sudo usermod -aG icp-agent app-user
Next Steps
- Multi-Cloud: See Kubernetes Integration Guide for hybrid deployments
Questions? Contact support@authsec.dev