capistrano_recia 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +64 -0
- data/RELEASE.rdoc +9 -0
- data/lib/capistrano_recia.rb +9 -0
- data/lib/capistrano_recia/filter.rb +91 -0
- data/lib/capistrano_recia/parents.rb +77 -0
- data/lib/capistrano_recia/password.rb +7 -0
- data/lib/capistrano_recia/password/manager.rb +143 -0
- metadata +120 -0
data/README.rdoc
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
= RECIA Capistrano
|
2
|
+
|
3
|
+
This gem is used by the recia to implements specific behavior in capistrano. It furnish the following services :
|
4
|
+
|
5
|
+
- parents : call of hosts' parents to execute action on them
|
6
|
+
- filters : filter hosts to execute by its roles
|
7
|
+
- password manager : help to generate password and secure them.
|
8
|
+
|
9
|
+
== Parents
|
10
|
+
|
11
|
+
The function is to execute command on the parent host of current hosts. To do this, we need the gem "nagios_mklivestatus".
|
12
|
+
|
13
|
+
To call the function from a task, we need to call a specific method :
|
14
|
+
|
15
|
+
run_parent(cmd)
|
16
|
+
|
17
|
+
This will select only the servers' parents containing the guest (select parents which are servers).
|
18
|
+
There are some notable informations.
|
19
|
+
|
20
|
+
The method works as the standard run, except that the ENV['HOSTS'] are remplaced by their parents.
|
21
|
+
The command is replaced by its Capistrano::Command::Script version and the original hosts are ordered by their parents and given to the script.
|
22
|
+
If some variables are encountered, a loop is created to go through all guest of each host (parents).
|
23
|
+
|
24
|
+
Those are for functionality purpose if you need to execute command with guest as variables you should use this :
|
25
|
+
|
26
|
+
$PARENT:GUEST$ : variable that will be replaced by the name of the guest (we'll create a loop in script)
|
27
|
+
$PARENT:LOOP:START$ : variable that will be replaced by the start of the loop (if not defined and $PARENT:GUEST$ exists, its automatically placed at the start of the command)
|
28
|
+
$PARENT:LOOP:END$ : variable that will be replaced by the end of the loop (if not defined and $PARENT:GUEST$ exists, its automatically placed at the end of the command)
|
29
|
+
|
30
|
+
== Filters
|
31
|
+
|
32
|
+
This module is made to filter hosts with the roles in task options. It furnish 2 methods for task to run :
|
33
|
+
|
34
|
+
run_filter_roles(cmd) # the host must match one of the roles (connection SSH)
|
35
|
+
run_telnet_filter_roles(cmd) # the host must match one of the roles (connection Telnet)
|
36
|
+
|
37
|
+
If hosts does not correspond to the filter, they are removed from the list (with informations display) and the command is executed on the remaining hosts.
|
38
|
+
|
39
|
+
== Password manager
|
40
|
+
|
41
|
+
This is a class which help to manage password security inside capistrano. It's help to generate password (with save in a crypted file), and check the corruption of this password, plus it can securize all the password through a security automaton
|
42
|
+
|
43
|
+
To use the manager:
|
44
|
+
|
45
|
+
require 'capistrano_recia'
|
46
|
+
manager = Capistrano::Password::Manager.new(<file_path>)
|
47
|
+
# generate pass
|
48
|
+
new_pass = manager.generate
|
49
|
+
# generate and save
|
50
|
+
new_pass = manager.generate("key")
|
51
|
+
|
52
|
+
# test corruption
|
53
|
+
is_corrupt = manager.is_corrupted? "key"
|
54
|
+
|
55
|
+
# corrupt password (giving it to a non authorized person
|
56
|
+
pass = manager.corrupt "key"
|
57
|
+
|
58
|
+
# live the password uncorrupted when asking for it
|
59
|
+
pass = manager.live_uncorrupt "key"
|
60
|
+
|
61
|
+
# securize all corrupted password
|
62
|
+
# securized_pass is a hash of key => password
|
63
|
+
securized_pass = manager.securize
|
64
|
+
|
data/RELEASE.rdoc
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
require 'capistrano'
|
2
|
+
require 'capistrano_telnet'
|
3
|
+
require 'capistrano_supports'
|
4
|
+
require 'nagios_mklivestatus'
|
5
|
+
|
6
|
+
load File.join(File.dirname(__FILE__), File.basename(__FILE__, ".rb"), "parents.rb")
|
7
|
+
load File.join(File.dirname(__FILE__), File.basename(__FILE__, ".rb"), "filter.rb")
|
8
|
+
load File.join(File.dirname(__FILE__), File.basename(__FILE__, ".rb"), "password.rb")
|
9
|
+
|
@@ -0,0 +1,91 @@
|
|
1
|
+
##
|
2
|
+
# override the actions invocation class of capistrano
|
3
|
+
# to add the run_parent method and give access to it inside task definition
|
4
|
+
##
|
5
|
+
Capistrano::Configuration::Actions::Invocation.class_eval do
|
6
|
+
|
7
|
+
##
|
8
|
+
# Check if host contains task
|
9
|
+
##
|
10
|
+
def host_as_task_role(current_host)
|
11
|
+
# Active From debug Only !
|
12
|
+
# puts "DEBUG :: #{current_host}"
|
13
|
+
|
14
|
+
roles_ok_from_host = nil
|
15
|
+
|
16
|
+
current_task.options[:roles].each do |task_role|
|
17
|
+
|
18
|
+
# Active From debug Only !
|
19
|
+
# puts "DEBUG :: #{task_role.to_s} ::"
|
20
|
+
roles[:"#{task_role.to_s}"].servers.each do |server|
|
21
|
+
# Active From debug Only !
|
22
|
+
# puts "#{server}"
|
23
|
+
if server.host.to_s == current_host.to_s
|
24
|
+
roles_ok_from_host = 1
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
if roles_ok_from_host != nil && roles_ok_from_host != 0
|
29
|
+
return 1
|
30
|
+
else
|
31
|
+
return 0
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
##
|
36
|
+
# Use the role task option to filter the server with this role
|
37
|
+
##
|
38
|
+
def run_filter_roles(cmd, options={}, &block)
|
39
|
+
|
40
|
+
block ||= self.class.default_io_proc
|
41
|
+
|
42
|
+
hosts_server = find_servers_for_task(current_task)
|
43
|
+
hosts = Array.new
|
44
|
+
|
45
|
+
## calcul des rôles de la tâche
|
46
|
+
if current_task.options[:roles].kind_of? Array
|
47
|
+
current_task.options[:roles].each do |task_role|
|
48
|
+
task_roles = "#{task_roles} #{task_role}"
|
49
|
+
end
|
50
|
+
else
|
51
|
+
task_roles = "#{current_task.options[:roles]}"
|
52
|
+
current_task.options[:roles] = [current_task.options[:roles]]
|
53
|
+
end
|
54
|
+
|
55
|
+
# redistribution des machines par hôte
|
56
|
+
hosts_server.each do |server|
|
57
|
+
if host_as_task_role(server).to_i == 0
|
58
|
+
logger.info "Ce Serveur (#{server}) n'a pas un des role de la tache :#{task_roles}"
|
59
|
+
else
|
60
|
+
hosts.push(server.to_s)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
if hosts != nil and not hosts.empty?
|
65
|
+
|
66
|
+
# save remaining hosts as the callee
|
67
|
+
ENV['HOSTS'] = hosts.join(',')
|
68
|
+
|
69
|
+
# execute action.
|
70
|
+
tree = Capistrano::Command::Tree.new(self) { |t| t.else(cmd, &block) }
|
71
|
+
run_tree(tree, options)
|
72
|
+
else
|
73
|
+
logger.important "Aucun serveurs ne possede un role : #{task_roles}"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
##
|
78
|
+
# Use the role task option to filter the server with this role and run a telnet method
|
79
|
+
##
|
80
|
+
def run_telnet_filter_roles(cmd, options={}, &block)
|
81
|
+
|
82
|
+
set(:telnet, "true")
|
83
|
+
options[:shell] = false
|
84
|
+
|
85
|
+
run_filter_roles(cmd, options, &block)
|
86
|
+
|
87
|
+
unset(:telnet)
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
##
|
2
|
+
# Include Nagios Query Helper to Capistrano::Configuration
|
3
|
+
# in order to access helper from methods.
|
4
|
+
##
|
5
|
+
Capistrano::Configuration.class_eval do
|
6
|
+
include Nagios::MkLiveStatus::QueryHelper
|
7
|
+
end
|
8
|
+
|
9
|
+
##
|
10
|
+
# override the actions invocation class of capistrano
|
11
|
+
# to add the run_parent method and give access to it inside task definition
|
12
|
+
##
|
13
|
+
Capistrano::Configuration::Actions::Invocation.class_eval do
|
14
|
+
|
15
|
+
##
|
16
|
+
# Calculate the parent server of those in parameters and execute the command on them.
|
17
|
+
# We can loop through the child by using:
|
18
|
+
# - $PARENT:GUEST$ : required if we loop through guests, its replaced by the child name of the host.
|
19
|
+
# - $PARENT:LOOP:START$ : (opt.) start of the loop if not defined its automatically positionned at the beginning of the command
|
20
|
+
# - $PARENT:LOOP:END$ : (opt.) end of the loop if not defined its automatically positionned at the end of the command
|
21
|
+
def run_parent(cmd, options={}, &block)
|
22
|
+
|
23
|
+
block ||= self.class.default_io_proc
|
24
|
+
|
25
|
+
guest_servers = find_servers_for_task(current_task)
|
26
|
+
hosts = Hash.new
|
27
|
+
|
28
|
+
# redistribution des machines par hôte
|
29
|
+
guest_servers.each do |server|
|
30
|
+
guest = server.to_s
|
31
|
+
mklive = Nagios::MkLiveStatus::Request.new(fetch(:nagios_path, nil))
|
32
|
+
query = Nagios::MkLiveStatus::Query.new()
|
33
|
+
query.get "hosts"
|
34
|
+
query.addColumn "parents"
|
35
|
+
query.addFilter nagmk_filter("host_name", "=", guest)
|
36
|
+
|
37
|
+
result = mklive.query(query)
|
38
|
+
|
39
|
+
parent = result.split("\n")[0] if result != nil
|
40
|
+
|
41
|
+
if parent == nil or parent.empty? or parent.match(/^rca/)
|
42
|
+
logger.important "Ce serveur (#{guest}) n'est pas une machine virtuelle : Aucun serveur hote trouve pour la machine"
|
43
|
+
else
|
44
|
+
if not hosts.key?(parent)
|
45
|
+
hosts[parent] = Array.new
|
46
|
+
end
|
47
|
+
hosts[parent].push(guest)
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
# save parent hosts as the callee
|
53
|
+
ENV['HOSTS'] = hosts.keys.join(',')
|
54
|
+
|
55
|
+
# create a command script.
|
56
|
+
cmd = Capistrano::Command::Script.new("#{cmd.gsub(/\\/, '\\').gsub(/\"/,'\"')}") if cmd.kind_of? String
|
57
|
+
cmd['host'] = hosts
|
58
|
+
|
59
|
+
## replace vars if they are encountered
|
60
|
+
loop_start = "<% prop('host', Hash.new)['$CAPISTRANO:HOST$'].each do |guest| %>"
|
61
|
+
loop_end = "<% end %>"
|
62
|
+
loop_guest = "<%= guest %>"
|
63
|
+
|
64
|
+
if cmd.include? "$PARENT:GUEST$"
|
65
|
+
cmd.contents.insert(0, "$PARENT:LOOP:START$\n") if not cmd.include? "$PARENT:LOOP:START"
|
66
|
+
cmd.contents.insert(-1, "\n$PARENT:LOOP:END$") if not cmd.include? "$PARENT:LOOP:END"
|
67
|
+
end
|
68
|
+
|
69
|
+
cmd.gsub(/\$PARENT:LOOP:START\$/, loop_start)
|
70
|
+
cmd.gsub(/\$PARENT:LOOP:END\$/, loop_end)
|
71
|
+
cmd.gsub(/\$PARENT:GUEST\$/, loop_guest)
|
72
|
+
|
73
|
+
# execute action.
|
74
|
+
tree = Capistrano::Command::Tree.new(self) { |t| t.else(cmd, &block) }
|
75
|
+
run_tree(tree, options)
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
class Capistrano::Password::Manager
|
3
|
+
|
4
|
+
require 'yaml'
|
5
|
+
require 'encrypted_strings'
|
6
|
+
require 'keepass/password'
|
7
|
+
|
8
|
+
|
9
|
+
# constructor must determine the path to the save file.
|
10
|
+
# if we need to change the keepass regexp, the second parameter is used for it (default [S]20)
|
11
|
+
def initialize(file_path, keepass = "[S]{20}")
|
12
|
+
@file_path = file_path
|
13
|
+
@salt = "recia_password"
|
14
|
+
@keepass_regex = keepass
|
15
|
+
end
|
16
|
+
|
17
|
+
# to use only in a secure context (batch, admins who should know the pass)
|
18
|
+
def live_uncorrupt(save_key)
|
19
|
+
pass = nil
|
20
|
+
# load yaml file
|
21
|
+
yaml = load_file
|
22
|
+
# if yaml file contains informations
|
23
|
+
if yaml and yaml.has_key? save_key
|
24
|
+
# get the password
|
25
|
+
pass = yaml[save_key]["password"]
|
26
|
+
end
|
27
|
+
|
28
|
+
# decrypt the password
|
29
|
+
if pass
|
30
|
+
pass = pass.decrypt(:symmetric, :password => @salt)
|
31
|
+
end
|
32
|
+
|
33
|
+
# return password
|
34
|
+
return pass
|
35
|
+
end
|
36
|
+
|
37
|
+
# permet de savoir si le mot de passe est corrompu
|
38
|
+
def is_corrupted?(save_key)
|
39
|
+
# load yaml file
|
40
|
+
yaml = load_file
|
41
|
+
if yaml and yaml.has_key? save_key
|
42
|
+
# check if password is corrupted
|
43
|
+
corrupt = yaml[save_key]["corrupt"]
|
44
|
+
if corrupt.to_s == "true"
|
45
|
+
return true
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
return false
|
50
|
+
end
|
51
|
+
|
52
|
+
# permet de récupérer les mots de passe corrompus
|
53
|
+
def corrupted()
|
54
|
+
list = Array.new
|
55
|
+
|
56
|
+
yaml = load_file
|
57
|
+
|
58
|
+
yaml.keys.each do |key|
|
59
|
+
if is_corrupted? key
|
60
|
+
list.push key
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
list
|
65
|
+
end
|
66
|
+
|
67
|
+
# permet de récupérer un mot de passe si sa clé de sauvegarde est enregistré, sinon retourne nil
|
68
|
+
def corrupt(save_key)
|
69
|
+
# get passwd
|
70
|
+
pass = live_uncorrupt save_key
|
71
|
+
|
72
|
+
# load yaml file
|
73
|
+
yaml = load_file
|
74
|
+
|
75
|
+
# if yaml file contains informations
|
76
|
+
if yaml and pass
|
77
|
+
# set password to corrupted
|
78
|
+
yaml[save_key]["corrupt"] = true
|
79
|
+
|
80
|
+
# save the information of corruption to the file
|
81
|
+
File.open(@file_path, "w") do |io|
|
82
|
+
io.write(yaml.to_yaml)
|
83
|
+
io.close
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# return password
|
88
|
+
return pass
|
89
|
+
end
|
90
|
+
|
91
|
+
# permet de générer un mot de passe.
|
92
|
+
# le premier paramètre indique si nous devons sauvegarder ou non le mot de passe (nil ou "" aucune sauvegarde)
|
93
|
+
# le deuxième paramètre est celui des options de keepass.
|
94
|
+
def generate(save_key="")
|
95
|
+
|
96
|
+
# generate a password without lookalikes (l, I, !, ...)
|
97
|
+
pass = KeePass::Password.generate(@keepass_regex, :remove_lookalikes => true)
|
98
|
+
|
99
|
+
# crypt the password
|
100
|
+
enc_pass = pass.encrypt(:symmetric, :password => @salt)
|
101
|
+
enc_pass.send :remove_instance_variable, :@cipher
|
102
|
+
|
103
|
+
# if a save key is passed
|
104
|
+
if save_key and not save_key.empty?
|
105
|
+
# load yaml file
|
106
|
+
yaml = load_file
|
107
|
+
yaml[save_key] = Hash.new if not yaml.has_key? save_key
|
108
|
+
# add password to file
|
109
|
+
yaml[save_key]["password"] = enc_pass
|
110
|
+
yaml[save_key]["corrupt"] = false
|
111
|
+
# save file
|
112
|
+
File.open(@file_path, "w") do |io|
|
113
|
+
io.write(yaml.to_yaml)
|
114
|
+
io.close
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
return pass, enc_pass
|
119
|
+
|
120
|
+
end
|
121
|
+
|
122
|
+
# regenerate all corrupted password. and return all the password which are regenerated by keys
|
123
|
+
def securize()
|
124
|
+
|
125
|
+
securized = Hash.new
|
126
|
+
|
127
|
+
# for each corrupted passwd
|
128
|
+
corrupted.each do |key|
|
129
|
+
securized[key] = Hash.new
|
130
|
+
securized[key]['old'] = corrupt(key)
|
131
|
+
securized[key]['new'], crypted = generate(key)
|
132
|
+
end
|
133
|
+
|
134
|
+
securized
|
135
|
+
end
|
136
|
+
|
137
|
+
private
|
138
|
+
def load_file()
|
139
|
+
yaml = Hash.new
|
140
|
+
yaml = YAML::load_file(@file_path) if File.exists? @file_path
|
141
|
+
yaml
|
142
|
+
end
|
143
|
+
end
|
metadata
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: capistrano_recia
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Esco-lan Team
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-08-03 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: capistrano
|
16
|
+
requirement: &10927620 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 2.11.2
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *10927620
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: capistrano_telnet
|
27
|
+
requirement: &10927080 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 0.0.1
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *10927080
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: capistrano_supports
|
38
|
+
requirement: &10926320 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 0.0.1
|
44
|
+
type: :runtime
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *10926320
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: nagios_mklivestatus
|
49
|
+
requirement: &10925540 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.0.11
|
55
|
+
type: :runtime
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *10925540
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: keepass-password-generator
|
60
|
+
requirement: &10924860 !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ! '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: 0.1.1
|
66
|
+
type: :runtime
|
67
|
+
prerelease: false
|
68
|
+
version_requirements: *10924860
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: encrypted_strings
|
71
|
+
requirement: &10923940 !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ! '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: 0.3.3
|
77
|
+
type: :runtime
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: *10923940
|
80
|
+
description: RECIA Capistrano Support
|
81
|
+
email: team@esco-lan.org
|
82
|
+
executables: []
|
83
|
+
extensions: []
|
84
|
+
extra_rdoc_files:
|
85
|
+
- README.rdoc
|
86
|
+
- RELEASE.rdoc
|
87
|
+
files:
|
88
|
+
- lib/capistrano_recia/filter.rb
|
89
|
+
- lib/capistrano_recia/password.rb
|
90
|
+
- lib/capistrano_recia/password/manager.rb
|
91
|
+
- lib/capistrano_recia/parents.rb
|
92
|
+
- lib/capistrano_recia.rb
|
93
|
+
- README.rdoc
|
94
|
+
- RELEASE.rdoc
|
95
|
+
homepage: https://github.com/RECIA/capistrano_recia
|
96
|
+
licenses: []
|
97
|
+
post_install_message:
|
98
|
+
rdoc_options: []
|
99
|
+
require_paths:
|
100
|
+
- lib
|
101
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
102
|
+
none: false
|
103
|
+
requirements:
|
104
|
+
- - ! '>='
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: '0'
|
107
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
108
|
+
none: false
|
109
|
+
requirements:
|
110
|
+
- - ! '>='
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
version: '0'
|
113
|
+
requirements: []
|
114
|
+
rubyforge_project:
|
115
|
+
rubygems_version: 1.8.8
|
116
|
+
signing_key:
|
117
|
+
specification_version: 3
|
118
|
+
summary: Provides additionnal methods to capistrano like run_parent, run_filter_role_contained
|
119
|
+
and run_filter_role_uncontained
|
120
|
+
test_files: []
|