0.0
The project is in a healthy, maintained state
Provides SSH connectivity to Juniper Networks devices running JunOS for InSpec compliance testing and infrastructure inspection. Supports platform detection, command execution, and configuration file access.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
 Dependencies

Development

~> 2.0
~> 11.1
~> 5.0
~> 2.0
~> 13.0
~> 3.5
~> 1.0
~> 0.22
~> 0.9

Runtime

>= 2.9, < 8.0
~> 3.12, >= 3.12.13
>= 1.15.5, < 1.18.0
 Project Readme

Train Juniper Plugin

A production-ready Train plugin that provides SSH connectivity to Juniper Networks devices running JunOS for InSpec compliance testing and infrastructure inspection.

!!! info "Quick Start" Install the plugin: inspec plugin install train-juniper and start testing your Juniper infrastructure immediately.

Features :material-star:

The plugin supports:

  • :material-ssh: SSH Authentication - Secure connections with password or key-based auth
  • :material-router-network: JunOS Platform Detection - Automatic version parsing and platform registration
  • :material-console: Command Execution - CLI command execution with Juniper-specific prompt handling
  • :material-network: Proxy/Bastion Support - Enterprise network connectivity through jump hosts
  • :material-file-document: Configuration Inspection - Pseudo-file operations for configuration access
  • :material-test-tube: Mock Mode - Complete testing support without requiring real hardware
  • :material-speedometer: Performance Optimized - Efficient platform detection with result caching

Installation :material-download:

!!! warning "Prerequisites" You will need InSpec v4.0 or later.

Production Installation (from RubyGems)

# Search for train plugins
$ inspec plugin search train-

# Install train-juniper (once published)
$ inspec plugin install train-juniper

# Verify installation
$ inspec plugin list

Development Installation (local testing)

# Clone the repository
$ git clone https://github.com/mitre/train-juniper.git
$ cd train-juniper

# Install dependencies and run tests
$ bundle install
$ bundle exec rake test

# Build and install local gem
$ gem build train-juniper.gemspec
$ inspec plugin install ./train-juniper-0.1.0.gem

# Verify plugin is loaded
$ inspec plugin list

Usage

Basic Connection

# Detect platform
$ inspec detect -t juniper://admin@192.168.1.1 --password yourpassword

== Platform Details
Name:      juniper
Families:  bsd
Release:   21.4R3-S1.6
Arch:      x86_64

# Interactive shell
$ inspec shell -t juniper://admin@192.168.1.1 --password yourpassword
inspec> command('show version').stdout
=> "Hostname: srx-fw\nModel: SRX340\nJunos: 21.4R3-S1.6\n..."

With Bastion Host (Jump Host)

# Simplified: Same username/password for bastion and device (most common)
$ inspec shell -t juniper://admin@10.1.1.1 --password yourpassword \
    --bastion-host jump.example.com

# Different credentials for bastion and device
$ inspec shell -t juniper://admin@10.1.1.1 --password device_password \
    --bastion-host jump.example.com --bastion-user netadmin \
    --bastion-password jump_password

# With custom port
$ inspec shell -t juniper://admin@10.1.1.1 --password yourpassword \
    --bastion-host jump.example.com --bastion-port 2222

# Using environment variables (recommended for automation)
export JUNIPER_BASTION_HOST=jump.example.com
export JUNIPER_PASSWORD=shared_password  # Used for both bastion and device
$ inspec shell -t juniper://admin@10.1.1.1

# Different passwords via environment
export JUNIPER_BASTION_HOST=jump.example.com
export JUNIPER_BASTION_USER=netadmin  
export JUNIPER_BASTION_PASSWORD=jump_password
export JUNIPER_PASSWORD=device_password
$ inspec shell -t juniper://admin@10.1.1.1

With Custom Proxy Command

# Using SSH ProxyCommand syntax  
$ inspec shell -t "juniper://admin@device.internal?proxy_command=ssh%20jump.host%20-W%20%h:%p"

# Complex corporate network scenario
$ inspec detect -t "juniper://netadmin@core-switch.corp?bastion_host=jump.dmz.corp&bastion_user=svc_inspec"

Environment Variables (Auto-Detection)

The plugin automatically detects and uses standard environment variables, eliminating the need to pass connection flags:

# Basic connection with auto-detection
export JUNIPER_HOST=192.168.1.1
export JUNIPER_USER=admin  
export JUNIPER_PASSWORD=yourpassword
inspec detect -t juniper://  # No flags needed!

# With bastion host auto-detection
export JUNIPER_HOST=internal.device.corp
export JUNIPER_USER=netadmin
export JUNIPER_PASSWORD=devicepass
export JUNIPER_BASTION_HOST=jump.corp.com
export JUNIPER_BASTION_USER=admin
export JUNIPER_BASTION_PASSWORD=bastionpass
inspec detect -t juniper://  # Automatically uses bastion!

# Using .env file (recommended for development)
# Create .env file with your credentials:
source .env
inspec detect -t juniper://  # Reads from .env automatically

Configuration Options

Option Priority

The plugin uses the following priority order for configuration values:

  1. Command-line flags (highest priority) - e.g., --bastion-user
  2. Environment variables - e.g., JUNIPER_BASTION_USER
  3. Defaults/Fallbacks (lowest priority) - e.g., bastion_user falls back to main user

This allows maximum flexibility while providing sensible defaults for common scenarios.

Connection Options

Option Description Default Environment Variable
host Juniper device hostname/IP - JUNIPER_HOST
user SSH username - JUNIPER_USER
password SSH password - JUNIPER_PASSWORD
port SSH port 22 JUNIPER_PORT
timeout Connection timeout (seconds) 30 JUNIPER_TIMEOUT
keepalive SSH keepalive enabled true -
keepalive_interval SSH keepalive interval (seconds) 60 -

Proxy/Bastion Options

Option Description Default Environment Variable
bastion_host SSH bastion/jump host - JUNIPER_BASTION_HOST
bastion_user SSH bastion username Falls back to main user JUNIPER_BASTION_USER
bastion_port SSH bastion port 22 JUNIPER_BASTION_PORT
bastion_password Password for bastion authentication - JUNIPER_BASTION_PASSWORD
proxy_command Custom SSH ProxyCommand - JUNIPER_PROXY_COMMAND
key_files SSH private key files - -
keys_only Use only specified keys false -

!!! note "Important Configuration Notes" - Cannot specify both bastion_host and proxy_command simultaneously - If bastion_user not provided, falls back to using main user for bastion authentication - If bastion_password not provided, falls back to using main password for bastion authentication - Supports automated password authentication via SSH_ASKPASS mechanism

InSpec Configuration File

Create ~/.inspec/config.json:

{
  "credentials": {
    "juniper-lab": {
      "target": "juniper://admin@lab-srx.example.com",
      "password": "yourpassword",
      "insecure": true
    }
  }
}

Then use: inspec detect --config=juniper-lab

Proxy Connection Patterns

The train-juniper plugin supports Train-standard proxy/bastion connections for enterprise environments where Juniper devices are behind jump hosts or in isolated network segments.

Authentication Patterns

Important: Train does not have a separate --bastion-password option. Here are the standard authentication patterns:

🔐 Same Credentials (Most Common)

Use the same --password for both bastion host and Juniper device:

# Same username/password for jump host and device
inspec detect -t "juniper://admin@device.internal?bastion_host=jump.corp.com&bastion_user=admin" --password "shared_password"

# With environment variables
export JUNIPER_PASSWORD="shared_password"
inspec shell -t "juniper://admin@device.internal?bastion_host=jump.corp.com&bastion_user=admin"

🔑 SSH Key Authentication (Recommended)

Use SSH keys for both connections:

# SSH keys for both bastion and device
inspec detect -t "juniper://admin@device.internal?bastion_host=jump.corp.com&bastion_user=admin" -i ~/.ssh/id_rsa

# With multiple keys
inspec shell -t "juniper://admin@device.internal?bastion_host=jump.corp.com" --key-files ~/.ssh/bastion_key ~/.ssh/device_key

🔗 Different Credentials (Advanced)

Use SSH ProxyCommand when bastion and device require different authentication:

# Bastion uses one password, device uses another (embed bastion auth in proxy command)
inspec detect -t "juniper://deviceuser@device.internal?proxy_command=sshpass%20-p%20bastionpass%20ssh%20bastionuser@jump.corp.com%20-W%20%h:%p" --password "device_password"

# Bastion uses SSH key, device uses password
inspec shell -t "juniper://admin@device.internal?proxy_command=ssh%20-i%20~/.ssh/bastion_key%20admin@jump.corp.com%20-W%20%h:%p" --password "device_password"

Bastion Host Scenarios

# Corporate network with dedicated jump host
inspec exec profile -t "juniper://admin@core-switch.internal?bastion_host=jump.corp.com&bastion_user=netadmin" --password "shared_password"

# Cloud environment with bastion instance  
inspec exec profile -t "juniper://ubuntu@10.0.1.100?bastion_host=bastion.aws.company.com&bastion_port=2222" --key-files ~/.ssh/aws_key

# DMZ access pattern
inspec detect -t "juniper://operator@firewall.dmz?bastion_host=jump.dmz.corp&bastion_user=svc_account" --password "corporate_password"

Custom Proxy Commands

# SSH ProxyCommand for complex routing
inspec shell -t "juniper://admin@device?proxy_command=ssh%20-o%20StrictHostKeyChecking=no%20jump%20nc%20%h%20%p"

# Multi-hop proxy (SSH chain)
inspec exec profile -t "juniper://admin@target?proxy_command=ssh%20-J%20first-jump,second-jump%20-W%20%h:%p"

SSH Key Authentication with Proxy

# In Ruby code or configuration
Train.create('juniper', {
  host: 'secure.device.corp',
  user: 'admin',
  bastion_host: 'jump.corp.com',
  bastion_user: 'automation',
  key_files: ['/path/to/private/key'],
  keys_only: true
})

Common Authentication Issues

❌ Error: "No bastion password specified"

Solution: Train doesn't have --bastion-password. Use one of these patterns:

# Same password for both (most common)
inspec detect -t "juniper://user@device?bastion_host=jump" --password "shared_pass"

# SSH keys (recommended)  
inspec detect -t "juniper://user@device?bastion_host=jump" --key-files ~/.ssh/id_rsa

# Different passwords (use proxy command)
inspec detect -t "juniper://user@device?proxy_command=sshpass%20-p%20jumppass%20ssh%20jumpuser@jump%20-W%20%h:%p" --password "device_pass"

❌ Error: "Authentication failed"

Solutions:

# Verify bastion connection first
ssh jumpuser@jump.corp.com

# Test direct device connection (if accessible)
ssh deviceuser@device.internal

# Use verbose SSH for debugging
inspec detect -t "juniper://user@device?bastion_host=jump&proxy_command=ssh%20-v%20jump%20-W%20%h:%p" --password "pass"

# Use InSpec debug mode for detailed logging
inspec detect -t "juniper://user@device?bastion_host=jump" --password "pass" -l debug

❌ Error: "Connection timeout"

Solutions:

# Increase timeouts
inspec detect -t "juniper://user@device?bastion_host=jump&connection_timeout=60" --password "pass"

# Check network connectivity
ping device.internal  # From bastion host
telnet device.internal 22  # Test SSH port

Mock Mode (Testing Without Hardware)

The train-juniper plugin includes a comprehensive mock mode for testing profiles without requiring physical Juniper hardware:

# Use mock mode with InSpec detect
inspec detect -t "juniper://admin@mock-device?mock=true"

# Output shows mocked JunOS platform:
# Name:      juniper
# Families:  bsd
# Release:   12.1X47-D15.4

For programmatic usage in tests:

require 'train'
connection = Train.create('juniper', 
  host: 'test-device',
  user: 'admin',
  mock: true
)

# Mock mode returns predefined responses
result = connection.run_command('show version')
# => Returns mock JunOS version output

Mock mode provides:

  • ✅ Realistic JunOS command outputs
  • ✅ Platform detection (JunOS 12.1X47-D15.4)
  • ✅ Error simulation for negative testing
  • ✅ Fast execution for CI/CD pipelines

Development

Requirements

  • Ruby 3.1+
  • Bundler
  • InSpec 4.0+ (for testing)

Setup

git clone https://github.com/mitre/train-juniper.git
cd train-juniper
bundle install

Testing

# Run all tests
bundle exec rake test

# Run individual test suites  
bundle exec ruby test/unit/connection_test.rb
bundle exec ruby test/functional/juniper_test.rb

# Lint code
bundle exec rubocop

Architecture

This plugin implements the Train Plugin V1 API with:

  • Transport (lib/train-juniper/transport.rb) - Plugin registration and factory
  • Connection (lib/train-juniper/connection.rb) - SSH connectivity and command execution
  • Platform (lib/train-juniper/platform.rb) - JunOS platform detection
  • Version (lib/train-juniper/version.rb) - Plugin version management

Platform Support

This gem supports a wide range of platforms to ensure maximum compatibility:

Platform Description Use Case
ruby Platform-independent Pure Ruby installations
x86_64-linux Standard Linux Most Linux servers and CI/CD
aarch64-linux ARM64 Linux AWS Graviton, Raspberry Pi
x86_64-linux-musl Alpine Linux Docker containers
x86_64-darwin Intel macOS Older Mac workstations
arm64-darwin-* Apple Silicon macOS Modern Mac workstations
x64-mingw-ucrt Windows (UCRT) Windows 10/11 with modern Ruby (bastion setup)
x86_64-freebsd FreeBSD Network appliances (JunOS heritage)
x86_64-solaris Solaris/illumos Enterprise environments

!!! note "Platform Compatibility" This comprehensive platform support ensures the plugin works wherever InSpec runs, from developer workstations to CI/CD pipelines to production jump hosts. The FreeBSD support is particularly relevant given that JunOS is based on FreeBSD.

Documentation

Plugin Development Resources

  • Train Plugin Development Guide - Comprehensive tutorial for Train plugin development
  • How This Plugin Was Built - See modules 20-22 in the development guide for our research methodology, implementation approach, and containerlab testing environment

Contributing

We welcome contributions! Here's how to get started:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Make your changes with tests
  4. Run bundle exec rake test to ensure tests pass
  5. Submit a pull request

Please see our Contributing Guide for more details.

Support and Contact

General Support

For questions, feature requests, or general support:

Security Issues

For security issues or vulnerabilities:

Acknowledgments

This project was inspired by and references several excellent community Train plugins:

  • train-rest by Thomas Heinen (Prospectra) - REST API transport patterns
  • train-awsssm by Thomas Heinen (Prospectra) - AWS Systems Manager transport
  • train-pwsh by MITRE SAF Team - PowerShell/Windows automation transport
  • train-k8s-container by InSpec Team - Kubernetes container platform detection
  • train-local-rot13 by InSpec Team - Official plugin development example

Special thanks to the Train and InSpec communities for their excellent documentation and plugin examples.

License

Licensed under the Apache-2.0 license, except as noted below.

See LICENSE for full details.

Notice

This software was produced for the U.S. Government under contract and is subject to Federal Acquisition Regulation Clause 52.227-14.

See NOTICE for full details.

© 2025 The MITRE Corporation.