dust-deploy 0.15.2 → 0.16.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/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