Secure JumpCloud with cnspec
Scan JumpCloud users, systems, groups, SSO applications, policies, and RADIUS servers against security and compliance best practices with cnspec.
JumpCloud is both the directory that decides who your people are and the management plane that decides what their devices do, so one JumpCloud organization holds the identity posture and the device posture in the same place. cnspec reads a JumpCloud organization through its API and evaluates the user accounts and their multi-factor and lifecycle state, the enrolled systems and their SSH, multi-factor, and full disk encryption settings, the user and system groups that carry access, the SSO applications and the groups that reach them, and the policies, commands, RADIUS servers, and external directory integrations configured in the organization.
cnspec reads directory and device configuration only. It never reads user passwords, SSH private keys, or the contents of managed systems: it queries the JumpCloud API's directory endpoints.
If you're new to cnspec, start with the Quickstart. For an overview of every identity provider cnspec can scan, see the identity scanning overview.
Prerequisites
To scan JumpCloud with cnspec, you must have:
- cnspec installed on your workstation
- A JumpCloud organization
- A JumpCloud API key
Authenticate
cnspec authenticates with a JumpCloud API key. To create one, sign in to the JumpCloud Admin Portal, open the account menu in the top right, and go to My API Key. The key inherits the permissions of the administrator who owns it, so create it under an account with read access to the resources you want to scan.
cnspec shell jumpcloud --api-key YOUR_API_KEYMulti-tenant organizations
A multi-tenant (MSP) API key can act on more than one organization, so it can't infer which one you mean. Pass the organization ID alongside the key. You can find it in the Admin Portal under Settings > General.
cnspec shell jumpcloud --api-key YOUR_API_KEY --org-id YOUR_ORG_IDA single-tenant key resolves its own organization, so --org-id is optional there.
Environment variables
JUMPCLOUD_API_KEY supplies the key, and JUMPCLOUD_ORG_ID supplies the organization ID for a multi-tenant key:
export JUMPCLOUD_API_KEY=YOUR_API_KEY
export JUMPCLOUD_ORG_ID=YOUR_ORG_IDWhen these are set, you can omit the matching flags from the commands below.
Connection options
| Option | Description |
|---|---|
--api-key | JumpCloud API key |
--org-id | Organization ID, required for a multi-tenant (MSP) key |
Verify with a quick JumpCloud check
Confirm that cnspec can reach your organization by opening a cnspec shell:
cnspec shell jumpcloud --api-key YOUR_API_KEYcnspec> jumpcloud.users.length
jumpcloud.users.length: 187If cnspec connects and reports a user count, you're ready to scan.
Scan JumpCloud
cnspec scan jumpcloud --api-key YOUR_API_KEYThe organization is a single asset. There are no child assets to discover.
When a scan completes, cnspec prints a summary of all the checks it ran, grouped by policy, along with a risk score from 0 (no risk) to 100 (highest risk). Failed checks include remediation guidance to help you fix issues. To learn more about reading scan results, read Understand cnspec Results.
Mondoo doesn't yet ship an out-of-the-box JumpCloud policy, so use the checks below as a starting point and create your own policies to meet your specific requirements.
Explore and test checks interactively
Open a cnspec shell to discover resources and try out checks:
cnspec shell jumpcloud --api-key YOUR_API_KEYList users and their security posture
state carries the lifecycle stage, and the flags beside it carry the risk:
cnspec> jumpcloud.users { email state activated suspended accountLocked mfaConfigured totpEnabled }Review who holds administrative rights on systems
sudo grants administrator rights on associated systems, and passwordlessSudo removes the password prompt that would otherwise confirm the person is still there:
cnspec> jumpcloud.users { email sudo passwordlessSudo ldapBindingUser }List systems and their hardening settings
cnspec> jumpcloud.systems {
hostname
os
version
agentVersion
active
fdeActive
allowSshRootLogin
allowSshPasswordAuthentication
allowMultiFactorAuthentication
}Find systems that stopped reporting
lastContact is the last time the agent checked in, so a stale value means the device is off, gone, or no longer managed:
cnspec> jumpcloud.systems { hostname active lastContact remoteIP }Review the user groups and who's in them
cnspec> jumpcloud.userGroups { name type members { email state } }Review the system groups and what they contain
System groups are the unit policies and commands bind to, so a group's membership decides which devices a policy actually reaches:
cnspec> jumpcloud.systemGroups { name members { hostname os } }Review the SSO applications and who reaches them
cnspec> jumpcloud.applications { displayName ssoUrl active userGroups { name } }Review the policies and whether they're active
cnspec> jumpcloud.policies { name templateName active }Review the commands and the privileges they run with
A command is remote code execution by design, so the ones that run with sudo on a schedule deserve the most scrutiny:
cnspec> jumpcloud.commands { name commandType launchType sudo timeout }Review RADIUS authentication
cnspec> jumpcloud.radiusServers { name networkSourceIp mfa }Review external directory integrations
An integration synchronizes identities from another directory, so it's an inbound path into the organization:
cnspec> jumpcloud.directories { name type }Walk from a resource to its neighbors
Users, systems, and groups reference each other, so a query can start from any of them and walk in either direction:
cnspec> jumpcloud.users { email userGroups { name } systems { hostname } }
cnspec> jumpcloud.systems { hostname users { email } systemGroups { name } }
cnspec> jumpcloud.userGroups { name members { email } }Example security checks
Ensure every active user has multi-factor authentication configured
cnspec> jumpcloud.users.where(state == "ACTIVATED").all(mfaConfigured == true)
[ok] value: trueEnsure administrators use multi-factor authentication
An account with sudo on managed systems is the one worth protecting most:
cnspec> jumpcloud.users.where(sudo == true).all(mfaConfigured == true)
[ok] value: trueEnsure no account is granted passwordless sudo
cnspec> jumpcloud.users.none(passwordlessSudo == true)
[ok] value: trueEnsure sudo rights stay rare
cnspec> jumpcloud.users.where(sudo == true).length <= 5
[ok] value: trueEnsure no account is left locked out
A locked account is an unresolved incident or an offboarding nobody finished:
cnspec> jumpcloud.users.none(accountLocked == true)
[ok] value: trueEnsure staged accounts don't linger
A staged account was created but never activated, so it's a seat nobody watches:
cnspec> jumpcloud.users.none(state == "STAGED")
[ok] value: trueEnsure suspended accounts hold no group membership
Suspending an account stops the sign-in. It doesn't remove the access that was granted through groups:
cnspec> jumpcloud.users.where(suspended == true).all(userGroups.length == 0)
[ok] value: trueEnsure no user's password has expired unnoticed
cnspec> jumpcloud.users.where(state == "ACTIVATED").none(passwordExpired == true)
[ok] value: trueEnsure every active system encrypts its disk
Full disk encryption is what makes a lost laptop a lost laptop rather than a breach:
cnspec> jumpcloud.systems.where(active == true).all(fdeActive == true)
[ok] value: trueEnsure no system permits SSH login as root
cnspec> jumpcloud.systems.none(allowSshRootLogin == true)
[ok] value: trueEnsure systems require SSH keys rather than passwords
cnspec> jumpcloud.systems.all(allowSshPasswordAuthentication == false)
[ok] value: trueEnsure systems require multi-factor authentication at the login screen
cnspec> jumpcloud.systems.where(active == true).all(allowMultiFactorAuthentication == true)
[ok] value: trueEnsure RADIUS authentication requires a second factor
RADIUS often fronts the network itself, so a password alone there reaches further than a password elsewhere:
cnspec> jumpcloud.radiusServers.all(mfa == "REQUIRED")
[ok] value: trueEnsure no SSO application is left active without a group behind it
An application no group reaches is either unused or reachable some other way, and both are worth knowing:
cnspec> jumpcloud.applications.where(active == true).all(userGroups.length > 0)
[ok] value: trueEnsure every policy that exists is actually enforced
An inactive policy is a control someone wrote and nobody applied:
cnspec> jumpcloud.policies.all(active == true)
[ok] value: trueReview systems that haven't reported recently
cnspec> jumpcloud.systems.where(active == false) { hostname os lastContact }Learn more
- JumpCloud Resource Pack Reference: every JumpCloud resource and field cnspec can query
- Write Effective MQL: guide to authoring checks and queries