16 KiB
16 KiB
hiveops-remote Implementation Plan
Overview
Create hiveops-remote at /source/hiveops-src/hiveops-remote/ with:
- hiveops-remote-server - Java/Spring Boot control plane + module JAR hosting
- hiveops-remote-module - Java module JAR implementing AgentModule SPI
The module JAR is downloaded from the server when the remote feature is activated, then loaded dynamically into hiveops-agent.
Requirements
- Screen Capture: Static on-demand screenshots
- File System: Browse directories + download files
- License Tiers: Pro and Enterprise only
- Distribution: Module JAR downloaded from server on activation
- Location:
/source/hiveops-src/hiveops-remote/
Architecture
┌─────────────────────────────────────────────────────────────────┐
│ hiveops-remote │
├─────────────────────────────┬───────────────────────────────────┤
│ hiveops-remote-server │ hiveops-remote-module │
│ (Spring Boot) │ (AgentModule JAR) │
│ │ │
│ - Control Plane API │ Downloaded & loaded into │
│ - Command Queue │ hiveops-agent at runtime │
│ - Module JAR Hosting │ │
│ - Binary Result Storage │ - Screen Capture │
│ │ - File Browser │
│ PostgreSQL │ - File Download │
│ │ - Result Upload │
└─────────────────────────────┴───────────────────────────────────┘
Flow:
1. Agent checks for remote module availability
2. Downloads hiveops-remote-module.jar from server
3. ModuleLoader loads JAR via SPI
4. Module starts polling server for commands
Project Structure (Maven Multi-Module)
/source/hiveops-src/hiveops-remote/
├── pom.xml # Parent POM
├── hiveops-remote-server/ # Spring Boot server
│ ├── pom.xml
│ └── src/main/java/com/hiveops/remote/server/
│ ├── RemoteServerApplication.java
│ ├── config/
│ │ └── SecurityConfig.java
│ ├── controller/
│ │ ├── AgentController.java # Agent endpoints (poll, upload)
│ │ ├── RemoteController.java # User endpoints (commands)
│ │ └── ModuleController.java # Module JAR download
│ ├── service/
│ │ ├── AgentService.java
│ │ ├── CommandService.java
│ │ └── BinaryService.java
│ ├── repository/
│ │ ├── AgentRepository.java
│ │ ├── CommandRepository.java
│ │ └── BinaryChunkRepository.java
│ ├── entity/
│ │ ├── RemoteAgent.java
│ │ ├── RemoteCommand.java
│ │ └── BinaryChunk.java
│ └── dto/
│ ├── AgentRegistrationRequest.java
│ ├── CommandRequest.java
│ └── ...
├── hiveops-remote-module/ # Agent module JAR
│ ├── pom.xml
│ └── src/main/java/com/hiveops/remote/module/
│ ├── RemoteModule.java # AgentModule SPI implementation
│ ├── RemoteCommandProcessor.java # Command polling & dispatch
│ ├── RemoteHttpClient.java # Server communication
│ ├── screen/
│ │ ├── ScreenCaptureService.java
│ │ ├── LinuxScreenCapture.java # X11/Wayland capture
│ │ └── WindowsScreenCapture.java # Robot/GDI capture
│ ├── filesystem/
│ │ ├── FileSystemService.java
│ │ ├── DirectoryLister.java
│ │ ├── FileDownloader.java
│ │ └── PathValidator.java # Security
│ └── dto/
│ ├── RemoteCommand.java
│ ├── ScreenshotParams.java
│ └── FileListParams.java
├── hiveops-remote-common/ # Shared DTOs between server & module
│ ├── pom.xml
│ └── src/main/java/com/hiveops/remote/common/
│ ├── dto/
│ │ ├── CommandType.java
│ │ ├── CommandStatus.java
│ │ ├── PollResponse.java
│ │ └── FileEntry.java
│ └── security/
│ └── PathSecurityUtils.java
└── deployments/
├── docker/
│ └── Dockerfile
└── docker-compose.yml
Module Loading Flow
1. Check for Remote Module (in hiveops-agent)
The existing hiveops-agent can be enhanced to check for downloadable modules:
// In AgentApplication or a new ModuleDownloader
public void checkRemoteModules() {
String moduleUrl = config.get("remote.module.url");
if (moduleUrl != null && remoteEnabled) {
Path modulePath = downloadModule(moduleUrl, "hiveops-remote-module.jar");
moduleLoader.loadFromPath(modulePath);
}
}
2. Module SPI Registration
File: hiveops-remote-module/src/main/resources/META-INF/services/com.hiveops.core.module.AgentModule
com.hiveops.remote.module.RemoteModule
3. RemoteModule Implementation
public class RemoteModule implements AgentModule {
private RemoteCommandProcessor processor;
@Override
public String getName() {
return "hiveops-remote";
}
@Override
public void initialize(ModuleContext context) {
String serverUrl = context.getConfig("remote.server.url");
String agentToken = context.getConfig("remote.agent.token");
RemoteHttpClient client = new RemoteHttpClient(serverUrl, agentToken);
processor = new RemoteCommandProcessor(client, context);
}
@Override
public void start() {
processor.startPolling();
}
@Override
public void stop() {
processor.stopPolling();
}
}
Database Schema (PostgreSQL)
-- In hiveops-remote-server
CREATE TABLE remote_agents (
id BIGSERIAL PRIMARY KEY,
agent_id UUID UNIQUE NOT NULL DEFAULT gen_random_uuid(),
name VARCHAR(255) NOT NULL,
hostname VARCHAR(255),
platform VARCHAR(50) NOT NULL,
agent_version VARCHAR(50),
module_version VARCHAR(50),
status VARCHAR(50) NOT NULL DEFAULT 'OFFLINE',
last_heartbeat_at TIMESTAMPTZ,
capabilities JSONB,
license_key VARCHAR(255),
machine_id VARCHAR(255),
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(license_key, machine_id)
);
CREATE TABLE remote_commands (
id BIGSERIAL PRIMARY KEY,
command_id UUID UNIQUE NOT NULL DEFAULT gen_random_uuid(),
agent_id BIGINT NOT NULL REFERENCES remote_agents(id) ON DELETE CASCADE,
type VARCHAR(50) NOT NULL,
status VARCHAR(50) NOT NULL DEFAULT 'PENDING',
parameters JSONB,
result JSONB,
error_message TEXT,
created_at TIMESTAMPTZ DEFAULT NOW(),
started_at TIMESTAMPTZ,
completed_at TIMESTAMPTZ
);
CREATE TABLE binary_chunks (
id BIGSERIAL PRIMARY KEY,
command_id BIGINT NOT NULL REFERENCES remote_commands(id) ON DELETE CASCADE,
chunk_index INT NOT NULL,
total_chunks INT NOT NULL,
data BYTEA NOT NULL,
checksum VARCHAR(64),
UNIQUE(command_id, chunk_index)
);
-- Module JAR versions
CREATE TABLE module_versions (
id BIGSERIAL PRIMARY KEY,
version VARCHAR(50) NOT NULL UNIQUE,
filename VARCHAR(255) NOT NULL,
checksum VARCHAR(64) NOT NULL,
size_bytes BIGINT NOT NULL,
released_at TIMESTAMPTZ DEFAULT NOW(),
is_latest BOOLEAN DEFAULT FALSE
);
API Endpoints
Module Distribution
GET /api/v1/modules/latest - Get latest module version info
GET /api/v1/modules/{version}/download - Download module JAR
Agent-Facing (for hiveops-remote-module)
POST /api/v1/agents/register - Register agent with module
GET /api/v1/agents/poll - Poll for pending commands (long-poll 15s)
POST /api/v1/agents/heartbeat - Heartbeat update
POST /api/v1/commands/{id}/status - Update command status
POST /api/v1/commands/{id}/upload - Upload binary result (chunked)
User-Facing
GET /api/v1/agents - List registered agents
GET /api/v1/agents/{id} - Get agent details
DELETE /api/v1/agents/{id} - Unregister agent
POST /api/v1/commands/screenshot - Request screenshot
POST /api/v1/commands/file-list - List directory
POST /api/v1/commands/file-download - Download file
GET /api/v1/commands/{id} - Get command status
GET /api/v1/commands/{id}/result - Download binary result
Screen Capture Implementation
Linux (X11)
public class LinuxScreenCapture {
public byte[] capture(ScreenshotParams params) throws Exception {
// Option 1: Use Robot (requires X11 display)
Robot robot = new Robot();
Rectangle screenRect = new Rectangle(Toolkit.getDefaultToolkit().getScreenSize());
BufferedImage image = robot.createScreenCapture(screenRect);
// Encode as JPEG
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(image, "jpg", baos);
return baos.toByteArray();
}
}
Windows
public class WindowsScreenCapture {
public byte[] capture(ScreenshotParams params) throws Exception {
// Same Robot API works on Windows
Robot robot = new Robot();
Rectangle screenRect = new Rectangle(Toolkit.getDefaultToolkit().getScreenSize());
BufferedImage image = robot.createScreenCapture(screenRect);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(image, "jpg", baos);
return baos.toByteArray();
}
}
Note: Java's Robot class works cross-platform for basic screen capture. For headless Linux, may need Xvfb or alternative approach.
File System Service
public class FileSystemService {
private final PathValidator pathValidator;
public List<FileEntry> listDirectory(String path) {
pathValidator.validate(path); // Security check
Path dir = Paths.get(path);
return Files.list(dir)
.map(p -> new FileEntry(
p.getFileName().toString(),
Files.isDirectory(p) ? "DIRECTORY" : "FILE",
Files.size(p),
Files.getLastModifiedTime(p).toInstant(),
getPosixPermissions(p)
))
.collect(Collectors.toList());
}
public InputStream downloadFile(String path) {
pathValidator.validate(path);
pathValidator.checkMaxSize(path);
return Files.newInputStream(Paths.get(path));
}
}
Security
- Agent Auth: License key + machine ID → JWT token (validated against hiveops-mgmt or standalone)
- Path Security: Configurable allowed paths, block
..traversal - File Size Limit: Configurable max (default 100MB)
- Module Signature: JAR checksum verification on download
Maven Configuration
Parent pom.xml:
<groupId>com.hiveops</groupId>
<artifactId>hiveops-remote</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>hiveops-remote-common</module>
<module>hiveops-remote-module</module>
<module>hiveops-remote-server</module>
</modules>
<properties>
<java.version>21</java.version>
<spring-boot.version>3.4.1</spring-boot.version>
</properties>
hiveops-remote-module pom.xml:
<dependencies>
<dependency>
<groupId>com.hiveops</groupId>
<artifactId>hiveops-core</artifactId>
<version>3.0.1-SNAPSHOT</version>
<scope>provided</scope> <!-- Provided by hiveops-agent -->
</dependency>
<dependency>
<groupId>com.hiveops</groupId>
<artifactId>hiveops-remote-common</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<configuration>
<!-- Bundle only remote-common, exclude hiveops-core -->
</configuration>
</plugin>
</plugins>
</build>
Docker Deployment
docker-compose.yml:
version: '3.8'
services:
hiveops-remote-server:
build: .
ports:
- "8090:8090"
environment:
- DB_HOST=postgres
- DB_NAME=hiveops_remote
volumes:
- ./modules:/app/modules # Module JARs served from here
depends_on:
- postgres
postgres:
image: postgres:16-alpine
environment:
- POSTGRES_DB=hiveops_remote
- POSTGRES_USER=hiveops
- POSTGRES_PASSWORD=hiveops
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
Implementation Order
Phase 1: Project Setup
- Create Maven multi-module project at
/source/hiveops-src/hiveops-remote/ - Setup parent pom.xml with modules
- Create hiveops-remote-common with shared DTOs
Phase 2: Server (hiveops-remote-server)
- Create Spring Boot application
- Create entities and repositories
- Create AgentController (register, poll, upload)
- Create RemoteController (user commands)
- Create ModuleController (JAR download)
- Database migrations
Phase 3: Module (hiveops-remote-module)
- Create RemoteModule implementing AgentModule SPI
- Create RemoteCommandProcessor (polling loop)
- Implement ScreenCaptureService
- Implement FileSystemService + PathValidator
- SPI registration in META-INF/services
Phase 4: Agent Integration
- Add module download capability to hiveops-agent
- Test dynamic module loading
- Configuration for remote module URL
Phase 5: Docker & Testing
- Create Dockerfile
- Create docker-compose.yml
- End-to-end testing
Configuration
hiveops-agent hiveops.properties (to enable remote module):
# Remote module configuration
remote.enabled=true
remote.server.url=http://localhost:8090
remote.module.download.url=http://localhost:8090/api/v1/modules/latest/download
remote.license.key=${LICENSE_KEY}
remote.machine.id=${MACHINE_ID}
hiveops-remote-server application.yml:
server:
port: 8090
spring:
datasource:
url: jdbc:postgresql://${DB_HOST:localhost}:5432/${DB_NAME:hiveops_remote}
username: ${DB_USER:hiveops}
password: ${DB_PASSWORD:hiveops}
remote:
poll-timeout-ms: 15000
command-timeout-ms: 300000
module-storage-path: /app/modules
Verification
- Build:
mvn clean packagein hiveops-remote - Start Server:
docker-compose up -d - Test Module Download:
curl http://localhost:8090/api/v1/modules/latest curl -O http://localhost:8090/api/v1/modules/1.0.0/download - Integration Test:
- Start hiveops-agent with remote.enabled=true
- Verify module downloads and loads
- Request screenshot via API
- Verify result returned