bin/mu-tunnel-nagios
#!/usr/local/ruby-current/bin/ruby# Copyright:: Copyright (c) 2014 eGlobalTech, Inc., all rights reserved## Licensed under the BSD-3 license (the "License");# you may not use this file except in compliance with the License.# You may obtain a copy of the License in the root of the project or at## http://egt-labs.com/mu/LICENSE.html## Unless required by applicable law or agreed to in writing, software# distributed under the License is distributed on an "AS IS" BASIS,# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.# See the License for the specific language governing permissions and# limitations under the License. require 'syslog'require 'syslog/logger' logger = Syslog::Logger.new "mu-tunnel-nagios" #Syslog.open("mu-tunnel-nagios", Syslog::LOG_PID, Syslog::LOG_DAEMON | Syslog::LOG_LOCAL3) require 'net/ssh/config'require 'net/ssh/gateway'require 'optimist'require 'etc'SSH_CMD = "env -i /usr/bin/ssh"NAGIOS_HOME = "/opt/mu/var/nagios_user_home" $opts = Optimist::options do banner <<-EOSA utility to run a command through a remote SSH tunnel. Intended to serve as an intermediary for Nagios plugins attempting to contact private nodes through a bastion host.Usage:#{$0} -d <host> -p <port> -c <command> [-a <arg>] [-k </path/to/key>] [-g <host> [-u <user>]]Use meaningful heredoc delimiters. EOS opt :desthost, "The host to which we'll be connecting on the other side of the tunnel.", :require => true, :type => :string opt :port, "The port to which we'll be connecting on the other side of the tunnel.", :require => true, :type => :integer opt :command, "The command which we will be tunneling to the remote host.", :require => true, :type => :string opt :argument, "An argument to append to our command to specify a (local tunnel) port.", :require => false, :default => "-p", :type => :string opt :gateway, "The gateway host to use. If not specified, we'll assume we have something correct in our global ssh config.", :require => false, :type => :string opt :key, "Path to an SSH private key to use when connecting to the gateway host. If not specified, we'll assume we have something correct in our global ssh config.", :require => false, :type => :string opt :user, "The SSH user with which to connect to the gateway. If not specified, we'll assume we have something correct in our global ssh config.", :require => false, :type => :string, :default => nil# opt :local_ip, "The local (private) IP to which we'll be connecting on the other side of the tunnel.", :require => false, :type => :string, :default => "127.0.0.1" opt :directcommand, "The command to run if we don't need to tunnel to get to the remote host.", :require => false, :type => :string opt :verbose, "Debugging noise.", :require => false, :type => :boolean, :default => falseend dest_host = $opts[:desthost]dest_port = $opts[:port]cmd = $opts[:command]nat_host = $opts[:gateway]Useless assignment to variable - `nat_ssh_user`.nat_ssh_user = $opts[:user]port_arg = $opts[:argument]Useless assignment to variable - `nat_ssh_key`.nat_ssh_key = $opts[:key]#local_ip = $opts[:local_ip]verbose = $opts[:verbose] if !dest_host or !dest_port or !cmd Optimist::die "Missing required arguments"endnat_host = dest_host if !nat_host ENV.clearENV['HOME'] = NAGIOS_HOME needs_gateway = truebeginUseless assignment to variable - `ssh_conf`. ssh_conf = File.read("#{NAGIOS_HOME}/.ssh/config")Useless assignment to variable - `opts`. opts = Net::SSH::Config.for(dest_host, ["#{NAGIOS_HOME}/.ssh/config"]) needs_gateway = false if !$opts.has_key?(:proxy)rescue Errno::EACCES => e Syslog.log(Syslog::LOG_NOTICE, "Couldn't read #{NAGIOS_HOME}/.ssh/config: #{e.message}") puts "Couldn't read #{NAGIOS_HOME}/.ssh/config: #{e.message}" exit 3end full_cmd = output = nilbegin if !needs_gateway puts "No SSH gateway configured for #{dest_host}, running #{cmd} directly" if verbose cmd = $opts[:directcommand] if $opts[:directcommand_given] full_cmd = cmd else gateway = nil if verbose gateway = Net::SSH::Gateway.new( nat_host, nil, :config => ["#{NAGIOS_HOME}/.ssh/config"], :keys_only => true, :logger => logger, :auth_methods => ['publickey'], :use_agent => false, :verbose => :debug ) else gateway = Net::SSH::Gateway.new( nat_host, nil, :config => ["#{NAGIOS_HOME}/.ssh/config"], :keys_only => true, :auth_methods => ['publickey'], :use_agent => false ) end port = gateway.open("127.0.0.1", dest_port) if port_arg.empty? full_cmd = cmd else full_cmd = "#{cmd} #{port_arg} #{port}" end if verbose puts "Opening gateway to #{dest_host}:#{dest_port} by tunneling local #{port} to #{nat_host} and running #{full_cmd}" Syslog.log(Syslog::LOG_NOTICE, "Opening gateway to #{dest_host}:#{dest_port} by tunneling local #{port} to #{nat_host} and running #{full_cmd}") end end output = %x{#{full_cmd} 2>&1} to_return = $?.exitstatus > 3 ? 3 : $?.exitstatus puts outputrescue Net::SSH::AuthenticationFailed, Net::SSH::ConnectionTimeout => e Syslog.log(Syslog::LOG_NOTICE, e.message) puts e to_return = 2Avoid rescuing the `Exception` class. Perhaps you meant to rescue `StandardError`?rescue Exception => eRedundant use of `Object#to_s` in interpolation. Syslog.log(Syslog::LOG_NOTICE, "Tunnel failure for #{dest_host}:#{dest_port} from config #{NAGIOS_HOME}/.ssh/config, `#{full_cmd}`: #{e.inspect} **** #{output} **** ENV: #{envhash.to_s}") puts e to_return = 3ensure begin gateway.shutdown! if !gateway.nil?Avoid rescuing the `Exception` class. Perhaps you meant to rescue `StandardError`? rescue Exception => e if verbose puts "Got #{e.inspect} closing down gateway tunnel (remote port may have been dead)" Syslog.log(Syslog::LOG_NOTICE, "Got #{e.inspect} closing down gateway tunnel (remote port may have been dead)") end end if verbose puts "Exiting with status #{to_return}" Syslog.log(Syslog::LOG_NOTICE, "Exiting with status #{to_return}") end exit to_return.to_iend