myaxes 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/myaxes +38 -0
- data/lib/myaxes.rb +145 -0
- data/lib/myaxes/axeconfig.rb +40 -0
- data/lib/myaxes/options.rb +40 -0
- metadata +49 -0
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: []
|