dust-deploy 0.15.2 → 0.16.0

Sign up to get free protection for your applications and to get access to all the features.
data/changelog.md CHANGED
@@ -1,6 +1,36 @@
1
1
  Changelog
2
2
  =============
3
3
 
4
+ 0.16.0
5
+ ------------
6
+
7
+ - fixes an issue where the user was set to the sudo user when using node.scp, now defaults to root:root
8
+ - updates and fixes for cjdroute recipe
9
+ - adds node.selinuxenabled? and node.chcon
10
+ - users recipe now changes selinux content of home and ssh dirs
11
+ - improves motd recipe, now supports update-motd as well. there are three possible ways of maintaining the motd now:
12
+ using a motd string in the config file, and static file or a ERB template file. for more information see:
13
+ https://github.com/kechagia/dust-deploy/wiki/motd
14
+
15
+ - introduces node.get_gid()
16
+ - users recipe now chowns to primary gid
17
+ - users recipe now checks if public_keys.yaml and the requested user is actually present
18
+ - removes the leading 10- from sysctl.d files, so they can be set individually if needed
19
+ - sysctl recipe only applies rules directly if --restart is given
20
+ - also enables support for multiple files, please migrate
21
+
22
+ recipes:
23
+ # old (deprecated)
24
+ sysctl:
25
+ key: value
26
+
27
+ sysctl:
28
+ # new
29
+ name:
30
+ key: value
31
+ othername: { key: value }
32
+
33
+
4
34
  0.15.2
5
35
  ------------
6
36
 
@@ -45,6 +45,9 @@ module Dust
45
45
  l = [ 'warning', 'failed' ]
46
46
  when 'failed'
47
47
  l = [ 'failed' ]
48
+ else
49
+ puts "WARNING: unknown error level '#{level}', using 'all'".yellow
50
+ l = [ 'none', 'ok', 'warning', 'failed' ]
48
51
  end
49
52
 
50
53
  errors = {}
@@ -9,10 +9,6 @@ class Cjdroute< Recipe
9
9
  return unless install_dependencies
10
10
  return unless get_latest_version
11
11
 
12
- # clean up building directory, if --restart is given
13
- # using --restart, since there's no --cleanup
14
- # return unless make_clean if @options.restart?
15
-
16
12
  # compiling action
17
13
  return unless run_make
18
14
 
@@ -23,8 +19,8 @@ class Cjdroute< Recipe
23
19
  stop_cjdroute
24
20
 
25
21
  # copy binary
26
- return unless @node.mkdir @config['bin_dir']
27
- return unless @node.cp "#{@config['build_dir']}/build/cjdroute", "#{@config['bin_dir']}/cjdroute"
22
+ return unless @node.mkdir(@config['bin_dir'])
23
+ return unless @node.cp("#{@config['build_dir']}/cjdroute", "#{@config['bin_dir']}/cjdroute")
28
24
 
29
25
  start_cjdroute
30
26
  end
@@ -103,23 +99,23 @@ class Cjdroute< Recipe
103
99
  def install_dependencies
104
100
  @node.messages.add("installing build dependencies\n")
105
101
 
106
- return false unless @node.install_package 'cmake', :indent => 2
102
+ return false unless @node.install_package('cmake', :indent => 2)
107
103
 
108
104
  # check cmake version
109
- ret = @node.exec 'cmake --version'
105
+ ret = @node.exec('cmake --version')
110
106
  ver = ret[:stdout].match(/2.[0-9]/)[0].to_f
111
107
  return @node.messages.add('cjdroute requires cmake 2.8 or higher').failed if ver < 2.8
112
108
 
113
109
 
114
110
  if @node.uses_apt?
115
- return false unless @node.install_package 'git-core', :indent => 2
116
- return false unless @node.install_package 'build-essential', :indent => 2
117
- return false unless @node.install_package 'psmisc', :indent => 2
118
- return false unless @node.install_package 'coreutils', :indent => 2
111
+ return false unless @node.install_package('git-core', :indent => 2)
112
+ return false unless @node.install_package('build-essential', :indent => 2)
113
+ return false unless @node.install_package('psmisc', :indent => 2)
114
+ return false unless @node.install_package('coreutils', :indent => 2)
119
115
  else
120
- return false unless @node.install_package 'git', :indent => 2
121
- return false unless @node.install_package 'gcc', :indent => 2
122
- return false unless @node.install_package 'make', :indent => 2
116
+ return false unless @node.install_package('git', :indent => 2)
117
+ return false unless @node.install_package('gcc', :indent => 2)
118
+ return false unless @node.install_package('make', :indent => 2)
123
119
  end
124
120
 
125
121
  true
@@ -127,10 +123,10 @@ class Cjdroute< Recipe
127
123
 
128
124
  # gets/updates latest version from cjdns git repository
129
125
  def get_latest_version
130
- if @node.dir_exists? @config['build_dir'], :quiet => true
126
+ if @node.dir_exists?(@config['build_dir'], :quiet => true)
131
127
 
132
128
  # check if build directory is maintained by git
133
- unless @node.dir_exists? "#{@config['build_dir']}/.git", :quiet => true
129
+ unless @node.dir_exists?("#{@config['build_dir']}/.git", :quiet => true)
134
130
  return @node.messages.add("#{@config['build_dir']} doesn't appear to be a git repository").failed
135
131
  end
136
132
 
@@ -145,7 +141,7 @@ class Cjdroute< Recipe
145
141
 
146
142
  else
147
143
  # create build directory
148
- unless @node.mkdir @config['build_dir']
144
+ unless @node.mkdir(@config['build_dir'])
149
145
  return @node.messages.add("couldn't create build directory #{@config['build_dir']}").failed
150
146
  end
151
147
 
@@ -164,12 +160,6 @@ class Cjdroute< Recipe
164
160
  true
165
161
  end
166
162
 
167
- # remove and recreate building directory
168
- def make_clean
169
- msg = @node.messages.add('cleaning up')
170
- msg.parse_result(@node.exec("rm -rf #{@config['build_dir']}/build")[:exit_code])
171
- end
172
-
173
163
  def run_make
174
164
  msg = @node.messages.add('compiling cjdns')
175
165
  msg.parse_result(@node.exec("export Log_LEVEL=#{@config['loglevel']}; cd #{@config['build_dir']}; ./do", :live => true)[:exit_code])
@@ -183,11 +173,11 @@ class Cjdroute< Recipe
183
173
  end
184
174
 
185
175
  msg = @node.messages.add('generating config file')
186
- ret = @node.exec("#{@config['bin_dir']}/cjdroute --genconf")
176
+ ret = @node.exec("#{@config['build_dir']}/cjdroute --genconf")
187
177
  return false unless msg.parse_result(ret[:exit_code])
188
178
 
189
179
  # parse generated json
190
- cjdroute_conf = JSON.parse ret[:stdout]
180
+ cjdroute_conf = JSON.parse(ret[:stdout])
191
181
 
192
182
  # add some public peers, so we can get started directly
193
183
  msg = @node.messages.add('adding public peers', :indent => 2)
@@ -197,8 +187,8 @@ class Cjdroute< Recipe
197
187
  # exchange tun0 with configured tun device
198
188
  cjdroute_conf['router']['interface']['tunDevice'] = @config['tun']
199
189
 
200
- return false unless @node.mkdir @config['etc_dir']
201
- return @node.write "#{@config['etc_dir']}/cjdroute.conf", JSON.pretty_generate(cjdroute_conf)
190
+ return false unless @node.mkdir(@config['etc_dir'])
191
+ return @node.write("#{@config['etc_dir']}/cjdroute.conf", JSON.pretty_generate(cjdroute_conf))
202
192
  end
203
193
 
204
194
  # kill any cjdroute processes that might be running
@@ -1,7 +1,41 @@
1
+ require 'erb'
2
+
1
3
  class Motd < Recipe
2
4
  desc 'motd:deploy', 'creates message of the day'
3
- def deploy
4
- @node.deploy_file "#{@template_path}/motd", '/etc/motd', :binding => binding
5
+ def deploy
6
+ return @node.messages.add('no motd or motd template given') unless @config.is_a? String
7
+
8
+ file = "#{@template_path}/#{@config}"
9
+
10
+ # use the file, or file.erb if present
11
+ # if not, use the given string
12
+ if File.exists?(file)
13
+ @node.messages.add("found static motd file '#{File.basename(file)}'").ok
14
+ message = File.read(file)
15
+ elsif File.exists?(file + '.erb')
16
+ @node.messages.add("found template motd file '#{File.basename(file)}.erb'").ok
17
+ message = ERB.new(File.read(file + '.erb'), nil, '%<>').result(binding)
18
+ else
19
+ @node.messages.add("found motd string in config file").ok
20
+ message = ERB.new(@config, nil, '%<>').result(binding)
21
+ end
22
+
23
+ # check if /etc/update-motd.d is present
24
+ if @node.dir_exists?('/etc/update-motd.d', :quiet => true)
25
+ file = '/etc/update-motd.d/50-dust'
26
+ msg = @node.messages.add("update-motd was found, deploying motd to #{file}")
27
+
28
+ # create a simple shellscript that echos the motd, and deploy it
29
+ msg.parse_result(@node.write(file, shellscriptify(message), :quiet => true))
30
+
31
+ # since we've deployed a shellscript, make it executeable
32
+ @node.chmod('0755', file)
33
+
34
+ # not using update-motd, simply modify /etc/motd
35
+ else
36
+ msg = @node.messages.add('deploying message of the day directly to /etc/motd')
37
+ msg.parse_result(@node.write('/etc/motd', message, :quiet => true))
38
+ end
5
39
  end
6
40
 
7
41
  desc 'motd:status', 'shows current message of the day'
@@ -9,6 +43,14 @@ class Motd < Recipe
9
43
  msg = @node.messages.add('getting /etc/motd')
10
44
  ret = @node.exec 'cat /etc/motd'
11
45
  msg.parse_result(ret[:exit_code])
12
- msg.print_output(ret)
13
- end
46
+ msg.parse_output(ret)
47
+ end
48
+
49
+
50
+ private
51
+
52
+ # creates a shellscript echoing string
53
+ def shellscriptify(string)
54
+ "#!/bin/sh\n\ncat <<EOF\n#{string}\nEOF\n"
55
+ end
14
56
  end
@@ -20,25 +20,25 @@ class Postgres < Recipe
20
20
  set_permissions
21
21
 
22
22
  # configure pacemaker profile
23
- if @config['profile'].to_array.include? 'pacemaker'
24
- deploy_pacemaker_script if @node.package_installed? 'pacemaker'
23
+ if @config['profile'].to_array.include?('pacemaker')
24
+ deploy_pacemaker_script if @node.package_installed?('pacemaker')
25
25
  end
26
26
 
27
27
  # configure zabbix profile
28
- if @config['profile'].to_array.include? 'zabbix'
28
+ if @config['profile'].to_array.include?('zabbix')
29
29
  configure_for_zabbix if zabbix_installed?
30
30
  end
31
31
 
32
32
  # reload/restart postgres if command line option is given
33
- @node.restart_service @config['service_name'] if options.restart?
34
- @node.reload_service @config['service_name'] if options.reload?
33
+ @node.restart_service(@config['service_name']) if options.restart?
34
+ @node.reload_service(@config['service_name']) if options.reload?
35
35
  end
36
36
 
37
37
  desc 'postgres:status', 'displays status of postgres cluster'
38
38
  def status
39
- return unless @node.package_installed? [ 'postgresql-server', "postgresql-#{@config['version']}" ]
39
+ return unless @node.package_installed?([ 'postgresql-server', "postgresql-#{@config['version']}" ])
40
40
  set_default_directories
41
- @node.print_service_status @config['service_name']
41
+ @node.print_service_status(@config['service_name'])
42
42
  end
43
43
 
44
44
 
@@ -55,7 +55,7 @@ class Postgres < Recipe
55
55
  return @node.messages.add('os not supported, please specify "package: <package name>" in your config').failed
56
56
  end
57
57
 
58
- @node.install_package package
58
+ @node.install_package(package)
59
59
  end
60
60
 
61
61
  # set conf-dir and data-dir as well as service-name
@@ -85,25 +85,30 @@ class Postgres < Recipe
85
85
 
86
86
  # deploy postgresql.conf, pg_hba.conf and pg_ident.conf
87
87
  def deploy_config
88
- @node.write "#{@config['conf_directory']}/postgresql.conf", generate_postgresql_conf
89
- @node.write "#{@config['conf_directory']}/pg_hba.conf", generate_pg_hba_conf
90
- @node.write "#{@config['conf_directory']}/pg_ident.conf", generate_pg_ident_conf
88
+ @node.write("#{@config['conf_directory']}/postgresql.conf", generate_postgresql_conf)
89
+ @node.write("#{@config['conf_directory']}/pg_hba.conf", generate_pg_hba_conf)
90
+ @node.write( "#{@config['conf_directory']}/pg_ident.conf", generate_pg_ident_conf)
91
91
  end
92
92
 
93
93
  # copy recovery.conf to either recovery.conf or recovery.done
94
94
  # depending on which file already exists.
95
95
  def deploy_recovery
96
- if @node.file_exists? "#{@config['postgresql.conf']['data_directory']}/recovery.conf", :quiet => true
97
- @node.write "#{@config['postgresql.conf']['data_directory']}/recovery.conf", generate_recovery_conf
96
+ if @node.file_exists?("#{@config['postgresql.conf']['data_directory']}/recovery.conf", :quiet => true)
97
+ @node.write("#{@config['postgresql.conf']['data_directory']}/recovery.conf", generate_recovery_conf)
98
98
  else
99
- @node.write "#{@config['postgresql.conf']['data_directory']}/recovery.done", generate_recovery_conf
99
+ @node.write("#{@config['postgresql.conf']['data_directory']}/recovery.done", generate_recovery_conf)
100
100
  end
101
101
  end
102
102
 
103
103
  # deploy certificates to data-dir
104
104
  def deploy_certificates
105
- @node.deploy_file "#{@template_path}/#{@config['server.crt']}", "#{@config['postgresql.conf']['data_directory']}/server.crt", :binding => binding
106
- @node.deploy_file "#{@template_path}/#{@config['server.key']}", "#{@config['postgresql.conf']['data_directory']}/server.key", :binding => binding
105
+ @node.deploy_file("#{@template_path}/#{@config['server.crt']}",
106
+ "#{@config['postgresql.conf']['data_directory']}/server.crt",
107
+ :binding => binding)
108
+
109
+ @node.deploy_file("#{@template_path}/#{@config['server.key']}",
110
+ "#{@config['postgresql.conf']['data_directory']}/server.key",
111
+ :binding => binding)
107
112
  end
108
113
 
109
114
  # default settings for postgresql.conf
@@ -122,10 +127,10 @@ class Postgres < Recipe
122
127
 
123
128
  def generate_postgresql_conf
124
129
  @config['postgresql.conf'] ||= {}
125
- @config['postgresql.conf'] = default_postgres_conf.merge @config['postgresql.conf']
130
+ @config['postgresql.conf'] = default_postgres_conf.merge(@config['postgresql.conf'])
126
131
 
127
132
  # calculate values if dedicated profile is given
128
- profile_dedicated if @config['profile'].to_array.include? 'dedicated'
133
+ profile_dedicated if @config['profile'].to_array.include?('dedicated')
129
134
 
130
135
  postgresql_conf = ''
131
136
  @config['postgresql.conf'].each do |key, value|
@@ -150,18 +155,18 @@ class Postgres < Recipe
150
155
 
151
156
  def generate_pg_hba_conf
152
157
  @config['pg_hba.conf'] ||= [ 'local all postgres trust' ]
153
- @config['pg_hba.conf'].join "\n"
158
+ @config['pg_hba.conf'].join("\n")
154
159
  end
155
160
 
156
161
  def generate_pg_ident_conf
157
162
  @config['pg_ident.conf'] ||= []
158
- @config['pg_ident.conf'].join "\n"
163
+ @config['pg_ident.conf'].join("\n")
159
164
  end
160
165
 
161
166
  # try to find good values (but don't overwrite if set in config file) for
162
167
  # shared_buffers, work_mem and maintenance_work_mem, effective_cache_size and wal_buffers
163
168
  def profile_dedicated
164
- @node.collect_facts :quiet => true
169
+ @node.collect_facts(:quiet => true)
165
170
  system_mem = ::Dust.convert_size(@node['memorysize']).to_f
166
171
 
167
172
  msg = @node.messages.add("calculating recommended settings for a dedicated databse server with #{kb2mb system_mem} ram\n")
@@ -198,22 +203,24 @@ class Postgres < Recipe
198
203
 
199
204
  # give the configured dbuser the data_directory
200
205
  def set_permissions
201
- @node.chown @config['dbuser'], @config['postgresql.conf']['data_directory'] if @config['dbuser']
202
- @node.chmod 'u+Xrw,g-rwx,o-rwx', @config['postgresql.conf']['data_directory']
206
+ if @config['dbuser']
207
+ @node.chown("#{@config['dbuser']}:#{@node.get_gid(@config['dbuser'])}", @config['postgresql.conf']['data_directory'])
208
+ end
209
+ @node.chmod('u+Xrw,g-rwx,o-rwx', @config['postgresql.conf']['data_directory'])
203
210
  end
204
211
 
205
212
  # deploy the pacemaker script
206
213
  def deploy_pacemaker_script
207
- @node.deploy_file "#{@template_path}/pacemaker.sh", "#{@config['conf_directory']}/pacemaker.sh", :binding => binding
208
- @node.chmod '755', "#{@config['conf_directory']}/pacemaker.sh"
214
+ @node.deploy_file("#{@template_path}/pacemaker.sh", "#{@config['conf_directory']}/pacemaker.sh", :binding => binding)
215
+ @node.chmod('755', "#{@config['conf_directory']}/pacemaker.sh")
209
216
  end
210
217
 
211
218
  # check if zabbix is installed
212
219
  def zabbix_installed?
213
220
  if @node.uses_emerge?
214
- return @node.package_installed? 'zabbix', :quiet => true
221
+ return @node.package_installed?('zabbix', :quiet => true)
215
222
  else
216
- return @node.package_installed? 'zabbix-agent', :quiet => true
223
+ return @node.package_installed?('zabbix-agent', :quiet => true)
217
224
  end
218
225
  end
219
226
 
@@ -225,7 +232,7 @@ class Postgres < Recipe
225
232
  msg = @node.messages.add('adding zabbix user to postgres group', :indent => 2)
226
233
  msg.parse_result(@node.exec('usermod -a -G postgres zabbix')[:exit_code])
227
234
 
228
- if is_master? :indent => 2
235
+ if is_master?(:indent => 2)
229
236
  msg = @node.messages.add('checking if zabbix user exists in postgres', :indent => 3)
230
237
  ret = msg.parse_result(@node.exec('psql -U postgres -c ' +
231
238
  ' "SELECT usename FROM pg_user WHERE usename = \'zabbix\'"' +
@@ -243,9 +250,9 @@ class Postgres < Recipe
243
250
  end
244
251
 
245
252
  # checks if this server is a postgres master
246
- def is_master? options = {}
253
+ def is_master?(options = {})
247
254
  msg = @node.messages.add('checking if this host is the postgres master: ', options)
248
- if @node.file_exists? "#{@config['postgresql.conf']['data_directory']}/recovery.done", :quiet => true
255
+ if @node.file_exists?("#{@config['postgresql.conf']['data_directory']}/recovery.done", :quiet => true)
249
256
  msg.ok('yes')
250
257
  return true
251
258
  else
@@ -33,17 +33,22 @@ class Sudoers < Recipe
33
33
  private
34
34
 
35
35
  def remove_other_rules
36
- @node.messages.add("deleting old rules\n")
36
+ msg = @node.messages.add("removing non-dust rules\n")
37
37
  ret = @node.exec('ls /etc/sudoers.d/* |cat')
38
38
  if ret[:exit_code] != 0
39
39
  return @node.messages.add('couldn\'t get installed rule list, skipping deletion of old rules').warning
40
40
  end
41
41
 
42
- # delete file if not in config
42
+ # get unmaintained rules
43
+ old_rules = []
43
44
  ret[:stdout].each_line do |file|
44
45
  file.chomp!
45
- @node.rm(file, :indent => 2) unless @config.keys.include?(File.basename(file))
46
+ old_rules << file unless @config.keys.include?(File.basename(file))
46
47
  end
48
+
49
+ # delete old rules, or display message that none were found
50
+ old_rules.each { |file| @node.rm(file, :indent => 2) }
51
+ @node.messages.add('none found', :indent => 2).ok if old_rules.empty?
47
52
  end
48
53
 
49
54
  def deploy_rule(name, file)
@@ -2,13 +2,13 @@ class Sysctl < Recipe
2
2
  desc 'sysctl:deploy', 'configures sysctl'
3
3
  def deploy
4
4
  # we need support for /etc/sysctl.d/
5
- unless @node.dir_exists? '/etc/sysctl.d/'
5
+ unless @node.dir_exists?('/etc/sysctl.d/')
6
6
  return @node.messages.add('sysctl configuration not supported for your linux distribution').warning
7
7
  end
8
8
 
9
9
  # seperate templates from sysctls
10
10
  sysctls = @config.clone
11
- templates = sysctls.delete 'templates'
11
+ templates = sysctls.delete('templates')
12
12
 
13
13
  # apply template sysctls
14
14
  if templates
@@ -18,24 +18,46 @@ class Sysctl < Recipe
18
18
  end
19
19
  end
20
20
 
21
- # apply plain sysctls
22
- @node.messages.add("configuring plain sysctls\n")
23
- apply 'dust', sysctls
21
+ # apply sysctls
22
+ sysctls.each do |name, keys|
23
+ @node.messages.add("configuring sysctls (#{name})\n")
24
+ apply(name, keys)
25
+ end
26
+
27
+ # don't remove other rules for now
28
+ # remove_other_rules(sysctls.keys)
24
29
  end
25
30
 
26
31
 
27
32
  private
28
33
 
29
- def apply name, sysctl
34
+ def apply(name, sysctl)
30
35
  sysctl_conf = ''
31
36
  sysctl.each do |key, value|
32
- msg = @node.messages.add("setting #{key} = #{value}", :indent => 2)
33
- msg.parse_result(@node.exec("sysctl -w #{key}=#{value}")[:exit_code])
37
+ if options.restart?
38
+ msg = @node.messages.add("setting #{key} = #{value}", :indent => 2)
39
+ msg.parse_result(@node.exec("sysctl -w #{key}=#{value}")[:exit_code])
40
+ end
34
41
  sysctl_conf << "#{key} = #{value}\n"
35
42
  end
36
43
 
37
- msg = @node.messages.add("saving settings to /etc/sysctl.d/10-#{name}.conf", :indent => 2)
38
- msg.parse_result(@node.write("/etc/sysctl.d/10-#{name}.conf", sysctl_conf, :quiet => true))
44
+ msg = @node.messages.add("saving settings to /etc/sysctl.d/#{name}.conf", :indent => 2)
45
+ msg.parse_result(@node.write("/etc/sysctl.d/#{name}.conf", sysctl_conf, :quiet => true))
46
+ end
47
+
48
+ # removes rules from /etc/sysctl.d/ that are not present in "names"
49
+ def remove_other(names)
50
+ @node.messages.add("deleting old rules\n")
51
+ ret = @node.exec('ls /etc/sysctl.d/* |cat')
52
+ if ret[:exit_code] != 0
53
+ return @node.messages.add('couldn\'t get list of files in /etc/sysctl.d, skipping deletion of old rules').warning
54
+ end
55
+
56
+ # delete file if not in config
57
+ ret[:stdout].each_line do |file|
58
+ file.chomp!
59
+ @node.rm(file, :indent => 2) unless names.include?(File.basename(file))
60
+ end
39
61
  end
40
62
 
41
63
 
@@ -43,7 +65,7 @@ class Sysctl < Recipe
43
65
 
44
66
  # disable allocation of more ram than actually there for postgres
45
67
  def postgres
46
- database.merge 'vm.overcommit_memory' => 2
68
+ database.merge('vm.overcommit_memory' => 2)
47
69
  end
48
70
 
49
71
  # redis complains if vm.overcommit_memory != 1
@@ -58,7 +80,7 @@ class Sysctl < Recipe
58
80
  # use half of the system memory for shmmax
59
81
  # and set shmall according to pagesize
60
82
  def database
61
- @node.collect_facts :quiet => true
83
+ @node.collect_facts(:quiet => true)
62
84
 
63
85
  # get pagesize
64
86
  pagesize = @node.exec('getconf PAGESIZE')[:stdout].to_i || 4096
@@ -26,7 +26,7 @@ class Users < Recipe
26
26
  Dir["#{@template_path}/#{key_dir}/*"].each do |file|
27
27
  destination = "#{ssh_dir}/#{File.basename(file)}"
28
28
  @node.scp(file, destination, :indent => 2)
29
- @node.chown("#{user}:#{user}", destination)
29
+ @node.chown("#{user}:#{@node.get_gid(user)}", destination)
30
30
 
31
31
  # chmod private key
32
32
  if File.basename(file) =~ /^(id_rsa|id_dsa|id_ecdsa)$/
@@ -40,25 +40,38 @@ class Users < Recipe
40
40
  def deploy_authorized_keys(user, ssh_users)
41
41
  @node.messages.add("generating authorized_keys for #{user}\n")
42
42
  ssh_dir = create_ssh_dir(user)
43
+
43
44
  authorized_keys = generate_authorized_keys(ssh_users)
45
+ return false unless authorized_keys
46
+
44
47
  @node.write("#{ssh_dir}/authorized_keys", authorized_keys)
45
- @node.chown("#{user}:#{user}", "#{ssh_dir}/authorized_keys")
48
+ @node.chown("#{user}:#{@node.get_gid(user)}", "#{ssh_dir}/authorized_keys")
49
+ @node.chcon({ 'type' => 'ssh_home_t' }, "#{ssh_dir}/authorized_keys")
46
50
  end
47
51
 
48
52
  def create_ssh_dir(user)
49
53
  ssh_dir = @node.get_home(user) + '/.ssh'
50
54
  @node.mkdir(ssh_dir)
51
- @node.chown("#{user}:#{user}", ssh_dir)
55
+ @node.chown("#{user}:#{@node.get_gid(user)}", ssh_dir)
56
+ @node.chcon({ 'type' => 'ssh_home_t' }, ssh_dir)
52
57
  ssh_dir
53
58
  end
54
59
 
55
60
  def generate_authorized_keys(ssh_users)
56
61
  # load users and their ssh keys from yaml file
62
+ unless File.exists?("#{@template_path}/public_keys.yaml")
63
+ return @node.messages.add("#{@template_path}/public_keys.yaml not present").failed
64
+ end
65
+
57
66
  users = YAML.load_file("#{@template_path}/public_keys.yaml")
58
67
  authorized_keys = ''
59
68
 
60
69
  # create the authorized_keys hash for this user
61
70
  ssh_users.to_array.each do |ssh_user|
71
+ unless users[ssh_user]
72
+ return @node.messages.add("#{ssh_user} cannot be found in #{@template_path}/public_keys.yaml").failed
73
+ end
74
+
62
75
  users[ssh_user]['name'] ||= ssh_user
63
76
  msg = @node.messages.add("adding user #{users[ssh_user]['name']}", :indent => 2)
64
77
  users[ssh_user]['keys'].each do |key|
data/lib/dust/runner.rb CHANGED
@@ -337,9 +337,9 @@ module Dust
337
337
  inherited = {}
338
338
  node.delete('inherits').each do |file|
339
339
  template = YAML.load ERB.new( File.read("./nodes/#{file}.yaml"), nil, '%<>').result
340
- inherited.deep_merge! template
340
+ inherited.deep_merge!(template)
341
341
  end
342
- node = inherited.deep_merge node
342
+ node = inherited.deep_merge(node)
343
343
  end
344
344
 
345
345
  # if more than one hostname is specified, create a node
data/lib/dust/server.rb CHANGED
@@ -164,6 +164,8 @@ module Dust
164
164
  else
165
165
  # files = 644, dirs = 755
166
166
  permissions = 'ug-x,o-wx,u=rwX,g=rX,o=rX'
167
+ user = 'root'
168
+ group = 'root'
167
169
  end
168
170
 
169
171
  # if in sudo mode, copy file to temporary place, then move using sudo
@@ -232,6 +234,28 @@ module Dust
232
234
  msg.parse_result(exec("chown -R #{user} #{file}")[:exit_code])
233
235
  end
234
236
 
237
+ def chcon(permissions, file, options = {})
238
+ options = default_options.merge(options)
239
+
240
+ # just return if selinux is not enabled
241
+ return true unless selinuxenabled?
242
+
243
+ args = ""
244
+ args << " --type #{permissions['type']}" if permissions['type']
245
+ args << " --recursive #{permissions['recursive']}" if permissions['recursive']
246
+ args << " --user #{permissions['user']}" if permissions['user']
247
+ args << " --range #{permissions['range']}" if permissions['range']
248
+ args << " --role #{permissions['role']}" if permissions['role']
249
+
250
+ msg = messages.add("setting selinux permissions of #{File.basename file}", options)
251
+ msg.parse_result(exec("chcon #{args} #{file}")[:exit_code])
252
+ end
253
+
254
+ def selinuxenabled?
255
+ return true if exec('selinuxenabled')[:exit_code] == 0
256
+ false
257
+ end
258
+
235
259
  def rm file, options = {}
236
260
  options = default_options.merge options
237
261
 
@@ -275,12 +299,11 @@ module Dust
275
299
  def restorecon path, options = {}
276
300
  options = default_options.merge options
277
301
 
278
- # if restorecon is not installed, just return true
279
- ret = exec 'which restorecon'
280
- return true unless ret[:exit_code] == 0
302
+ # if selinux is not enabled, just return
303
+ return true unless selinuxenabled?
281
304
 
282
305
  msg = messages.add("restoring selinux labels for #{path}", options)
283
- msg.parse_result(exec("#{ret[:stdout].chomp} -R #{path}")[:exit_code])
306
+ msg.parse_result(exec("restorecon -R #{path}")[:exit_code])
284
307
  end
285
308
 
286
309
  def get_system_users options = {}
@@ -738,10 +761,10 @@ module Dust
738
761
  args << " --append --groups #{Array(options['groups']).join(',')}" if options['groups']
739
762
 
740
763
  if args.empty?
741
- return messages.add("user #{user} already set up correctly", options).ok
764
+ ret = messages.add("user #{user} already set up correctly", options).ok
742
765
  else
743
766
  msg = messages.add("modifying user #{user}", { :indent => options[:indent] }.merge(options))
744
- return msg.parse_result(exec("usermod #{user} #{args}")[:exit_code])
767
+ ret = msg.parse_result(exec("usermod #{args} #{user}")[:exit_code])
745
768
  end
746
769
 
747
770
  else
@@ -755,16 +778,20 @@ module Dust
755
778
  args << " --groups #{Array(options['groups']).join(',')}" if options['groups']
756
779
 
757
780
  msg = messages.add("creating user #{user}", { :indent => options[:indent] }.merge(options))
758
- return msg.parse_result(exec("useradd #{user} #{args}")[:exit_code])
781
+ ret = msg.parse_result(exec("useradd #{user} #{args}")[:exit_code])
759
782
  end
783
+
784
+ # set selinux permissions
785
+ chcon({ 'type' => 'user_home_dir_t' }, get_home(user), options)
786
+ return ret
760
787
  end
761
788
 
762
789
  # returns the home directory of this user
763
- def get_home user, options = {}
764
- options = default_options(:quiet => true).merge options
790
+ def get_home(user, options = {})
791
+ options = default_options(:quiet => true).merge(options)
765
792
 
766
793
  msg = messages.add("getting home directory of #{user}", options)
767
- ret = exec "getent passwd |cut -d':' -f1,6 |grep '^#{user}' |head -n1 |cut -d: -f2"
794
+ ret = exec("getent passwd |cut -d':' -f1,6 |grep '^#{user}' |head -n1 |cut -d: -f2")
768
795
  if msg.parse_result(ret[:exit_code])
769
796
  return ret[:stdout].chomp
770
797
  else
@@ -773,11 +800,24 @@ module Dust
773
800
  end
774
801
 
775
802
  # returns shell of this user
776
- def get_shell user, options = {}
777
- options = default_options(:quiet => true).merge options
803
+ def get_shell(user, options = {})
804
+ options = default_options(:quiet => true).merge(options)
778
805
 
779
806
  msg = messages.add("getting shell of #{user}", options)
780
- ret = exec "getent passwd |cut -d':' -f1,7 |grep '^#{user}' |head -n1 |cut -d: -f2"
807
+ ret = exec("getent passwd |cut -d':' -f1,7 |grep '^#{user}' |head -n1 |cut -d: -f2")
808
+ if msg.parse_result(ret[:exit_code])
809
+ return ret[:stdout].chomp
810
+ else
811
+ return false
812
+ end
813
+ end
814
+
815
+ # returns primary group id of this user
816
+ def get_gid(user, options = {})
817
+ options = default_options(:quiet => true).merge(options)
818
+
819
+ msg = messages.add("getting primary gid of #{user}", options)
820
+ ret = exec("getent passwd |cut -d':' -f1,4 |grep '^#{user}' |head -n1 |cut -d: -f2")
781
821
  if msg.parse_result(ret[:exit_code])
782
822
  return ret[:stdout].chomp
783
823
  else
data/lib/dust/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Dust
2
- VERSION = "0.15.2"
2
+ VERSION = "0.16.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dust-deploy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.15.2
4
+ version: 0.16.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-07-30 00:00:00.000000000 Z
12
+ date: 2012-08-20 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: json