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 +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: []
|