Skip to content

VM & LXC Security Standard

Purpose

This document defines the baseline security, access model, directory layout, and secret delivery standard for all VM and LXC instances used for Appcircle workloads.

Design Principle

Identity verification is performed at the Guacamole and SSO layer. VMs use two shared accounts — sysadmin for infrastructure operations and appcircle for application operations. Access to each account is controlled through separate Guacamole connection groups with SSO-based group membership.

Attribution is maintained through Guacamole session logs and recordings, not through per-operator VM accounts.

User Model

Each VM/LXC must contain the following accounts.

Account Role Access path
root System default Privilege escalation only — no direct SSH login
sysadmin Shared administrative account, member of sudo Guacamole vm-<name>-admin connection group
appcircle Dual-role: application service identity and developer operational identity Guacamole vm-<name>-app connection group

Operational distinction:

  • Infrastructure operators use sysadmin for OS-level tasks
  • Developers and deployment operators use appcircle for application operations
  • Neither group needs to switch accounts during normal operations

Note

No per-operator accounts exist on the VM. Operators must not know the sysadmin or appcircle passwords directly. Both passwords are stored in OpenBao and injected into Guacamole through a controlled credential flow.

Core Security Rules

Root Login

  • Direct SSH login as root is forbidden
  • PermitRootLogin no must be set
  • Root access must only be obtained through sudo

SSH Hardening

Minimum required sshd_config settings:

PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
Port <custom-port>
MaxAuthTries 3
LoginGraceTime 30
ClientAliveInterval 300
ClientAliveCountMax 2
X11Forwarding no
AllowTcpForwarding no
PermitEmptyPasswords no
  • SSH must not listen on port 22
  • SSH access must be restricted to the Guacamole source IP or approved subnet
  • Preferred key type: Ed25519. RSA minimum: 4096-bit. DSA must not be used.

Network Exposure

  • Only required ports must be opened
  • Internal services must not be exposed publicly unless explicitly required
  • East-west access between systems should be minimized and documented

Guacamole Access Model

Two Guacamole connection groups must be defined per VM.

Connection group Target account Who uses it
vm-<name>-admin sysadmin Infrastructure and ops team
vm-<name>-app appcircle Developers and deployment operators

Access to each connection group is governed by SSO group membership.

Mandatory Safeguards

Session recording:

  • Every Guacamole session must be recorded
  • Recordings must be stored on central storage, not on the Guacamole host
  • Minimum retention: 90 days

Credential isolation:

  • Both passwords must be stored in OpenBao and injected into Guacamole
  • Operators must never see or retrieve either password directly
  • Passwords must be randomly generated, at least 32 characters, and rotated every 90 days

Network enforcement:

  • SSH must accept connections only from the Guacamole source IP or approved subnet
  • No direct SSH access from any other source

Directory Layout

/opt/appcircle/
  bin/          operational scripts
  config/       configuration templates and rendered secret files
  compose/      Docker Compose files
  releases/     versioned release content
  shared/
    logs/
    data/
    tmp/

/home/appcircle/
  .ssh/
  scripts/
  workspace/

/opt/appcircle is the authoritative application root. It must be owned by appcircle:appcircle.

/home/appcircle is the service user's working area for user-scoped helpers and temporary files.

Ownership and Permissions

/opt/appcircle               appcircle:appcircle  0755
/opt/appcircle/config        appcircle:appcircle  0750
/opt/appcircle/config/role-id      root:root      0600
/opt/appcircle/config/secret-id    root:root      0600
/opt/appcircle/config/*.env        root:root      0600  (rendered secret files)
/opt/appcircle/config/*.env.tpl    root:root      0644  (templates, no secret values)

Rendered secret files are owned by root so that neither sysadmin nor appcircle can read them directly. Only the processes running as root (OpenBao Agent, Docker Compose stack) access these files.

Secret Delivery

Secrets must be delivered through OpenBao Agent.

  • OpenBao Agent runs as a systemd service
  • Agent authenticates using AppRole (role-id + secret-id)
  • Agent renders secrets to files via templates
  • The application reads rendered files and never contacts OpenBao directly
  • Token TTL: 1 hour, auto-renewed by the agent

File permission model

/opt/appcircle/config/role-id     root:root  0600
/opt/appcircle/config/secret-id   root:root  0600
/opt/appcircle/config/app.env     root:root  0600  (rendered output)
/opt/appcircle/config/app.env.tpl root:root  0644  (template)

See OpenBao for agent configuration details.

Native Application Service Users

For natively installed applications (nginx, PostgreSQL, OpenBao, etc.), use the upstream-defined service user — do not substitute appcircle.

Application Expected service user
nginx www-data (Debian/Ubuntu) or nginx (RHEL)
PostgreSQL postgres
OpenBao openbao
Redis redis

appcircle retains its operational role (deployment scripts, configuration management) but must not be set as the runtime service user for third-party applications.

Secret delivery for native applications

When the application's service user differs from appcircle, choose one of:

Option Approach
Group access (preferred) Add the service user to the appcircle group; set rendered file to 0640 appcircle:appcircle
Dedicated output path Render directly to a path owned by the service user
systemd EnvironmentFile Render an env file; reference it in the unit with EnvironmentFile=

The chosen option must be documented in the deployment runbook.

Operational Tooling for the appcircle User

Makefile

A Makefile must be placed at /opt/appcircle/bin/Makefile. It serves as the single entry point for all operations the appcircle user performs on a VM. When a developer logs in via Guacamole, they run make help to see what actions are available — without needing to know the underlying systemctl commands or service names.

cd /opt/appcircle/bin
make help

The Makefile wraps sudo systemctl calls and journalctl log commands into named targets. This keeps the operational surface predictable and auditable.

Permissions:

/opt/appcircle/bin/Makefile   appcircle:appcircle  0644

Scoped sudo via sudoers

The appcircle user must not have unrestricted sudo access. Service management permissions are granted per-service through a dedicated sudoers file under /etc/sudoers.d/.

Example for a Guacamole host (/etc/sudoers.d/appcircle-guacamole):

appcircle ALL=(root) NOPASSWD: /usr/bin/systemctl start guacamole-stack.service
appcircle ALL=(root) NOPASSWD: /usr/bin/systemctl stop guacamole-stack.service
appcircle ALL=(root) NOPASSWD: /usr/bin/systemctl restart guacamole-stack.service
appcircle ALL=(root) NOPASSWD: /usr/bin/systemctl status guacamole-stack.service

Rules:

  • Only the specific service(s) relevant to the application are listed
  • openbao-agent.service and other infrastructure services must not be included — appcircle must not be able to stop the secret agent
  • The sudoers file name must reflect the host or application context (e.g., appcircle-guacamole, appcircle-nginx)

Deployment flow for the appcircle user

Developer authenticates via SSO
    ↓
Opens vm-<name>-app connection in Guacamole
    ↓
Logged in as appcircle on the VM
    ↓
cd /opt/appcircle/bin && make help
    ↓
Runs make start / make restart / make logs
    ↓
sudo systemctl ... executed with scoped NOPASSWD permission

Docker Standard

If Docker is used:

  • appcircle is not in the docker group by default (Model A — controlled via sudo wrappers)
  • Containers must run as non-root wherever possible
  • Compose files: /opt/appcircle/compose
  • Environment files: /opt/appcircle/config
  • Volume data: /opt/appcircle/shared

Onboarding and Offboarding

Onboarding:

  1. Grant SSO/IdP access
  2. Assign to the correct Guacamole connection group based on role
  3. No VM-level user creation required

Offboarding:

  1. Disable the IdP account to immediately revoke Guacamole access
  2. Rotate sysadmin and appcircle passwords if there is any exposure risk
  3. No VM-level user deletion required

Audit and Logging

Minimum events to log:

  • SSH login attempts (successful and failed)
  • sudo usage
  • Service start, stop, restart, and failure events
  • OpenBao Agent authentication and render events
  • Changes under /opt/appcircle/config and /opt/appcircle/compose

Recommended tools: auditd for file and privilege auditing, tlog for interactive session recording, rsyslog for central log forwarding.

Minimum Baseline Checklist

  • Root SSH login disabled
  • SSH port changed from 22
  • SSH firewall allows only Guacamole source IP
  • sysadmin account created and in sudo
  • appcircle account created
  • /opt/appcircle created with correct ownership
  • Session recording enabled for both Guacamole connection groups
  • sysadmin password stored in OpenBao and injected via Guacamole
  • appcircle password stored in OpenBao and injected via Guacamole
  • SSO group membership controls access to each connection group
  • Firewall configured with minimum required rules
  • OpenBao Agent configured and running
  • Deployment procedure documented