v0.1.0-draft AI Drafted

JumpCloud Hardening Guide

Identity Last updated: 2025-02-05

Cloud directory and identity management hardening for JumpCloud SSO, MFA, and device management

Overview

JumpCloud is a cloud-based directory platform providing identity management, SSO, MFA, and device management for over 200,000 organizations. As a unified directory replacing traditional Active Directory, JumpCloud security configurations directly impact access control across all integrated resources including systems, applications, and networks.

Intended Audience

  • Security engineers managing JumpCloud deployments
  • IT administrators configuring directory policies
  • GRC professionals assessing identity controls
  • Third-party risk managers evaluating directory services

How to Use This Guide

  • L1 (Baseline): Essential controls for all organizations
  • L2 (Hardened): Enhanced controls for security-sensitive environments
  • L3 (Maximum Security): Strictest controls for regulated industries

Scope

This guide covers JumpCloud Admin Portal security, MFA policies, conditional access, device management, and system policies.


Table of Contents

  1. Admin Account Security
  2. Multi-Factor Authentication
  3. Conditional Access
  4. Device & System Management
  5. Monitoring & Detection
  6. Compliance Quick Reference

1. Admin Account Security

1.1 Secure Admin Portal Access

Profile Level: L1 (Baseline)

Framework Control
CIS Controls 5.4
NIST 800-53 AC-6(1)

Description

Secure JumpCloud Admin Portal access with MFA and role-based access controls. Admin accounts with unrestricted access are high-value targets.

Rationale

Why This Matters:

  • Admin Portal controls all identity and access settings
  • Compromised admin can disable security controls
  • MFA for admins is critical but often overlooked

ClickOps Implementation

Step 1: Enable Admin MFA

  1. Navigate to: JumpCloud Admin PortalSecurityMFA for Admins
  2. Enable Require MFA for Admin Portal
  3. Configure allowed MFA methods:
    • TOTP Authenticator: Recommended
    • WebAuthn: Highly recommended
    • JumpCloud Go: Recommended

Step 2: Configure Admin Roles

  1. Navigate to: SettingsAdmin Roles
  2. Review default roles:
    • Administrator: Full access (limit to 2-3)
    • Manager: User and group management
    • Help Desk: Password reset, limited user view
    • Read Only: View-only access
  3. Create custom roles for specific functions
  4. Assign minimum required permissions

Step 3: Audit Admin Accounts

  1. Navigate to: Admins
  2. Review all administrator accounts
  3. Remove unnecessary admin access
  4. Verify all admins have MFA enrolled

Time to Complete: ~30 minutes


Code Pack: API Script
hth-jumpcloud-1.01-secure-admin-portal-access.sh View source on GitHub ↗
# Require MFA for all admin portal logins
ORG_ID=$(jc_get_v1 "/organizations" | jq -r '.[0].id // empty')
if [ -z "${ORG_ID}" ]; then
  fail "1.1 Unable to determine organization ID"
  increment_failed; summary; exit 0
fi

CURRENT=$(jc_get_v1 "/organizations/${ORG_ID}" | jq -r '.settings.requireAdminMFA')
info "1.1 Current admin MFA requirement: ${CURRENT}"

if [ "${CURRENT}" = "true" ]; then
  pass "1.1 Admin MFA already required"
  increment_applied
else
  info "1.1 Enabling admin MFA requirement..."
  RESPONSE=$(jc_put_v1 "/organizations/${ORG_ID}" '{
    "settings": {
      "requireAdminMFA": true
    }
  }') || {
    fail "1.1 Failed to enable admin MFA"
    increment_failed; summary; exit 0
  }
  RESULT=$(echo "${RESPONSE}" | jq -r '.settings.requireAdminMFA')
  [ "${RESULT}" = "true" ] && { pass "1.1 Admin MFA requirement enabled"; increment_applied; } || { fail "1.1 Admin MFA not confirmed"; increment_failed; }
fi
Code Pack: Sigma Detection Rule
hth-jumpcloud-1.01-secure-admin-portal-access.yml View source on GitHub ↗
detection:
    selection:
        event_type: 'admin_login_attempt'
    filter_failure:
        success: false
    condition: selection and filter_failure
fields:
    - timestamp
    - event_type
    - initiated_by.email
    - client_ip
    - success
    - mfa_meta.type

1.2 Implement Least Privilege Administration

Profile Level: L1 (Baseline)

Framework Control
CIS Controls 5.4
NIST 800-53 AC-6(1)

Description

Implement tiered administration following the principle of least privilege.

Implementation

Tier 0 (Critical):

  • Full Administrator role
  • Limit to 2-3 trusted admins
  • Require strongest MFA (WebAuthn/hardware keys)

Tier 1 (Standard Admin):

  • Manager role for user/group management
  • Day-to-day administration tasks

Tier 2 (Support):

  • Help Desk role for password resets
  • Read Only for auditors

2. Multi-Factor Authentication

2.1 Enforce Organization-Wide MFA

Profile Level: L1 (Baseline)

Framework Control
CIS Controls 6.5
NIST 800-53 IA-2(1)

Description

Require MFA for all user authentication to protected resources including the User Portal, applications, and systems.

Rationale

Why This Matters:

  • MFA blocks 99.9% of automated attacks
  • JumpCloud supports multiple MFA methods
  • Organization-wide enforcement prevents gaps

ClickOps Implementation

Step 1: Enable User MFA

  1. Navigate to: SecurityMFA
  2. Enable Require MFA for User Portal
  3. Configure enforcement:
    • All Users: Recommended for most organizations
    • User Groups: For phased rollout

Step 2: Configure Allowed Methods

  1. Select allowed MFA methods:
    • TOTP: Enabled (Google Authenticator, Authy)
    • WebAuthn (Security Keys): Enabled
    • WebAuthn (Platform): Enabled (Touch ID, Windows Hello)
    • JumpCloud Go: Enabled (recommended)
    • SMS/Voice: Disable if possible (less secure)
  2. Set Default MFA Method preference

Step 3: Enable JumpCloud Go

  1. Navigate to: SecurityJumpCloud Go
  2. Enable JumpCloud Go for passwordless authentication
  3. This uses device authenticators with biometrics

Time to Complete: ~20 minutes


Code Pack: API Script
hth-jumpcloud-2.01-enforce-organization-wide-mfa.sh View source on GitHub ↗
# Create a conditional access policy requiring MFA for all user portal logins
EXISTING=$(jc_get_v2 "/authn/policies" | jq -r '.[] | select(.name == "HTH: Require MFA - All Users") | .id') || true

if [ -n "${EXISTING}" ]; then
  info "2.1 MFA policy already exists (id: ${EXISTING})"
  pass "2.1 User portal MFA policy configured"
  increment_applied
else
  info "2.1 Creating MFA enforcement policy..."
  RESPONSE=$(jc_post_v2 "/authn/policies" '{
    "name": "HTH: Require MFA - All Users",
    "type": "user_portal",
    "disabled": false,
    "effect": {
      "action": "allow",
      "obligations": {
        "mfa": { "required": true },
        "mfaFactors": ["TOTP", "WEBAUTHN", "PUSH"],
        "userVerification": { "requirement": "required" }
      }
    },
    "targets": {
      "resources": [{ "type": "user_portal" }]
    }
  }') || {
    fail "2.1 Failed to create MFA policy"
    increment_failed; summary; exit 0
  }
  POLICY_ID=$(echo "${RESPONSE}" | jq -r '.id')
  pass "2.1 MFA policy created (id: ${POLICY_ID})"
  increment_applied
fi

# Audit users without MFA enrolled
info "2.1 Auditing user MFA enrollment..."
USERS=$(jc_get_v1 "/systemusers?limit=100")
NO_MFA=$(echo "${USERS}" | jq '[.results[] | select(.totp_enabled == false and .enable_user_portal_multifactor == false)] | length')
TOTAL=$(echo "${USERS}" | jq '.totalCount')
info "2.1 Users without MFA: ${NO_MFA} / ${TOTAL}"

if [ "${NO_MFA}" -gt 0 ]; then
  warn "2.1 Users without MFA enrollment:"
  echo "${USERS}" | jq -r '.results[] | select(.totp_enabled == false and .enable_user_portal_multifactor == false) | "  - \(.email)"'
fi
Code Pack: Sigma Detection Rule
hth-jumpcloud-2.01-enforce-organization-wide-mfa.yml View source on GitHub ↗
detection:
    selection:
        event_type: 'organization_update'
    condition: selection
fields:
    - timestamp
    - event_type
    - initiated_by.email
    - client_ip
    - changes

2.2 Configure MFA for System Access

Profile Level: L2 (Hardened)

Framework Control
CIS Controls 6.5
NIST 800-53 IA-2(1)

Description

Require MFA for system login (Windows, macOS, Linux) and SSH access.

ClickOps Implementation

Step 1: Enable MFA for Systems

  1. Navigate to: SecurityMFA
  2. Enable Require MFA for System Login
  3. Configure per-OS settings:
    • Windows: Enable MFA at Windows logon
    • macOS: Enable MFA at macOS login
    • Linux: Enable MFA for SSH

Step 2: Configure SSH MFA

  1. For Linux systems, configure JumpCloud agent
  2. Enable MFA Required for SSH connections
  3. Users will need to complete MFA after password

3. Conditional Access

3.1 Configure Conditional Access Policies

Profile Level: L2 (Hardened)

Framework Control
CIS Controls 6.4
NIST 800-53 AC-2(11)

Description

Configure conditional access policies to enforce context-aware security controls based on location, device, and risk signals.

Rationale

Why This Matters:

  • Context-aware access enables Zero Trust
  • Block access from risky locations or devices
  • Dynamically adjust MFA requirements

ClickOps Implementation

Step 1: Create Conditional Access Policy

  1. Navigate to: SecurityConditional Access
  2. Click Create New Policy
  3. Configure policy conditions:
    • Location: Define trusted/untrusted locations
    • Device Trust: Require managed devices
    • User Groups: Apply to specific groups

Step 2: Define Policy Actions

  1. Configure actions based on conditions:
    • Allow access: From trusted locations
    • Require MFA: From unknown locations
    • Block access: From blocked countries
  2. Set policy priority
Code Pack: API Script
hth-jumpcloud-3.01-configure-conditional-access-policies.sh View source on GitHub ↗
# Create IP list for trusted corporate networks
EXISTING_LIST=$(jc_get_v2 "/iplists" | jq -r '.[] | select(.name == "HTH: Corporate IPs") | .id') || true

if [ -z "${EXISTING_LIST}" ]; then
  info "3.1 Creating corporate IP list..."
  IP_LIST=$(jc_post_v2 "/iplists" '{
    "name": "HTH: Corporate IPs",
    "description": "Trusted corporate network ranges",
    "ips": ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"]
  }') || {
    fail "3.1 Failed to create IP list"
    increment_failed; summary; exit 0
  }
  EXISTING_LIST=$(echo "${IP_LIST}" | jq -r '.id')
  pass "3.1 Corporate IP list created (id: ${EXISTING_LIST})"
fi

# Create policy: require MFA from non-corporate networks
EXISTING_POLICY=$(jc_get_v2 "/authn/policies" | jq -r '.[] | select(.name == "HTH: MFA Outside Corporate") | .id') || true

if [ -n "${EXISTING_POLICY}" ]; then
  info "3.1 Conditional access policy already exists (id: ${EXISTING_POLICY})"
else
  info "3.1 Creating conditional access policy..."
  RESPONSE=$(jc_post_v2 "/authn/policies" "$(jq -n --arg list_id "${EXISTING_LIST}" '{
    "name": "HTH: MFA Outside Corporate",
    "type": "user_portal",
    "disabled": false,
    "conditions": {
      "not": {
        "ipAddressIn": [$list_id]
      }
    },
    "effect": {
      "action": "allow",
      "obligations": {
        "mfa": { "required": true },
        "mfaFactors": ["TOTP", "WEBAUTHN"],
        "userVerification": { "requirement": "required" }
      }
    },
    "targets": {
      "resources": [{ "type": "user_portal" }]
    }
  }')") || {
    fail "3.1 Failed to create conditional access policy"
    increment_failed; summary; exit 0
  }
  pass "3.1 Conditional access policy created"
fi

increment_applied

Time to Complete: ~45 minutes


Code Pack: API Script
hth-jumpcloud-3.01-configure-conditional-access-policies.sh View source on GitHub ↗
# Create IP list for trusted corporate networks
EXISTING_LIST=$(jc_get_v2 "/iplists" | jq -r '.[] | select(.name == "HTH: Corporate IPs") | .id') || true

if [ -z "${EXISTING_LIST}" ]; then
  info "3.1 Creating corporate IP list..."
  IP_LIST=$(jc_post_v2 "/iplists" '{
    "name": "HTH: Corporate IPs",
    "description": "Trusted corporate network ranges",
    "ips": ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"]
  }') || {
    fail "3.1 Failed to create IP list"
    increment_failed; summary; exit 0
  }
  EXISTING_LIST=$(echo "${IP_LIST}" | jq -r '.id')
  pass "3.1 Corporate IP list created (id: ${EXISTING_LIST})"
fi

# Create policy: require MFA from non-corporate networks
EXISTING_POLICY=$(jc_get_v2 "/authn/policies" | jq -r '.[] | select(.name == "HTH: MFA Outside Corporate") | .id') || true

if [ -n "${EXISTING_POLICY}" ]; then
  info "3.1 Conditional access policy already exists (id: ${EXISTING_POLICY})"
else
  info "3.1 Creating conditional access policy..."
  RESPONSE=$(jc_post_v2 "/authn/policies" "$(jq -n --arg list_id "${EXISTING_LIST}" '{
    "name": "HTH: MFA Outside Corporate",
    "type": "user_portal",
    "disabled": false,
    "conditions": {
      "not": {
        "ipAddressIn": [$list_id]
      }
    },
    "effect": {
      "action": "allow",
      "obligations": {
        "mfa": { "required": true },
        "mfaFactors": ["TOTP", "WEBAUTHN"],
        "userVerification": { "requirement": "required" }
      }
    },
    "targets": {
      "resources": [{ "type": "user_portal" }]
    }
  }')") || {
    fail "3.1 Failed to create conditional access policy"
    increment_failed; summary; exit 0
  }
  pass "3.1 Conditional access policy created"
fi

increment_applied

3.2 Configure Device Trust

Profile Level: L2 (Hardened)

Framework Control
CIS Controls 4.1
NIST 800-53 AC-2(11)

Description

Configure device trust to verify endpoint compliance before granting access to protected resources.

ClickOps Implementation

Step 1: Enable Device Trust

  1. Navigate to: SecurityConditional Access
  2. Create policy with device conditions
  3. Configure:
    • Require JumpCloud Agent: Verify device is managed
    • Require encryption: Verify disk encryption
    • Require updated OS: Verify minimum OS version

Step 2: Create Device Trust Policy

  1. Integrate with conditional access
  2. Block access from non-compliant devices
  3. Or require additional authentication

4. Device & System Management

4.1 Configure System Policies

Profile Level: L1 (Baseline)

Framework Control
CIS Controls 4.1
NIST 800-53 CM-7

Description

Configure JumpCloud system policies to enforce security settings across managed devices.

ClickOps Implementation

Step 1: Access System Policies

  1. Navigate to: Device ManagementPolicies
  2. Review available policy types

Step 2: Configure Security Policies Create and apply these essential policies:

Screen Lock Policy:

  1. Create new policy for screen lock
  2. Configure:
    • Lock after inactivity: 5 minutes
    • Require password: Yes
  3. Apply to all systems

Full Disk Encryption Policy:

  1. Create policy for FDE
  2. Configure:
    • Windows: BitLocker
    • macOS: FileVault
    • Linux: LUKS
  3. Enforce encryption with key escrow

Firewall Policy:

  1. Create policy enabling firewall
  2. Configure:
    • Windows Firewall: Enabled
    • macOS Firewall: Enabled
  3. Apply to all systems

System Updates Policy:

  1. Create policy for OS updates
  2. Configure update schedule
  3. Enforce critical security patches

Time to Complete: ~1 hour


Code Pack: API Script
hth-jumpcloud-4.01-configure-system-policies.sh View source on GitHub ↗
# List all device policies and check compliance status
POLICIES=$(jc_get_v2 "/policies") || {
  fail "4.1 Unable to retrieve policies"
  increment_failed; summary; exit 0
}

TOTAL_POLICIES=$(echo "${POLICIES}" | jq 'length')
info "4.1 Total device policies: ${TOTAL_POLICIES}"

# Check each policy for compliance status
for POLICY_ID in $(echo "${POLICIES}" | jq -r '.[].id'); do
  POLICY_NAME=$(echo "${POLICIES}" | jq -r ".[] | select(.id == \"${POLICY_ID}\") | .name")
  STATUSES=$(jc_get_v2 "/policies/${POLICY_ID}/policystatuses" 2>/dev/null) || continue

  TOTAL=$(echo "${STATUSES}" | jq 'length')
  COMPLIANT=$(echo "${STATUSES}" | jq '[.[] | select(.status == "success")] | length')
  NON_COMPLIANT=$(echo "${STATUSES}" | jq '[.[] | select(.status != "success")] | length')

  if [ "${NON_COMPLIANT}" -gt 0 ]; then
    warn "4.1 Policy '${POLICY_NAME}': ${COMPLIANT}/${TOTAL} compliant (${NON_COMPLIANT} non-compliant)"
  else
    info "4.1 Policy '${POLICY_NAME}': ${COMPLIANT}/${TOTAL} compliant"
  fi
done

pass "4.1 System policy compliance audit complete"
increment_applied

4.2 Configure LDAP & RADIUS Security

Profile Level: L2 (Hardened)

Framework Control
CIS Controls 3.10
NIST 800-53 SC-8

Description

Secure JumpCloud’s cloud LDAP and RADIUS services for directory and network authentication.

ClickOps Implementation

Step 1: Configure Cloud LDAP

  1. Navigate to: SettingsCloud LDAP
  2. Review bound applications
  3. Use dedicated service accounts for LDAP binds
  4. Enable TLS for LDAP connections

Step 2: Configure Cloud RADIUS

  1. Navigate to: SettingsCloud RADIUS
  2. Configure for WiFi/VPN authentication
  3. Require MFA for RADIUS authentication
  4. Configure shared secrets securely

5. Monitoring & Detection

5.1 Enable Directory Insights

Profile Level: L1 (Baseline)

Framework Control
CIS Controls 8.2
NIST 800-53 AU-2, AU-6

Description

Enable JumpCloud Directory Insights for comprehensive audit logging and security monitoring.

ClickOps Implementation

Step 1: Access Directory Insights

  1. Navigate to: ReportsDirectory Insights
  2. Review available log types:
    • Admin events
    • User authentication
    • System events
    • SSO events

Step 2: Configure Log Export

  1. Navigate to: SettingsDirectory Insights
  2. Configure SIEM integration:
    • AWS S3
    • Azure Blob Storage
    • Webhook (generic SIEM)
  3. Configure retention period

Time to Complete: ~30 minutes


Code Pack: API Script
hth-jumpcloud-5.01-enable-directory-insights.sh View source on GitHub ↗
# Enable Directory Insights feature
ORG_ID=$(jc_get_v1 "/organizations" | jq -r '.[0].id // empty')
if [ -z "${ORG_ID}" ]; then
  fail "5.1 Unable to determine organization ID"
  increment_failed; summary; exit 0
fi

CURRENT=$(jc_get_v1 "/organizations/${ORG_ID}" | jq -r '.settings.features.directoryInsights.enabled // false')
info "5.1 Directory Insights enabled: ${CURRENT}"

if [ "${CURRENT}" != "true" ]; then
  info "5.1 Enabling Directory Insights..."
  jc_put_v1 "/organizations/${ORG_ID}" '{
    "settings": {
      "features": {
        "directoryInsights": { "enabled": true }
      }
    }
  }' || {
    fail "5.1 Failed to enable Directory Insights"
    increment_failed; summary; exit 0
  }
  pass "5.1 Directory Insights enabled"
fi

# Query recent security-relevant events
START_TIME=$(date -u -d '7 days ago' +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -v-7d +%Y-%m-%dT%H:%M:%SZ)
info "5.1 Querying admin events from last 7 days..."

ADMIN_EVENTS=$(jc_insights "/events" "{
  \"service\": [\"directory\"],
  \"start_time\": \"${START_TIME}\",
  \"limit\": 20,
  \"sort\": \"DESC\",
  \"search_term\": {
    \"and\": [
      {\"event_type\": {\"\$in\": [
        \"admin_login_attempt\",
        \"admin_update\",
        \"admin_create\",
        \"organization_update\"
      ]}}
    ]
  }
}") || {
  warn "5.1 Unable to query Directory Insights (may require Platform plan)"
  increment_applied; summary; exit 0
}

EVENT_COUNT=$(echo "${ADMIN_EVENTS}" | jq 'length')
info "5.1 Admin/org events in last 7 days: ${EVENT_COUNT}"

if [ "${EVENT_COUNT}" -gt 0 ]; then
  echo "${ADMIN_EVENTS}" | jq -r '.[:5][] | "  - \(.timestamp): \(.event_type) by \(.initiated_by.email // "unknown")"'
fi

pass "5.1 Directory Insights configured and operational"
increment_applied
Code Pack: Sigma Detection Rule
hth-jumpcloud-5.01-enable-directory-insights.yml View source on GitHub ↗
detection:
    selection:
        event_type:
            - 'admin_create'
            - 'admin_update'
    condition: selection
fields:
    - timestamp
    - event_type
    - initiated_by.email
    - client_ip
    - changes

5.2 Key Events to Monitor

Event Type Detection Use Case
Admin login Unauthorized admin access
Admin changes Policy modifications
MFA bypass Security control circumvention
Failed authentication Brute force attempts
New device enrollment Unauthorized device
Policy changes Configuration drift

6. Compliance Quick Reference

SOC 2 Trust Services Criteria Mapping

Control ID JumpCloud Control Guide Section
CC6.1 Admin MFA 1.1
CC6.1 User MFA 2.1
CC6.2 Admin roles 1.2
CC6.6 Conditional access 3.1
CC7.2 Directory Insights 5.1

NIST 800-53 Rev 5 Mapping

Control JumpCloud Control Guide Section
IA-2(1) MFA enforcement 2.1
AC-6(1) Least privilege 1.2
AC-2(11) Conditional access 3.1
CM-7 System policies 4.1
AU-2 Logging 5.1

Appendix A: Plan Compatibility

Feature Free Core Platform Platform Plus
User MFA ✅ (10 users)
Admin MFA
Conditional Access
System Policies Limited
Directory Insights
JumpCloud Go

Appendix B: References

Official JumpCloud Documentation:

API & Developer Resources:

Compliance Frameworks:

Security Incidents:

  • July 2023: JumpCloud disclosed a security incident involving a nation-state threat actor who compromised JumpCloud’s internal systems, targeting a small set of customers. JumpCloud invalidated all admin API keys and notified affected customers. The incident was attributed to a North Korean state-sponsored group.

Changelog

Date Version Maturity Changes Author
2025-02-05 0.1.0 draft Initial guide with admin security, MFA, and conditional access Claude Code (Opus 4.5)

Contributing

Found an issue or want to improve this guide?