cloudamatic/mu

View on GitHub
bin/mu-tunnel-nagios

Summary

Maintainability
Test Coverage
#!/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 <<-EOS
A 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 => false
end
 
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"
end
nat_host = dest_host if !nat_host
 
ENV.clear
ENV['HOME'] = NAGIOS_HOME
 
needs_gateway = true
begin
Useless 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 3
end
 
full_cmd = output = nil
begin
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 output
rescue Net::SSH::AuthenticationFailed, Net::SSH::ConnectionTimeout => e
Syslog.log(Syslog::LOG_NOTICE, e.message)
puts e
to_return = 2
Avoid rescuing the `Exception` class. Perhaps you meant to rescue `StandardError`?
rescue Exception => e
Redundant 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 = 3
ensure
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_i
end