├── .gitignore ├── CHANGELOG.md ├── Gemfile ├── LICENSE ├── README.md ├── Rakefile ├── lib ├── vagrant-multi-putty.rb └── vagrant-multi-putty │ ├── command.rb │ ├── config.rb │ ├── plugin.rb │ └── version.rb └── vagrant-multi-putty.gemspec /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | .bundle 4 | .config 5 | .yardoc 6 | Gemfile.lock 7 | InstalledFiles 8 | _yardoc 9 | coverage 10 | doc/ 11 | lib/bundler/man 12 | pkg 13 | rdoc 14 | spec/reports 15 | test/tmp 16 | test/version_tmp 17 | tmp 18 | *.sw[op] -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | v1.6.0 (2016-11-03) 2 | ------------------- 3 | 4 | FEATURES: 5 | * Added the ability to override the path to the putty or putty like binary via `config.putty.ssh_client`. [GH-22] 6 | 7 | v1.5.0 (2016-04-04) 8 | ------------------- 9 | 10 | Added automatic putty .ppk file generation [GH-20] 11 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | 3 | group :development do 4 | gem "vagrant", git: "https://github.com/mitchellh/vagrant.git" 5 | end 6 | 7 | group :plugins do 8 | gem "vagrant-multi-putty", path: "." 9 | end 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2012 Nicholas Downs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vagrant-multi-putty 2 | 3 | * Source: https://github.com/nickryand/vagrant-multi-putty 4 | 5 | This plugin allows you to use putty to ssh into VMs. It has been tested on 6 | Windows and should also work on Linux. Multi-vm environments are supported. 7 | 8 | ## Installation 9 | ### Vagrant Version Support 10 | Vagrant > 1.1.X 11 | ### Software 12 | To install for Vagrant versions > 1.1 13 | ``` 14 | $ vagrant plugin install vagrant-multi-putty 15 | ``` 16 | 17 | ### Putty Binary 18 | Download: http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html 19 | 20 | Download the putty executable for your platform and add it's location to your 21 | PATH environment variable. Seek your operating system manual for instructions 22 | on how to modify your PATH variable. 23 | 24 | ### SSH Private Key conversion 25 | SSH keys will be automatically converted to the ppk format used by putty. 26 | 27 | As of Vagrant 1.4.0, the `config.ssh.private_key_path` variable is converted into 28 | an array. This allows multiple SSH keys to be passed to ssh. PuTTY does not 29 | allow for a list of ssh keys via the command line. Therefore, if the 30 | `config.putty.private_key_path` variable is not set, we attempt to use the first 31 | key in the `config.ssh.private_key_path` list. 32 | 33 | ## Configuration 34 | Most of the ssh configuration options used to control vagrant ssh also 35 | control vagrant-multi-putty. The following Vagrantfile options are NOT used by 36 | vagrant-multi-putty: 37 | 38 | * `config.ssh.max_tries` 39 | * `config.ssh.timeout` 40 | * `config.ssh.shell` 41 | 42 | All other config.ssh options should work for vagrant-multi-putty just like they 43 | do for vagrant ssh. 44 | 45 | There are currently a few additional configuration parameters available: 46 | 47 | * `config.putty.username`: Overrides the username set with 48 | ` config.ssh.username`. 49 | * `config.putty.private_key_path`: Used to explicity set the path to the 50 | private key variable. When set to `:agent`, no private key file is supplied 51 | and PuTTY will try private keys loaded by Pageant. 52 | * `config.putty.modal`: change vagrant-multi-putty to use modal window mode. 53 | Execute putty and block the terminal until all putty processes have exited. 54 | Can be set on the command line with `-m` or `--modal`. This is false by default. 55 | * `config.putty.after_modal`: Configure a post hook block that will be called 56 | once all child putty processes have exited and modal mode is enabled. The 57 | default block is empty. 58 | * `config.putty.session`: Load settings from a saved putty session. 59 | * `config.putty.ssh_client`: Allow end users to control the path to the putty 60 | or putty like (kitty for example) binary. 61 | Use slashes (not backslashes) for full path under Windows, for example: 62 | `config.putty.ssh_client = "C:/Program Files (x86)/PuTTY/putty.exe"` 63 | * `config.putty.ssh_options`: Allow end users define the Connection type or 64 | any other arguments. Multiple options separaed by comma. Default is `-ssh`. 65 | 66 | #### Example usage of after_modal post hook 67 | This is an example which uses the the win32-activate gem written by nazoking. This 68 | only works on windows since win32-activate uses the win32 API. 69 | 70 | Github Page: https://github.com/nazoking/win32-activate 71 | 72 | After all putty windows are closed, the terminal window used to run the 'vagrant putty' 73 | command will be brought into focus and placed on top of all open windows. 74 | ``` 75 | Vagrant.configure("2") do |config| 76 | # always modal mode 77 | config.putty.modal = true 78 | # set hook. 79 | config.putty.after_modal do 80 | require 'win32/activate' 81 | Win32::Activate.active 82 | end 83 | end 84 | ``` 85 | 86 | #### Example multiple tunnels 87 | This example sets the path to your PuTTY installation. 88 | The ssh_options are used to enable a tunnel from host port 8008 to port 80 on guest. 89 | The second tunnel is a reverse tunnel from guest port 6000 back to the host port 6000, 90 | so you can use Xming as Xserver on your host as output for X11, for example 91 | `DISPLAY=localhost:0.0 xclock`. 92 | ``` 93 | Vagrant.configure("2") do |config| 94 | # Set PATH for PuTTY 95 | config.putty.ssh_client = "C:/Program Files (x86)/PuTTY/putty.exe" 96 | # Overwrite default options with SSH as protocol, 97 | # enable tunnel from host port 8008 to guest port 80, and set 98 | # reverse tunnel from guest port 6000 to host port 6000. 99 | config.putty.ssh_options = "-ssh", "-L", "8008:localhost:80", "-R", "6000:localhost:6000" 100 | # 101 | end 102 | ``` 103 | 104 | ## Usage 105 | Basic usage: 106 | ``` 107 | vagrant putty 108 | ``` 109 | 110 | Login into a single vm in a multiple vm environment: 111 | ``` 112 | vagrant putty 113 | ``` 114 | 115 | Pass putty options directly to the putty binary: 116 | ``` 117 | vagrant putty -- -l testuser -i 118 | ``` 119 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'bundler/setup' 3 | 4 | # Immediately sync all stdout so that tools like buildbot can 5 | # immediately load in the output. 6 | $stdout.sync = true 7 | $stderr.sync = true 8 | 9 | # Change to the directory of this file. 10 | Dir.chdir(File.expand_path("../", __FILE__)) 11 | 12 | # This installs the tasks that help with gem creation and 13 | # publishing. 14 | Bundler::GemHelper.install_tasks -------------------------------------------------------------------------------- /lib/vagrant-multi-putty.rb: -------------------------------------------------------------------------------- 1 | require 'vagrant-multi-putty/command' 2 | require 'vagrant-multi-putty/config' 3 | require 'vagrant-multi-putty/plugin' 4 | require 'vagrant-multi-putty/version' 5 | -------------------------------------------------------------------------------- /lib/vagrant-multi-putty/command.rb: -------------------------------------------------------------------------------- 1 | # Pieces of this plugin were taken from the bundled vagrant ssh plugin. 2 | require 'rubygems' 3 | require 'openssl' 4 | require 'optparse' 5 | require 'putty/key' 6 | 7 | using PuTTY::Key 8 | 9 | module VagrantMultiPutty 10 | class Command < Vagrant.plugin(2, :command) 11 | def execute 12 | 13 | # config_global is deprecated from v1.5 14 | if Gem::Version.new(::Vagrant::VERSION) >= Gem::Version.new('1.5') 15 | @config = @env.vagrantfile.config 16 | else 17 | @config = @env.config_global 18 | end 19 | 20 | options = {:modal => @config.putty.modal, 21 | :plain_auth => false } 22 | opts = OptionParser.new do |opts| 23 | opts.banner = "Usage: vagrant putty [vm-name...] [-- extra putty args]" 24 | 25 | opts.on("-p", "--plain", "Plain auth mode which will require the user to provide a password.") do |p| 26 | options[:plain_auth] = p 27 | end 28 | 29 | opts.on("-m", "--modal", "Block until all spawned putty processes have exited") do |m| 30 | options[:modal] = m 31 | end 32 | 33 | opts.separator "" 34 | end 35 | 36 | argv = parse_options(opts) 37 | return -1 if !argv 38 | 39 | # This is borrowed from the ssh base command that ships with vagrant. 40 | # It is used to parse out arguments meant for the putty program. 41 | putty_args = ARGV.drop_while { |i| i != "--" } 42 | putty_args = putty_args[1..-1] if !putty_args.empty? 43 | @logger.debug("Putty args: #{putty_args}") 44 | 45 | # argv needs to be purged of the extra putty arguments. The remaining arguments 46 | # (if any) will be the VM names to log into. 47 | argv = argv - putty_args 48 | 49 | # Since putty is a program with a GUI window, we can perform a spawn and 50 | # detach the process from vagrant. 51 | with_target_vms(argv) do |vm| 52 | @logger.info("Launching putty session to: #{vm.name}") 53 | putty_connect(vm, putty_args, options) 54 | end 55 | 56 | if options[:modal] 57 | Process.waitall 58 | @config.putty.after_modal_hook.call 59 | end 60 | 61 | return 0 62 | end 63 | 64 | def putty_connect(vm, args, options={}) 65 | # This isn't called by vagrant automatically. 66 | vm.config.putty.finalize! 67 | 68 | ssh_info = vm.ssh_info 69 | # If ssh_info is nil, the machine is not ready for ssh. 70 | raise Vagrant::Errors::SSHNotReady if ssh_info.nil? 71 | 72 | ssh_options = [] 73 | 74 | # Load a saved putty session if provided. Putty (v0.63 at least) appears 75 | # to have a weird bug where a hostname specified on the command line will 76 | # not override the hostname in a session unless the hostname comes after 77 | # the -load option. This doesn't appear to affect any other command line 78 | # options aside from hostname. 79 | ssh_options += ["-load", vm.config.putty.session] if 80 | vm.config.putty.session 81 | 82 | # Load options from machine ssh_info. 83 | ssh_options += [ssh_info[:host]] 84 | # config.putty.username overrides the machines ssh_info username. 85 | ssh_options += ["-l", vm.config.putty.username || ssh_info[:username]] 86 | ssh_options += ["-P", ssh_info[:port].to_s] 87 | ssh_options += ["-X"] if ssh_info[:forward_x11] 88 | ssh_options += ["-A"] if ssh_info[:forward_agent] 89 | 90 | # Putty only allows one ssh key to be passed with the -i option 91 | # so we default to choosing the first default key if it is not 92 | # explicitly set. 93 | private_key = vm.config.putty.private_key_path || 94 | get_putty_key_file(ssh_info[:private_key_path][0]) 95 | @logger.debug("Putty Private Keys: #{private_key.to_s}") 96 | ssh_options += ["-i", private_key] unless 97 | options[:plain_auth] || private_key == :agent 98 | 99 | # Set Connection type to -ssh, in cases other protocol 100 | # stored in Default Settings of Putty. 101 | if vm.config.putty.ssh_options 102 | if vm.config.putty.ssh_options.class == Array 103 | ssh_options += vm.config.putty.ssh_options 104 | else 105 | ssh_options += [vm.config.putty.ssh_options] 106 | end 107 | end 108 | 109 | # Add in additional args from the command line. 110 | ssh_options.concat(args) if !args.nil? 111 | 112 | # Spawn putty and detach it so we can move on. 113 | @logger.debug("Putty cmd line options: #{ssh_options.to_s}") 114 | pid = spawn(@config.putty.ssh_client, *ssh_options) 115 | @logger.debug("Putty Child Pid: #{pid}") 116 | Process.detach(pid) 117 | end 118 | 119 | private 120 | 121 | def get_putty_key_file(ssh_key_path) 122 | "#{ssh_key_path}.ppk".tap do |ppk_path| 123 | if !File.exist?(ppk_path) || File.mtime(ssh_key_path) > File.mtime(ppk_path) 124 | ssh_key = OpenSSL::PKey.read(File.read(ssh_key_path, mode: 'rb')) 125 | ppk = ssh_key.to_ppk 126 | ppk.comment = "Converted by vagrant-multi-putty at #{Time.now}" 127 | ppk.save(ppk_path) 128 | end 129 | end 130 | end 131 | end 132 | end 133 | -------------------------------------------------------------------------------- /lib/vagrant-multi-putty/config.rb: -------------------------------------------------------------------------------- 1 | module VagrantMultiPutty 2 | class PuttyConfig < Vagrant.plugin(2, :config) 3 | attr_accessor :username 4 | attr_accessor :private_key_path 5 | attr_accessor :after_modal_hook 6 | attr_accessor :modal 7 | attr_accessor :session 8 | attr_accessor :ssh_client 9 | attr_accessor :ssh_options 10 | 11 | def after_modal &proc 12 | @after_modal_hook = proc 13 | end 14 | 15 | def initialize 16 | @username = UNSET_VALUE 17 | @private_key_path = UNSET_VALUE 18 | @after_modal_hook = UNSET_VALUE 19 | @modal = UNSET_VALUE 20 | @session = UNSET_VALUE 21 | @ssh_client = UNSET_VALUE 22 | @ssh_options = UNSET_VALUE 23 | end 24 | 25 | def finalize! 26 | @username = nil if @username == UNSET_VALUE 27 | @private_key_path = nil if @private_key_path == UNSET_VALUE 28 | @after_modal_hook = Proc.new{ } if @after_modal_hook == UNSET_VALUE 29 | @modal = false if @modal == UNSET_VALUE 30 | @session = nil if @session == UNSET_VALUE 31 | @ssh_client = "putty" if @ssh_client == UNSET_VALUE 32 | @ssh_options = "-ssh" if @ssh_options == UNSET_VALUE 33 | end 34 | 35 | def validate(machine) 36 | {} 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /lib/vagrant-multi-putty/plugin.rb: -------------------------------------------------------------------------------- 1 | require 'vagrant' 2 | 3 | module VagrantMultiPutty 4 | class Plugin < Vagrant.plugin("2") 5 | name "vagrant-multi-putty" 6 | description <<-DESC 7 | Vagrant-multi-putty allows you to ssh into your virtual machines using the putty 8 | program (or other compatible ssh clients like kitty). This plugin also supports 9 | opening putty sessions into multi-vm environments. 10 | DESC 11 | 12 | command "putty" do 13 | require_relative "command" 14 | Command 15 | end 16 | 17 | config "putty" do 18 | require_relative "config" 19 | PuttyConfig 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/vagrant-multi-putty/version.rb: -------------------------------------------------------------------------------- 1 | module VagrantMultiPutty 2 | VERSION = "1.6.0" 3 | end 4 | -------------------------------------------------------------------------------- /vagrant-multi-putty.gemspec: -------------------------------------------------------------------------------- 1 | require File.expand_path("../lib/vagrant-multi-putty/version", __FILE__) 2 | 3 | Gem::Specification.new do |s| 4 | s.name = "vagrant-multi-putty" 5 | s.version = VagrantMultiPutty::VERSION 6 | s.platform = Gem::Platform::RUBY 7 | s.authors = ["Nick Downs"] 8 | s.email = ["nickryand@gmail.com"] 9 | s.licenses = ["MIT"] 10 | s.homepage = "https://github.com/nickryand/vagrant-multi-putty" 11 | s.summary = "Vagrant plugin to allow VM ssh with PuTTY (multi-vm supported)" 12 | s.description = "Vagrant plugin to allow VM ssh with PuTTY (multi-vm supported)" 13 | 14 | s.required_ruby_version = ">= 2.1.0" 15 | s.required_rubygems_version = ">= 1.4.0" 16 | 17 | s.files = `git ls-files`.split("\n") 18 | s.require_path = 'lib' 19 | 20 | s.add_runtime_dependency "putty-key", "~> 1.0" 21 | end 22 | --------------------------------------------------------------------------------