myaxes 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/bin/myaxes ADDED
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "myaxes"
4
+
5
+ AXES_VER = "0.1.0 alpha"
6
+ AXES_AUTHORS = "InViZz"
7
+
8
+ ################################################################################
9
+ puts "------------------------------------------------------------------------------------ "
10
+ puts " @ @ "
11
+ puts " @@ @@ "
12
+ puts " @@@@ @@@@ "
13
+ puts " @@@@@ @@@@@ "
14
+ puts " @@@@@@ @@@ @@@@@@ "
15
+ puts " @@@@@@@ @@@ @@@@@@@ "
16
+ puts " @@@@@@@@@@@@@@@@@@@@@@@@@@@ "
17
+ puts " @@@@@@@@@@@@@@@@@@@@@@@@@@@ "
18
+ puts " @@@@@@@ @@@ @@@@@@@ "
19
+ puts " @@@@@@ @@@ @@@@@@ "
20
+ puts "@@@@@@@@@@ @@@ @@@ @@@@@ @@@ @@@@@ @@@@@@ @@@ @@@ @@@@@@@@ @@@@@@ "
21
+ puts "@@@@@@@@@@@ @@@ @@@ @@@@ @@@ @@@@ @@@@@@@@ @@@ @@@ @@@@@@@@ @@@@@@@ "
22
+ puts "@@! @@! @@! @@! !@@ @@ @@@ @@ @@! @@@ @@! !@@ @@! !@@ "
23
+ puts "!@! !@! !@! !@! @!! @ @@@ @ !@! @!@ !@! @!! !@! !@! "
24
+ puts "@!! !!@ @!@ !@!@! @@@ @!@!@!@! !@@!@! @!!!:! !!@@!! "
25
+ puts "!@! ! !@! @!!! @@@ !!!@!!!! @!!! !!!!!: !!@!!! "
26
+ puts "!!: !!: !!: @@@ !!: !!! !: :!! !!: !:! "
27
+ puts ":!: :!: :!: @@@ :!: !:! :!: !:! :!: !:! "
28
+ puts "::: :: :: @@@ :: ::: :: ::: :: :::: :::: :: "
29
+ puts " : : : @@@ : : : : :: : :: :: :: : : "
30
+ puts " @@@@@ "
31
+ puts "#{AXES_VER} by #{AXES_AUTHORS} @@@@@ "
32
+ puts " @@@@@ "
33
+ puts "------------------------------------------------------------------------------------"
34
+ ################################################################################
35
+ $DEBUG = false
36
+
37
+ myaxes = MyAxes.new('/etc/myaxes.yml')
38
+ myaxes.start
data/lib/myaxes.rb ADDED
@@ -0,0 +1,145 @@
1
+ require "rubygems"
2
+ require "net/ssh"
3
+ require "net/ssh/gateway"
4
+ require "log4r"
5
+ require 'myaxes/axeconfig'
6
+ require 'myaxes/options'
7
+
8
+ class MyAxes
9
+
10
+ include Log4r
11
+
12
+ def initialize(config='~/.myaxes.yml')
13
+ @logger = Logger.new('MyAxes')
14
+ @logger.outputters = Outputter.stdout
15
+
16
+ @options = Options.new.parse
17
+ @logger.debug "test #{@options}" if $DEBUG
18
+
19
+ @conf = AxeConfig.new(@options)
20
+ @config = @conf.read(config)
21
+ @targets = @conf.targets
22
+ @threads = []
23
+
24
+ @ssh_options ={
25
+ :port => @config['Global']['ssh_port'],
26
+ :verbose => @config['Global']['debug_level'].to_sym,
27
+ :auth_methods => %w(publickey password keyboard-interactive),
28
+ :keys => @config['Global']['ssh_keys'],
29
+ :password => @config['Global']['password']
30
+ }
31
+ @commands_proc = Proc.new { |session, hostname|
32
+ @targets[hostname].each do |query|
33
+ @logger.debug "Query: #{query}" if $DEBUG
34
+ name = hostname.chomp.split(".")[0]
35
+ cmd = "mysql -u #{@config['Targets'][name]['login']} -e '#{query}' -p"
36
+ output = self.exec(session,cmd,name)
37
+ puts "\033[0;32m[*] #{hostname}\033[0m: #{output}"
38
+ end
39
+ }
40
+
41
+ end
42
+
43
+ def start
44
+ @targets.each_key do |hostname|
45
+ if self.use_gw?
46
+ @threads << Thread.new {
47
+ self.via_gw do |jump_server|
48
+ begin
49
+ jump_server.ssh(hostname, @config['Global']['login'], @ssh_options) do |session|
50
+ @commands_proc.call(session,hostname)
51
+ end
52
+ rescue Net::SSH::Disconnect => errmsg
53
+ warn "#{hostname} : #{errmsg}"
54
+ rescue Net::SSH::AuthenticationFailed => errmsg
55
+ warn "#{hostname} : #{errmsg}"
56
+ rescue Errno::ETIMEDOUT => errmsg
57
+ warn "#{hostname} : #{errmsg}"
58
+ rescue Errno::ECONNREFUSED => errmsg
59
+ warn "#{hostname} : #{errmsg}"
60
+ end
61
+ end
62
+ }
63
+ else
64
+ @threads << Thread.new {
65
+ begin
66
+ Net::SSH.start(hostname, @config['Global']['login'], @ssh_options) do |session|
67
+ @commands_proc.call(session,hostname)
68
+ end
69
+ rescue Net::SSH::Disconnect => errmsg
70
+ warn "#{hostname} : #{errmsg}"
71
+ rescue Net::SSH::AuthenticationFailed => errmsg
72
+ warn "#{hostname} : #{errmsg}"
73
+ rescue Errno::ETIMEDOUT => errmsg
74
+ warn "#{hostname} : #{errmsg}"
75
+ rescue Errno::ECONNREFUSED => errmsg
76
+ warn "#{hostname} : #{errmsg}"
77
+ end
78
+ }
79
+ end
80
+ end
81
+
82
+ @threads.each {|thread|
83
+ thread.join
84
+ }
85
+ end
86
+
87
+ def via_gw
88
+ begin
89
+ jump_server = Net::SSH::Gateway.new(@config['Global']['jump_server'], @config['Global']['login'], @ssh_options)
90
+
91
+ @logger.debug "port forwarding ok" if $DEBUG
92
+
93
+ yield jump_server
94
+
95
+ rescue Net::SSH::Disconnect => errmsg
96
+ warn "Gateway : #{errmsg}"
97
+ rescue Net::SSH::AuthenticationFailed => errmsg
98
+ warn "Gateway : #{errmsg}"
99
+ rescue Errno::ETIMEDOUT => errmsg
100
+ warn "Gateway : #{errmsg}"
101
+ rescue Errno::ECONNREFUSED => errmsg
102
+ warn "Gateway : #{errmsg}"
103
+ end
104
+
105
+ end
106
+
107
+ def use_gw?
108
+ @config['Global']['use_jump']
109
+ end
110
+
111
+ def exec(session,cmd,name)
112
+ channel = session.open_channel do |channel|
113
+ channel.request_pty do |ch, success|
114
+ raise "Could not obtain pty (i.e. an interactive ssh session)" if !success
115
+ end
116
+ channel.exec(cmd) do |ch, success|
117
+ die "could not execute command" unless success
118
+ channel.on_data do |ch, data|
119
+ if data == "Enter password: "
120
+ @logger.debug "DEBUG: Password request" if $DEBUG
121
+ channel.send_data "#{@config['Targets'][name]['password']}\n"
122
+ else
123
+ channel[:result] ||= ""
124
+ channel[:result] << data
125
+ end
126
+ end
127
+
128
+ channel.on_extended_data do |ch, type, data|
129
+ raise "SSH command returned on stderr: #{data}"
130
+ end
131
+ end
132
+ end
133
+
134
+ # Nothing has actually happened yet. Everything above will respond to the
135
+ # server after each execution of the ssh loop until it has nothing left
136
+ # to process. For example, if the above recieved a password challenge from
137
+ # the server, ssh's exec loop would execute twice - once for the password,
138
+ # then again after clearing the password (or twice more and exit if the
139
+ # password was bad)
140
+ channel.wait
141
+
142
+ return channel[:result] # it returns with \r\n at the end
143
+ end
144
+
145
+ end
@@ -0,0 +1,40 @@
1
+ require "yaml"
2
+
3
+ class AxeConfig < Hash
4
+
5
+ attr_reader :targets, :config
6
+
7
+ def initialize(options)
8
+ @config = Hash.new
9
+ @options = options
10
+ @targets = Hash.new
11
+ end
12
+
13
+ def read(config)
14
+ user = @options['user']
15
+ ip = @options['ip']
16
+ #password = @options['password']
17
+ group = @options['group']
18
+
19
+ puts "Configfile (#{config}) not found" unless File.readable?(config)
20
+
21
+ begin
22
+ @config = YAML.load_file(config)
23
+ rescue Exception => errmsg
24
+ puts "Configfile format error: #{errmsg}"
25
+ end
26
+
27
+ @config['Targets'].each_key do |target|
28
+ if @config['Targets'][target]['query'].include? group
29
+ @config['Targets'][target]['query'][group].each do |query|
30
+ query.to_s.gsub!('{ip}', ip).gsub!('{user}', user)
31
+ end
32
+ @targets["#{@config['Targets'][target]['hostname']}"] = @config['Targets'][target]['query'][group]
33
+ end
34
+ end
35
+
36
+ self.config
37
+ end
38
+
39
+
40
+ end
@@ -0,0 +1,40 @@
1
+ require "optparse"
2
+
3
+ class Options
4
+
5
+ attr_reader :options
6
+
7
+ def initialize
8
+ @options = Hash.new
9
+
10
+ @options['mode'] = 'log'
11
+ @options['log'] = $stdout
12
+
13
+ end
14
+
15
+ def parse
16
+ OptionParser.new do |opt|
17
+ opt.separator ""
18
+ opt.separator "Usage: myaxes -g <group> -a <ip> -u <username> -p <password>[-H <hostnames>]"
19
+ opt.separator ""
20
+ opt.separator "shell> myaxes -g web -a 192.168.0.101 -u web301 -p qwerty"
21
+ opt.separator ""
22
+ opt.separator ""
23
+ opt.separator "Options:"
24
+ opt.separator "-------------"
25
+ opt.on('-g', '--group GROUPNAME', 'Group from config') { |val| @options['group'] = val }
26
+ opt.on('-a', '--address IP-ADDRESS', 'Ip address') { |val| @options['ip'] = val }
27
+ opt.on('-u', '--user USERNAME', 'Username') { |val| @options['user'] = val }
28
+ opt.on('-p', '--password PASSWORD', 'Password') { |val| @options['password'] = val }
29
+ opt.on('-H', '--hosts HOSTNAME_X,HOSTNAME_Y,HOSTNAME_Z', Array, 'Other Hosts(not from config)') { |val| @options['hosts'] = val }
30
+ opt.separator ""
31
+ opt.on_tail('-h', '--help') { puts opt; exit }
32
+
33
+ opt.parse!
34
+ end
35
+
36
+ self.options
37
+
38
+ end
39
+
40
+ end
metadata ADDED
@@ -0,0 +1,49 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: myaxes
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - InViZz
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-02-08 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: Simple console app for remote execute mysql queries
15
+ email: morion.estariol@gmail.com
16
+ executables:
17
+ - myaxes
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - lib/myaxes.rb
22
+ - lib/myaxes/axeconfig.rb
23
+ - lib/myaxes/options.rb
24
+ - bin/myaxes
25
+ homepage: https://github.com/InViZz/MyAxes
26
+ licenses: []
27
+ post_install_message:
28
+ rdoc_options: []
29
+ require_paths:
30
+ - lib
31
+ required_ruby_version: !ruby/object:Gem::Requirement
32
+ none: false
33
+ requirements:
34
+ - - ! '>='
35
+ - !ruby/object:Gem::Version
36
+ version: '0'
37
+ required_rubygems_version: !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ! '>='
41
+ - !ruby/object:Gem::Version
42
+ version: '0'
43
+ requirements: []
44
+ rubyforge_project:
45
+ rubygems_version: 1.8.10
46
+ signing_key:
47
+ specification_version: 3
48
+ summary: MyAxes
49
+ test_files: []