Skip to content

Security Compliance Guide

Overview

Vulcan implements comprehensive security controls aligned with NIST SP 800-53 Revision 5 and the Application Security & Development STIG. This guide provides practical implementation details and configuration requirements for deploying Vulcan in compliance with federal security standards.

Quick Start Security Configuration

For organizations requiring immediate compliance, apply these essential settings:

bash
# Core Security Settings
export VULCAN_SESSION_TIMEOUT=10m              # 10 minutes (STIG requirement - default is 1h)
export VULCAN_WELCOME_TEXT="AUTHORIZED USE ONLY. By accessing this system, you agree to comply with all organizational security policies. All activities are monitored and logged."
export RAILS_FORCE_SSL=true                    # Force HTTPS
export RAILS_ENV=production                    # Production mode
export SECRET_KEY_BASE=$(rails secret)         # Generate secure key

# Authentication (Choose One)
# Option 1: OIDC/SAML
export VULCAN_ENABLE_OIDC=true
export VULCAN_OIDC_ISSUER_URL=https://your-idp.example.com
export VULCAN_OIDC_CLIENT_ID=vulcan
export VULCAN_OIDC_CLIENT_SECRET=<secure-secret>

# Option 2: LDAP
export VULCAN_ENABLE_LDAP=true
export VULCAN_LDAP_HOST=ldap.example.com
export VULCAN_LDAP_PORT=636
export VULCAN_LDAP_BASE="dc=example,dc=com"

Security Architecture

Defense in Depth Strategy

Control Implementation Details

🔐 Access Control (AC)

Account Management (AC-02)

What's Required: Automated account lifecycle management with audit trails

How Vulcan Implements It:

  • External Integration: Leverages your organization's existing identity provider (LDAP, OIDC, GitHub OAuth)
  • Local Account Management: Admin interface for manual account control when needed
  • Audit Trail: All account actions logged with timestamp and user ID

Your Action Items: ✅ Configure external authentication provider
✅ Disable local registration in production
✅ Document account provisioning procedures

Session Management (AC-12)

What's Required: Automatic session termination after inactivity

How Vulcan Implements It:

  • Configurable timeout via VULCAN_SESSION_TIMEOUT
  • Secure session cookies with Rails session management
  • Manual logout capability with session invalidation

Configuration:

bash
# STIG Requirements — accepts suffixes (10m, 900s) or plain numbers
VULCAN_SESSION_TIMEOUT=10m     # Required: 10 min for admin, 15 min for users
                               # Default: 1h (1 hour) if not set
                               # Note: Single timeout for all user types
                               # Formats: 30s, 15m, 1h, or plain (900 = seconds)

⚠️ Known Gaps:

  • Logout confirmation message (Issue #635) - In development

System Use Notification (AC-08)

What's Required: Display approved system use notification before granting access. Retain the notification until users acknowledge the usage conditions and take explicit action to log on.

Control Intent: AC-8 targets the logon interface — the authentication event, not every HTTP request. Per the NIST SP 800-53 Rev 5 discussion: "System use notifications can be implemented using messages or warning banners displayed before individuals log in to systems. System use notifications are used only for access via logon interfaces with human users." The obligation is satisfied once per authentication session. Page refreshes, navigation, and tab switching within an active session do not require re-display.

How Vulcan Implements It:

Vulcan provides three complementary mechanisms:

  1. Welcome Text — Customizable text on the login page via VULCAN_WELCOME_TEXT
  2. Classification Banner — Persistent top/bottom banner on every page showing system sensitivity level (e.g., UNCLASSIFIED, CUI). Configured via VULCAN_BANNER_ENABLED, VULCAN_BANNER_TEXT, VULCAN_BANNER_BACKGROUND_COLOR, VULCAN_BANNER_TEXT_COLOR. Server-rendered (works without JavaScript).
  3. Consent Modal — Blocks access until user acknowledges terms of use. Supports Markdown content. Acknowledgment is tracked server-side in the Rails session (not browser localStorage), tying consent to the authentication lifecycle per AC-8. Configured via VULCAN_CONSENT_ENABLED, VULCAN_CONSENT_VERSION, VULCAN_CONSENT_TITLE, VULCAN_CONSENT_CONTENT, VULCAN_CONSENT_TTL.

Consent Flow (AC-8 a/b):

  1. User navigates to login page → consent modal blocks all interaction
  2. User reads notice and clicks "I Agree" → POST /consent/acknowledge stores timestamp in Rails session (server-side)
  3. User submits login credentials → Devise authenticates and resets session (fixation protection) → Vulcan preserves consent timestamp across the reset
  4. User accesses app → consent_required? returns false → no modal
  5. On logout, session timeout, or browser close → session cleared → consent required again on next visit

Per-session acknowledgment is the standard government implementation pattern. Hard refreshes and page navigation within an active session do not re-trigger the modal because the Rails session (and the consent timestamp within it) persists across requests. The modal only re-appears when the session itself ends.

Configurable TTL

VULCAN_CONSENT_TTL=0 (default) means per-session — consent expires when the session ends. Organizations requiring less frequent prompting can set a duration (e.g., 24h, 8h, 30m). Per-session is the DoD-compliant default.

Configuration:

bash
# Classification Banner (visible on every page)
export VULCAN_BANNER_ENABLED=true
export VULCAN_BANNER_TEXT="UNCLASSIFIED"
export VULCAN_BANNER_BACKGROUND_COLOR="#007a33"
export VULCAN_BANNER_TEXT_COLOR="#ffffff"

# Consent Modal (must acknowledge before use)
export VULCAN_CONSENT_ENABLED=true
export VULCAN_CONSENT_VERSION=1
export VULCAN_CONSENT_TITLE="Terms of Use"
export VULCAN_CONSENT_CONTENT="By using this system you agree to the **acceptable use policy**."
export VULCAN_CONSENT_TTL=0    # 0 = per-session (DoD default)

# Welcome Text (login page)
export VULCAN_WELCOME_TEXT="AUTHORIZED USE ONLY. All activities are monitored."

See Configuration for full details including color codes for standard classification levels.

References:

📝 Audit & Accountability (AU)

What Gets Logged

Event TypeInformation CapturedRetention
AuthenticationUser ID, IP, Success/Failure, Timestamp90 days minimum
AuthorizationUser ID, Resource, Action, Decision90 days minimum
Data ChangesUser ID, Before/After Values, Timestamp1 year minimum
Admin ActionsUser ID, Action, Target, Result1 year minimum
System EventsService Start/Stop, Errors, Config Changes30 days minimum

Log Format

json
{
  "timestamp": "2024-10-11T14:30:00Z",
  "level": "INFO",
  "user_id": "user@example.com",
  "session_id": "abc123",
  "ip_address": "192.168.1.100",
  "method": "POST",
  "path": "/api/projects/123",
  "status": 200,
  "message": "Project updated successfully",
  "duration_ms": 145
}

Database Audit Configuration

Enable comprehensive database auditing with pgAudit:

sql
-- Install and configure pgAudit extension
CREATE EXTENSION IF NOT EXISTS pgaudit;

-- Set audit parameters
ALTER SYSTEM SET pgaudit.log = 'ALL';
ALTER SYSTEM SET pgaudit.log_catalog = off;
ALTER SYSTEM SET pgaudit.log_parameter = on;
ALTER SYSTEM SET pgaudit.log_statement_once = on;
ALTER SYSTEM SET pgaudit.log_relation = on;

-- Apply configuration
SELECT pg_reload_conf();

🔑 Identification & Authentication (IA)

Supported Authentication Methods

MethodMFA SupportPIV/CACSSORecommendation
OIDC/SAML✅ Yes✅ Yes✅ YesPreferred for enterprise
LDAP/AD⚠️ Via LDAP❌ No✅ YesGood for on-premise
GitHub OAuth✅ Yes❌ No✅ YesGood for development teams
Local Accounts❌ No❌ No❌ NoAdmin/emergency only

OIDC Configuration Example

yaml
# config/vulcan.yml
oidc:
  enabled: true
  issuer_url: https://login.example.com
  client_id: vulcan-prod
  client_secret: <%= ENV['OIDC_SECRET'] %>
  scope: "openid profile email"
  
  # Advanced Settings
  discovery: true                    # Auto-discover endpoints
  response_type: "code"              # Authorization code flow
  prompt: "select_account"           # Force account selection
  max_age: 3600                      # Force re-auth after 1 hour
  
  # Attribute Mapping
  uid_field: "preferred_username"
  email_field: "email"
  name_field: "name"

Password Complexity (IA-05)

What's Required: Enforce password complexity meeting organizational standards

How Vulcan Implements It:

  • Configurable count-based validator: minimum length, uppercase, lowercase, numbers, special characters
  • Default policy follows DoD "2222" standard: 15 chars, 2 of each character type
  • Real-time checklist in the UI shows compliance as user types
  • OmniAuth users (OIDC, LDAP, GitHub) skip complexity validation — their passwords are managed externally
  • All thresholds configurable via environment variables

Configuration:

bash
export VULCAN_PASSWORD_MIN_LENGTH=15       # Default: 15
export VULCAN_PASSWORD_MIN_UPPERCASE=2     # Default: 2
export VULCAN_PASSWORD_MIN_LOWERCASE=2     # Default: 2
export VULCAN_PASSWORD_MIN_NUMBER=2        # Default: 2
export VULCAN_PASSWORD_MIN_SPECIAL=2       # Default: 2

See Configure Password Policy for details.

Admin Account Protection (AC-06)

What's Required: Prevent unauthorized privilege escalation and ensure admin continuity

How Vulcan Implements It:

  • Last-admin protection: The system prevents demoting or deleting the only admin user, ensuring at least one administrator always exists
  • Admin bootstrap: Three methods to create the initial admin account (env vars, first-user-admin, rake task)
  • SMTP-aware password tools: When SMTP is unavailable, admins can generate reset links or set passwords directly — email-dependent forms show a "contact your admin" message instead of silently failing

🛡️ System & Communications Protection (SC)

TLS Configuration

Minimum Requirements:

  • TLS 1.2 or higher
  • Strong cipher suites only
  • Valid certificates from trusted CA
  • HSTS enabled with 1-year max-age

NGINX Configuration:

nginx
server {
    listen 443 ssl http2;
    server_name vulcan.example.com;
    
    # TLS Configuration
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers off;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;
    ssl_stapling on;
    ssl_stapling_verify on;
    
    # Security Headers
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
    add_header X-Frame-Options "DENY" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline';" always;
    
    # Rate Limiting
    limit_req zone=vulcan_api burst=20 nodelay;
    limit_req_status 429;
    
    location / {
        proxy_pass http://vulcan_backend;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Input Security Hardening (SI-10)

What's Required: Check validity of information inputs

How Vulcan Implements It:

ProtectionImplementationScope
XXE PreventionNONET flag on all XML parsers (Nokogiri + HappyMapper)XCCDF uploads
File Size Limitsbefore_action validation: 50 MB XML, 100 MB ZIP, 50 MB spreadsheetAll uploads
Content-Type ValidationFile extension whitelist per endpoint (.xml, .zip, .xlsx/.csv)All uploads
Rate Limitingrack-attack: 5 login attempts/min/IP, 10 uploads/min/IPLogin + uploads
Input Length LimitsActiveRecord validations on Project/Component metadata fieldsDatabase writes

Configuration: Rate limiting is enabled by default. Thresholds can be adjusted in config/initializers/rack_attack.rb.

Deployment Configurations

Production Deployment Checklist

Pre-Deployment

  • [ ] Security review completed
  • [ ] Penetration testing performed
  • [ ] STIG compliance validated
  • [ ] Backup procedures tested
  • [ ] Incident response plan documented

Application Configuration

  • [ ] Production environment variables set
  • [ ] TLS certificates installed and valid
  • [ ] Session timeout configured (≤10 minutes)
  • [ ] Welcome banner configured
  • [ ] External authentication enabled
  • [ ] Local registration disabled
  • [ ] Debug mode disabled
  • [ ] Error messages sanitized

Infrastructure Security

  • [ ] Network segmentation implemented
  • [ ] Firewall rules configured (allow only 443)
  • [ ] Database on separate network segment
  • [ ] Load balancer configured with TLS
  • [ ] DDoS protection enabled
  • [ ] Rate limiting configured
  • [ ] WAF rules applied

Monitoring & Logging

  • [ ] Centralized logging configured
  • [ ] Log retention policies set
  • [ ] SIEM integration tested
  • [ ] Alert rules configured
  • [ ] Audit log review process established

Container Security

dockerfile
# Secure Dockerfile Example
FROM ruby:3.4.9-slim AS production

# Security: Run as non-root user
RUN groupadd -r app && useradd -r -g app app

# Security: Install only required packages
RUN apt-get update && \
    apt-get install -y --no-install-recommends \
        postgresql-client \
        nodejs && \
    rm -rf /var/lib/apt/lists/*

# Security: Set secure permissions
WORKDIR /app
COPY --chown=app:app . .

# Security: No secrets in image
RUN bundle config set --local without 'development test' && \
    bundle install --jobs 4 --retry 3

# Security: Run as non-root
USER app

# Security: Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
    CMD curl -f http://localhost:3000/health || exit 1

# Security: Minimal exposure
EXPOSE 3000
CMD ["bundle", "exec", "puma", "-C", "config/puma.rb"]

Kubernetes Security

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: vulcan
spec:
  template:
    spec:
      # Security Context
      securityContext:
        runAsNonRoot: true
        runAsUser: 1000
        fsGroup: 1000
        
      containers:
      - name: vulcan
        image: mitre/vulcan:latest
        
        # Security Settings
        securityContext:
          allowPrivilegeEscalation: false
          readOnlyRootFilesystem: true
          capabilities:
            drop:
              - ALL
        
        # Resource Limits
        resources:
          limits:
            memory: "1Gi"
            cpu: "500m"
          requests:
            memory: "512Mi"
            cpu: "250m"
        
        # Liveness/Readiness
        livenessProbe:
          httpGet:
            path: /health
            port: 3000
          initialDelaySeconds: 30
          periodSeconds: 10
          
---
# Network Policy
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: vulcan-netpol
spec:
  podSelector:
    matchLabels:
      app: vulcan
  policyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: nginx
    ports:
    - port: 3000
  egress:
  - to:
    - podSelector:
        matchLabels:
          app: postgres
    ports:
    - port: 5432

Monitoring & Incident Response

Security Metrics Dashboard

Monitor these key security indicators:

MetricThresholdAlert LevelResponse
Failed login attempts>5 in 15 minHighLock account, investigate
Privilege escalation attemptsAnyCriticalImmediate investigation
Mass data export>1000 recordsMediumReview and validate
Configuration changesAnyLowLog and review daily
New admin accountsAnyHighVerify authorization
Unusual access patternsDeviation >3σMediumInvestigate anomaly

Incident Response Playbook

1. Detection & Analysis

bash
# Check recent authentication failures
grep "authentication_failed" /var/log/vulcan/production.log | tail -100

# Review admin actions
grep "admin_action" /var/log/vulcan/audit.log | tail -50

# Check for data exfiltration
grep "export\|download" /var/log/vulcan/access.log | awk '{print $1}' | sort | uniq -c

2. Containment

bash
# Disable compromised account
rails console
User.find_by(email: 'compromised@example.com').lock_access!

# Block IP address
iptables -A INPUT -s <malicious_ip> -j DROP

# Revoke all sessions
Rails.cache.clear

3. Eradication & Recovery

  • Reset affected passwords
  • Review and revoke API keys
  • Patch identified vulnerabilities
  • Restore from clean backups if needed

4. Post-Incident

  • Document timeline and actions
  • Update security controls
  • Conduct lessons learned session
  • Update incident response procedures

Compliance Validation

Automated Compliance Checks

ruby
# spec/compliance/nist_spec.rb
require 'rails_helper'

RSpec.describe "NIST SP 800-53 Compliance" do
  describe "AC-12: Session Termination" do
    it "enforces session timeout" do
      expect(Settings.session_timeout).to be <= 10.minutes
    end
    
    it "provides logout capability" do
      expect(page).to have_button("Log Out")
    end
  end
  
  describe "AU-03: Audit Content" do
    it "logs required event attributes" do
      log_entry = JSON.parse(File.read('/var/log/vulcan/audit.log').last)
      expect(log_entry).to include("timestamp", "user_id", "action", "result")
    end
  end
end

Manual Validation Checklist

Quarterly Reviews:

  • [ ] User access review
  • [ ] Privileged account audit
  • [ ] Log retention verification
  • [ ] Certificate expiration check
  • [ ] Security patch status

Annual Requirements:

  • [ ] Penetration testing
  • [ ] Security control assessment
  • [ ] Disaster recovery test
  • [ ] Security awareness training
  • [ ] Policy and procedure review

Known Limitations & Roadmap

Current Limitations

ControlGapWorkaroundTarget Resolution
AC-12(02)No logout confirmationCheck audit logsQ1 2025 (Issue #635)
AU-05No built-in log overflow handlingExternal log rotationUse log management system

Security Roadmap

Completed (v2.3.1):

  • ✅ OIDC auto-discovery

  • ✅ Enhanced audit logging

  • ✅ Container security hardening

  • ✅ Classification banner and consent modal

  • ✅ Password complexity policy (DoD 2222)

  • ✅ Admin user management with last-admin protection

  • ✅ SMTP-aware Devise views (no silent failures)

  • ✅ Deny-by-default authorization safety net

  • ✅ Account lockout (Devise :lockable, AC-07 compliance, admin unlock UI)

  • ✅ XXE prevention (NOENT→NONET in XML parsers, HappyMapper NONET patch)

  • ✅ Upload validation (file size limits + content-type checks on all endpoints)

  • ✅ Rate limiting (rack-attack: login throttling, upload throttling)

  • ✅ Input length limits on project and component metadata

  • ✅ Per-section rule field locking

  • ✅ Session limits per user (configurable max_active_sessions, session history tracking)

  • ✅ Logout confirmation (Devise flash notice via Toaster component)

  • ✅ Devise hardening (paranoid mode, DELETE logout, cookie security, change notifications)

  • ✅ PBKDF2-SHA512 password hashing (FIPS 140-2 compliant, transparent bcrypt migration)

Planned:

  • 📋 Built-in MFA for local accounts
  • 📋 Enhanced RBAC with custom roles

Configuration Verification & Cross-References

Source Code Validation

This table provides direct links to the Vulcan source code that implements each security control:

Control CategoryFeatureImplementation LocationStatus
Session ManagementSession Timeoutconfig/vulcan.default.yml:29
config/initializers/devise.rb:161
✅ Implemented
⚠️ Note: Defaults to 60 min, set to 10 min for compliance
System BannerWelcome Textconfig/vulcan.default.yml:11
app/views/devise/shared/_what_is_vulcan.html.haml:4
✅ Implemented
Audit LoggingUser Auditingapp/models/user.rb:8✅ Implemented
Audit LoggingComponent Auditingapp/models/component.rb:42✅ Implemented
OIDCAuto-Discoveryconfig/initializers/oidc_startup_validation.rb✅ Implemented
LDAPConfigurationconfig/vulcan.default.yml:35-44✅ Implemented
AuthorizationRBACapp/controllers/application_controller.rb:16-22✅ Implemented
Classification BannerSystem Sensitivityconfig/vulcan.default.yml
app/views/layouts/application.html.haml
✅ Implemented
Consent ModalTerms Acknowledgementapp/javascript/components/navbar/ConsentModal.vue✅ Implemented
Password ComplexityDoD 2222 Policyapp/models/concerns/password_complexity_validator.rb✅ Implemented
Last-Admin ProtectionPrevent Lockoutapp/controllers/users_controller.rb✅ Implemented
Admin User ManagementCreate/Edit/Deleteapp/controllers/users_controller.rb✅ Implemented
Account LockoutAC-07 Complianceapp/models/user.rb
config/initializers/devise.rb
✅ Implemented
Upload ValidationFile Size + Content-Typeapp/controllers/concerns/upload_validatable.rb✅ Implemented
Rate LimitingLogin + Upload Throttlingconfig/initializers/rack_attack.rb✅ Implemented
XXE PreventionXML Parser Hardeningapp/models/disa_rule_description.rb
config/initializers/nokogiri_security.rb
✅ Implemented
Section LockingPer-Field Rule Locksapp/controllers/rules_controller.rb✅ Implemented
Session LimitsPer-User Limits (AC-10)app/models/user.rb
config/initializers/devise.rb
✅ Implemented
Session HistoryLogin Audit Traildb/migrate/..._create_session_histories.rb✅ Implemented
Logout MessageConfirmation (AC-12)app/controllers/sessions_controller.rb✅ Implemented
Paranoid ModeAccount Enumeration Preventionconfig/initializers/devise.rb✅ Implemented
Cookie SecuritySecure + HttpOnly + SameSiteconfig/initializers/session_store.rb
config/initializers/devise.rb
✅ Implemented

Configuration Clarifications

Based on source code analysis, the following clarifications apply:

ConfigurationDocumentation StatesActual ImplementationAction Required
Session Timeout10 minutes requiredDefaults to 1 hour⚠️ Must set VULCAN_SESSION_TIMEOUT=10m (or 600)
Admin TimeoutSeparate timeoutUses same timeoutℹ️ No separate admin timeout available
CSRF ProtectionEnabledRails default (enabled)✅ No action needed
Strong ParametersRequiredRails default (enabled)✅ No action needed
Log RotationExternal requiredLogs to stdout⚠️ Must configure log management system

Implementation Roadmap

The following improvements are tracked as GitHub issues:

PriorityIssueDescriptionTarget
High#685Change default session timeout to 10 minutesv2.3.0
High#635Add logout confirmation messagev2.3.0
Medium#686Document CSRF protection explicitlyv2.3.0

These improvements will be addressed as part of the Vue 3 migration and Turbolinks removal work in v2.3.0.

Resources & Support

Documentation

Support Contacts

Compliance Artifacts

Available in /docs/compliance/:

  • Security Control Matrix (Excel)
  • POAM Template
  • Risk Assessment Template
  • Incident Response Plan Template
  • Configuration Baseline

Document Version: 2.3.1 Last Updated: February 2026 Classification: UNCLASSIFIED Distribution: Public Release

This document is maintained as part of the Vulcan project and updated with each security-relevant release.

Part of the MITRE Security Automation Framework (SAF)