cloud-mu 3.5.0 → 3.6.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (245) hide show
  1. checksums.yaml +4 -4
  2. data/Berksfile +5 -2
  3. data/Berksfile.lock +135 -0
  4. data/ansible/roles/mu-base/README.md +33 -0
  5. data/ansible/roles/mu-base/defaults/main.yml +2 -0
  6. data/ansible/roles/mu-base/files/check_apm.cfg +1 -0
  7. data/ansible/roles/mu-base/files/check_apm.sh +18 -0
  8. data/ansible/roles/mu-base/files/check_disk.cfg +1 -0
  9. data/ansible/roles/mu-base/files/check_elastic_shards.cfg +1 -0
  10. data/ansible/roles/mu-base/files/check_elastic_shards.sh +12 -0
  11. data/ansible/roles/mu-base/files/check_logstash.cfg +1 -0
  12. data/ansible/roles/mu-base/files/check_logstash.sh +14 -0
  13. data/ansible/roles/mu-base/files/check_mem.cfg +1 -0
  14. data/ansible/roles/mu-base/files/check_updates.cfg +1 -0
  15. data/ansible/roles/mu-base/files/logrotate.conf +35 -0
  16. data/ansible/roles/mu-base/files/nrpe-apm-sudo +1 -0
  17. data/ansible/roles/mu-base/files/nrpe-elasticshards-sudo +2 -0
  18. data/ansible/roles/mu-base/handlers/main.yml +5 -0
  19. data/ansible/roles/mu-base/meta/main.yml +53 -0
  20. data/ansible/roles/mu-base/tasks/main.yml +113 -0
  21. data/ansible/roles/mu-base/templates/nrpe.cfg.j2 +231 -0
  22. data/ansible/roles/mu-base/tests/inventory +2 -0
  23. data/ansible/roles/mu-base/tests/test.yml +5 -0
  24. data/ansible/roles/mu-base/vars/main.yml +1 -0
  25. data/ansible/roles/mu-compliance/README.md +33 -0
  26. data/ansible/roles/mu-compliance/defaults/main.yml +2 -0
  27. data/ansible/roles/mu-compliance/files/U_MS_Windows_Server_2016_V2R1_STIG_SCAP_1-2_Benchmark.xml +15674 -0
  28. data/ansible/roles/mu-compliance/files/U_MS_Windows_Server_2019_V2R1_STIG_SCAP_1-2_Benchmark.xml +17553 -0
  29. data/ansible/roles/mu-compliance/handlers/main.yml +2 -0
  30. data/ansible/roles/mu-compliance/meta/main.yml +53 -0
  31. data/ansible/roles/mu-compliance/tasks/main.yml +45 -0
  32. data/ansible/roles/mu-compliance/tests/inventory +2 -0
  33. data/ansible/roles/mu-compliance/tests/test.yml +5 -0
  34. data/ansible/roles/mu-compliance/vars/main.yml +4 -0
  35. data/ansible/roles/mu-elastic/README.md +51 -0
  36. data/ansible/roles/mu-elastic/defaults/main.yml +2 -0
  37. data/ansible/roles/mu-elastic/files/jvm.options +93 -0
  38. data/ansible/roles/mu-elastic/handlers/main.yml +10 -0
  39. data/ansible/roles/mu-elastic/meta/main.yml +52 -0
  40. data/ansible/roles/mu-elastic/tasks/main.yml +186 -0
  41. data/ansible/roles/mu-elastic/templates/elasticsearch.yml.j2 +110 -0
  42. data/ansible/roles/mu-elastic/templates/kibana.yml.j2 +131 -0
  43. data/ansible/roles/mu-elastic/templates/password_set.expect.j2 +19 -0
  44. data/ansible/roles/mu-elastic/tests/inventory +2 -0
  45. data/ansible/roles/mu-elastic/tests/test.yml +5 -0
  46. data/ansible/roles/mu-elastic/vars/main.yml +2 -0
  47. data/ansible/roles/mu-logstash/README.md +51 -0
  48. data/ansible/roles/mu-logstash/defaults/main.yml +2 -0
  49. data/ansible/roles/mu-logstash/files/02-beats-input.conf +5 -0
  50. data/ansible/roles/mu-logstash/files/10-rails-filter.conf +16 -0
  51. data/ansible/roles/mu-logstash/files/jvm.options +84 -0
  52. data/ansible/roles/mu-logstash/files/logstash.yml +304 -0
  53. data/ansible/roles/mu-logstash/handlers/main.yml +20 -0
  54. data/ansible/roles/mu-logstash/meta/main.yml +52 -0
  55. data/ansible/roles/mu-logstash/tasks/main.yml +254 -0
  56. data/ansible/roles/mu-logstash/templates/20-cloudtrail.conf.j2 +28 -0
  57. data/ansible/roles/mu-logstash/templates/30-elasticsearch-output.conf.j2 +19 -0
  58. data/ansible/roles/mu-logstash/templates/apm-server.yml.j2 +33 -0
  59. data/ansible/roles/mu-logstash/templates/heartbeat.yml.j2 +29 -0
  60. data/ansible/roles/mu-logstash/templates/nginx/apm.conf.j2 +25 -0
  61. data/ansible/roles/mu-logstash/templates/nginx/default.conf.j2 +56 -0
  62. data/ansible/roles/mu-logstash/templates/nginx/elastic.conf.j2 +27 -0
  63. data/ansible/roles/mu-logstash/tests/inventory +2 -0
  64. data/ansible/roles/mu-logstash/tests/test.yml +5 -0
  65. data/ansible/roles/mu-logstash/vars/main.yml +2 -0
  66. data/ansible/roles/mu-rdp/README.md +33 -0
  67. data/ansible/roles/mu-rdp/meta/main.yml +53 -0
  68. data/ansible/roles/mu-rdp/tasks/main.yml +9 -0
  69. data/ansible/roles/mu-rdp/tests/inventory +2 -0
  70. data/ansible/roles/mu-rdp/tests/test.yml +5 -0
  71. data/ansible/roles/mu-windows/tasks/main.yml +3 -0
  72. data/bin/mu-ansible-secret +1 -1
  73. data/bin/mu-aws-setup +4 -3
  74. data/bin/mu-azure-setup +5 -5
  75. data/bin/mu-configure +25 -17
  76. data/bin/mu-firewall-allow-clients +1 -0
  77. data/bin/mu-gcp-setup +3 -3
  78. data/bin/mu-load-config.rb +1 -0
  79. data/bin/mu-node-manage +66 -33
  80. data/bin/mu-self-update +2 -2
  81. data/bin/mu-upload-chef-artifacts +6 -1
  82. data/bin/mu-user-manage +1 -1
  83. data/cloud-mu.gemspec +25 -23
  84. data/cookbooks/firewall/CHANGELOG.md +417 -224
  85. data/cookbooks/firewall/LICENSE +202 -0
  86. data/cookbooks/firewall/README.md +153 -126
  87. data/cookbooks/firewall/TODO.md +6 -0
  88. data/cookbooks/firewall/attributes/firewalld.rb +7 -0
  89. data/cookbooks/firewall/attributes/iptables.rb +3 -3
  90. data/cookbooks/firewall/chefignore +115 -0
  91. data/cookbooks/firewall/libraries/helpers.rb +5 -0
  92. data/cookbooks/firewall/libraries/helpers_firewalld.rb +1 -1
  93. data/cookbooks/firewall/libraries/helpers_firewalld_dbus.rb +72 -0
  94. data/cookbooks/firewall/libraries/helpers_iptables.rb +3 -3
  95. data/cookbooks/firewall/libraries/helpers_nftables.rb +170 -0
  96. data/cookbooks/firewall/libraries/helpers_ufw.rb +7 -0
  97. data/cookbooks/firewall/libraries/helpers_windows.rb +8 -9
  98. data/cookbooks/firewall/libraries/provider_firewall_firewalld.rb +9 -9
  99. data/cookbooks/firewall/libraries/provider_firewall_iptables.rb +7 -7
  100. data/cookbooks/firewall/libraries/provider_firewall_iptables_ubuntu.rb +12 -8
  101. data/cookbooks/firewall/libraries/provider_firewall_iptables_ubuntu1404.rb +13 -9
  102. data/cookbooks/firewall/libraries/provider_firewall_rule.rb +1 -1
  103. data/cookbooks/firewall/libraries/provider_firewall_ufw.rb +5 -5
  104. data/cookbooks/firewall/libraries/provider_firewall_windows.rb +4 -4
  105. data/cookbooks/firewall/libraries/resource_firewall_rule.rb +3 -3
  106. data/cookbooks/firewall/metadata.json +40 -1
  107. data/cookbooks/firewall/metadata.rb +15 -0
  108. data/cookbooks/firewall/recipes/default.rb +7 -7
  109. data/cookbooks/firewall/recipes/disable_firewall.rb +1 -1
  110. data/cookbooks/firewall/recipes/firewalld.rb +87 -0
  111. data/cookbooks/firewall/renovate.json +18 -0
  112. data/cookbooks/firewall/resources/firewalld.rb +28 -0
  113. data/cookbooks/firewall/resources/firewalld_config.rb +39 -0
  114. data/cookbooks/firewall/resources/firewalld_helpers.rb +106 -0
  115. data/cookbooks/firewall/resources/firewalld_icmptype.rb +88 -0
  116. data/cookbooks/firewall/resources/firewalld_ipset.rb +104 -0
  117. data/cookbooks/firewall/resources/firewalld_policy.rb +115 -0
  118. data/cookbooks/firewall/resources/firewalld_service.rb +98 -0
  119. data/cookbooks/firewall/resources/firewalld_zone.rb +118 -0
  120. data/cookbooks/firewall/resources/nftables.rb +71 -0
  121. data/cookbooks/firewall/resources/nftables_rule.rb +113 -0
  122. data/cookbooks/mu-activedirectory/Berksfile +1 -1
  123. data/cookbooks/mu-activedirectory/metadata.rb +1 -1
  124. data/cookbooks/mu-firewall/metadata.rb +2 -2
  125. data/cookbooks/mu-master/Berksfile +4 -3
  126. data/cookbooks/mu-master/attributes/default.rb +5 -2
  127. data/cookbooks/mu-master/files/default/check_elastic.sh +761 -0
  128. data/cookbooks/mu-master/files/default/check_kibana.rb +45 -0
  129. data/cookbooks/mu-master/libraries/mu.rb +24 -0
  130. data/cookbooks/mu-master/metadata.rb +5 -5
  131. data/cookbooks/mu-master/recipes/default.rb +31 -20
  132. data/cookbooks/mu-master/recipes/firewall-holes.rb +5 -0
  133. data/cookbooks/mu-master/recipes/init.rb +58 -19
  134. data/cookbooks/mu-master/recipes/update_nagios_only.rb +251 -178
  135. data/cookbooks/mu-master/templates/default/nagios.conf.erb +5 -11
  136. data/cookbooks/mu-master/templates/default/web_app.conf.erb +3 -0
  137. data/cookbooks/mu-php54/Berksfile +1 -1
  138. data/cookbooks/mu-php54/metadata.rb +2 -2
  139. data/cookbooks/mu-tools/Berksfile +2 -3
  140. data/cookbooks/mu-tools/attributes/default.rb +3 -4
  141. data/cookbooks/mu-tools/files/amazon/etc/bashrc +90 -0
  142. data/cookbooks/mu-tools/files/amazon/etc/login.defs +292 -0
  143. data/cookbooks/mu-tools/files/amazon/etc/profile +77 -0
  144. data/cookbooks/mu-tools/files/amazon/etc/security/limits.conf +63 -0
  145. data/cookbooks/mu-tools/files/amazon/etc/sysconfig/init +19 -0
  146. data/cookbooks/mu-tools/files/amazon/etc/sysctl.conf +82 -0
  147. data/cookbooks/mu-tools/files/amazon-2023/etc/login.defs +294 -0
  148. data/cookbooks/mu-tools/files/default/logrotate.conf +35 -0
  149. data/cookbooks/mu-tools/files/default/nrpe_conf_d.pp +0 -0
  150. data/cookbooks/mu-tools/libraries/helper.rb +21 -9
  151. data/cookbooks/mu-tools/metadata.rb +4 -4
  152. data/cookbooks/mu-tools/recipes/apply_security.rb +3 -2
  153. data/cookbooks/mu-tools/recipes/aws_api.rb +23 -5
  154. data/cookbooks/mu-tools/recipes/base_repositories.rb +4 -1
  155. data/cookbooks/mu-tools/recipes/gcloud.rb +56 -56
  156. data/cookbooks/mu-tools/recipes/nagios.rb +1 -1
  157. data/cookbooks/mu-tools/recipes/nrpe.rb +20 -2
  158. data/cookbooks/mu-tools/recipes/rsyslog.rb +12 -1
  159. data/cookbooks/mu-tools/recipes/set_local_fw.rb +1 -1
  160. data/data_bags/nagios_services/apm_backend_connect.json +5 -0
  161. data/data_bags/nagios_services/apm_listen.json +5 -0
  162. data/data_bags/nagios_services/elastic_shards.json +5 -0
  163. data/data_bags/nagios_services/logstash.json +5 -0
  164. data/data_bags/nagios_services/rhel7_updates.json +8 -0
  165. data/extras/image-generators/AWS/centos7.yaml +1 -0
  166. data/extras/image-generators/AWS/rhel7.yaml +21 -0
  167. data/extras/image-generators/AWS/win2k12r2.yaml +1 -0
  168. data/extras/image-generators/AWS/win2k16.yaml +1 -0
  169. data/extras/image-generators/AWS/win2k19.yaml +1 -0
  170. data/extras/list-stock-amis +0 -0
  171. data/extras/ruby_rpm/muby.spec +8 -5
  172. data/extras/vault_tools/export_vaults.sh +1 -1
  173. data/extras/vault_tools/recreate_vaults.sh +0 -0
  174. data/extras/vault_tools/test_vaults.sh +0 -0
  175. data/install/deprecated-bash-library.sh +1 -1
  176. data/install/installer +4 -2
  177. data/modules/mommacat.ru +3 -1
  178. data/modules/mu/adoption.rb +1 -1
  179. data/modules/mu/cloud/dnszone.rb +2 -2
  180. data/modules/mu/cloud/machine_images.rb +26 -25
  181. data/modules/mu/cloud/resource_base.rb +213 -182
  182. data/modules/mu/cloud/server_pool.rb +1 -1
  183. data/modules/mu/cloud/ssh_sessions.rb +7 -5
  184. data/modules/mu/cloud/wrappers.rb +2 -2
  185. data/modules/mu/cloud.rb +1 -1
  186. data/modules/mu/config/bucket.rb +1 -1
  187. data/modules/mu/config/function.rb +6 -1
  188. data/modules/mu/config/loadbalancer.rb +24 -2
  189. data/modules/mu/config/ref.rb +12 -0
  190. data/modules/mu/config/role.rb +1 -1
  191. data/modules/mu/config/schema_helpers.rb +42 -9
  192. data/modules/mu/config/server.rb +43 -27
  193. data/modules/mu/config/tail.rb +19 -10
  194. data/modules/mu/config.rb +6 -5
  195. data/modules/mu/defaults/AWS.yaml +78 -114
  196. data/modules/mu/deploy.rb +9 -2
  197. data/modules/mu/groomer.rb +12 -4
  198. data/modules/mu/groomers/ansible.rb +104 -20
  199. data/modules/mu/groomers/chef.rb +15 -6
  200. data/modules/mu/master.rb +9 -4
  201. data/modules/mu/mommacat/daemon.rb +4 -2
  202. data/modules/mu/mommacat/naming.rb +1 -2
  203. data/modules/mu/mommacat/storage.rb +7 -2
  204. data/modules/mu/mommacat.rb +33 -6
  205. data/modules/mu/providers/aws/database.rb +161 -8
  206. data/modules/mu/providers/aws/dnszone.rb +11 -6
  207. data/modules/mu/providers/aws/endpoint.rb +81 -6
  208. data/modules/mu/providers/aws/firewall_rule.rb +254 -172
  209. data/modules/mu/providers/aws/function.rb +65 -3
  210. data/modules/mu/providers/aws/loadbalancer.rb +39 -28
  211. data/modules/mu/providers/aws/log.rb +2 -1
  212. data/modules/mu/providers/aws/role.rb +25 -7
  213. data/modules/mu/providers/aws/server.rb +36 -12
  214. data/modules/mu/providers/aws/server_pool.rb +237 -127
  215. data/modules/mu/providers/aws/storage_pool.rb +7 -1
  216. data/modules/mu/providers/aws/user.rb +1 -1
  217. data/modules/mu/providers/aws/userdata/linux.erb +6 -2
  218. data/modules/mu/providers/aws/userdata/windows.erb +7 -5
  219. data/modules/mu/providers/aws/vpc.rb +49 -25
  220. data/modules/mu/providers/aws.rb +13 -8
  221. data/modules/mu/providers/azure/container_cluster.rb +1 -1
  222. data/modules/mu/providers/azure/loadbalancer.rb +2 -2
  223. data/modules/mu/providers/azure/server.rb +5 -2
  224. data/modules/mu/providers/azure/userdata/linux.erb +1 -1
  225. data/modules/mu/providers/azure.rb +11 -8
  226. data/modules/mu/providers/cloudformation/dnszone.rb +1 -1
  227. data/modules/mu/providers/google/container_cluster.rb +15 -2
  228. data/modules/mu/providers/google/folder.rb +2 -1
  229. data/modules/mu/providers/google/function.rb +130 -4
  230. data/modules/mu/providers/google/habitat.rb +2 -1
  231. data/modules/mu/providers/google/loadbalancer.rb +407 -160
  232. data/modules/mu/providers/google/role.rb +16 -3
  233. data/modules/mu/providers/google/server.rb +5 -1
  234. data/modules/mu/providers/google/user.rb +25 -18
  235. data/modules/mu/providers/google/userdata/linux.erb +1 -1
  236. data/modules/mu/providers/google/vpc.rb +53 -7
  237. data/modules/mu/providers/google.rb +39 -39
  238. data/modules/mu.rb +8 -8
  239. data/modules/tests/elk.yaml +46 -0
  240. data/test/mu-master-test/controls/all_in_one.rb +1 -1
  241. metadata +207 -112
  242. data/cookbooks/firewall/CONTRIBUTING.md +0 -2
  243. data/cookbooks/firewall/MAINTAINERS.md +0 -19
  244. data/cookbooks/firewall/libraries/matchers.rb +0 -30
  245. data/extras/image-generators/AWS/rhel71.yaml +0 -17
@@ -19,6 +19,7 @@ module MU
19
19
  class Groomer
20
20
  # Support for Ansible as a host configuration management layer.
21
21
  class Ansible
22
+ require 'open3'
22
23
 
23
24
  # Failure to load or create a deploy
24
25
  class NoAnsibleExecError < MuError;
@@ -77,7 +78,7 @@ module MU
77
78
  # @param data [Hash]: Data to save
78
79
  # @param permissions [Boolean]: If true, save the secret under the current active deploy (if any), rather than in the global location for this user
79
80
  # @param deploy_dir [String]: If permissions is +true+, save the secret here
80
- def self.saveSecret(vault: nil, item: nil, data: nil, permissions: false, deploy_dir: nil)
81
+ def self.saveSecret(vault: nil, item: nil, data: nil, permissions: false, deploy_dir: nil, quiet: false)
81
82
 
82
83
  if vault.nil? or vault.empty? or item.nil? or item.empty?
83
84
  raise MuError, "Must call saveSecret with vault and item names"
@@ -86,6 +87,7 @@ module MU
86
87
  raise MuError, "Ansible vault/item names cannot include forward slashes"
87
88
  end
88
89
  pwfile = vaultPasswordFile
90
+ vault_cmd = %Q{#{ansibleExecDir}/ansible-vault}
89
91
 
90
92
  dir = if permissions
91
93
  if deploy_dir
@@ -104,7 +106,7 @@ module MU
104
106
  FileUtils.mkdir_p(dir, mode: 0700)
105
107
  end
106
108
 
107
- if File.exist?(path)
109
+ if File.exist?(path) and !quiet
108
110
  MU.log "Overwriting existing vault #{vault} item #{item}"
109
111
  end
110
112
 
@@ -112,14 +114,36 @@ module MU
112
114
  f.write data.to_yaml
113
115
  }
114
116
 
115
- cmd = %Q{#{ansibleExecDir}/ansible-vault encrypt #{path} --vault-password-file #{pwfile}}
116
- MU.log cmd
117
+ cmd = %Q{#{vault_cmd} encrypt #{path} --vault-password-file #{pwfile}}
118
+ MU.log cmd if !quiet
117
119
  raise MuError, "Failed Ansible command: #{cmd}" if !system(cmd)
120
+
121
+ # If we're stashing things under a deploy, go ahead and munge them into
122
+ # variables that actual Ansible tasks can get at
123
+ if permissions
124
+ encrypted_string = File.read(path).chomp
125
+ dir = (deploy_dir ? deploy_dir : MU.mommacat.deploy_dir)+"/ansible"
126
+ FileUtils.mkdir_p(dir, mode: 0700) if !Dir.exist?(dir)
127
+ FileUtils.mkdir_p(dir+"/vars", mode: 0700) if !Dir.exist?(dir+"/vars")
128
+ vars_file = "#{dir}/vars/#{vault}.yml"
129
+
130
+ vars = if File.exist?(vars_file)
131
+ YAML.load(File.read(vars_file))
132
+ else
133
+ {}
134
+ end
135
+ vars[item] = encrypted_string
136
+ File.open(vars_file, File::CREAT|File::RDWR|File::TRUNC, 0600) { |f|
137
+ f.flock(File::LOCK_EX)
138
+ f.puts vars.to_yaml
139
+ f.flock(File::LOCK_UN)
140
+ }
141
+ end
118
142
  end
119
143
 
120
144
  # see {MU::Groomer::Ansible.saveSecret}
121
- def saveSecret(vault: @server.mu_name, item: nil, data: nil, permissions: true)
122
- self.class.saveSecret(vault: vault, item: item, data: data, permissions: permissions, deploy_dir: @server.deploy.deploy_dir)
145
+ def saveSecret(vault: @server.mu_name, item: nil, data: nil, permissions: true, quiet: false)
146
+ self.class.saveSecret(vault: vault, item: item, data: data, permissions: permissions, deploy_dir: @server.deploy.deploy_dir, quiet: quiet)
123
147
  end
124
148
 
125
149
  # Retrieve sensitive data, which hopefully we're storing and retrieving
@@ -128,7 +152,7 @@ module MU
128
152
  # @param item [String]: The item within the repository to retrieve
129
153
  # @param field [String]: OPTIONAL - A specific field within the item to return.
130
154
  # @return [Hash]
131
- def self.getSecret(vault: nil, item: nil, field: nil, deploy_dir: nil)
155
+ def self.getSecret(vault: nil, item: nil, field: nil, deploy_dir: nil, quiet: false, cmd_only: false)
132
156
  if vault.nil? or vault.empty?
133
157
  raise MuError, "Must call getSecret with at least a vault name"
134
158
  end
@@ -137,7 +161,7 @@ module MU
137
161
  dir = nil
138
162
  try = [secret_dir+"/"+vault]
139
163
  try << deploy_dir+"/ansible/vaults/"+vault if deploy_dir
140
- try << MU.mommacat.deploy_dir+"/ansible/vaults/"+vault if MU.mommacat.deploy_dir
164
+ try << MU.mommacat.deploy_dir+"/ansible/vaults/"+vault if MU.mommacat and MU.mommacat.deploy_dir
141
165
  try.each { |maybe_dir|
142
166
  if Dir.exist?(maybe_dir) and (item.nil? or File.exist?(maybe_dir+"/"+item))
143
167
  dir = maybe_dir
@@ -155,7 +179,8 @@ module MU
155
179
  raise MuNoSuchSecret, "No such item #{item} in vault #{vault}"
156
180
  end
157
181
  cmd = %Q{#{ansibleExecDir}/ansible-vault view #{itempath} --vault-password-file #{pwfile}}
158
- MU.log cmd
182
+ return cmd if cmd_only
183
+ MU.log cmd if !quiet
159
184
  a = `#{cmd}`
160
185
  # If we happen to have stored recognizeable JSON or YAML, return it
161
186
  # as parsed, which is a behavior we're used to from Chef vault.
@@ -187,8 +212,8 @@ module MU
187
212
  end
188
213
 
189
214
  # see {MU::Groomer::Ansible.getSecret}
190
- def getSecret(vault: nil, item: nil, field: nil)
191
- self.class.getSecret(vault: vault, item: item, field: field, deploy_dir: @server.deploy.deploy_dir)
215
+ def getSecret(vault: @server.mu_name, item: nil, field: nil, quiet: false, cmd_only: false)
216
+ self.class.getSecret(vault: vault, item: item, field: field, deploy_dir: @server.deploy.deploy_dir, quiet: quiet, cmd_only: cmd_only)
192
217
  end
193
218
 
194
219
  # Delete a Ansible data bag / Vault
@@ -259,6 +284,16 @@ module MU
259
284
 
260
285
  cmd = %Q{cd #{@ansible_path} && echo "#{purpose}" && #{@ansible_execs}/ansible-playbook -i hosts #{playbook} --limit=#{@server.windows? ? @server.canonicalIP : @server.mu_name} --vault-password-file #{pwfile} --timeout=30 --vault-password-file #{@ansible_path}/.vault_pw -u #{ssh_user}}
261
286
 
287
+ if @server.config['vault_access']
288
+ @server.config['vault_access'].each { |entry|
289
+ vault = entry['vault'] || @server.deploy.deploy_id
290
+ begin
291
+ MU.log "To retrieve secret #{vault}:#{entry['item']} - "+getSecret(vault: vault, item: entry['item'], cmd_only: true), MU::SUMMARY
292
+ rescue MuNoSuchSecret
293
+ end
294
+ }
295
+ end
296
+
262
297
  retries = 0
263
298
  begin
264
299
  MU.log cmd
@@ -315,8 +350,13 @@ module MU
315
350
  play["become"] = "yes"
316
351
  end
317
352
 
318
- if @server.config['run_list'] and !@server.config['run_list'].empty?
319
- play["roles"] = @server.config['run_list']
353
+ if @server.windows?
354
+ play["roles"] = ["mu-windows"]
355
+ else
356
+ play["roles"] = ["mu-base"]
357
+ end
358
+ if @server.config['run_list']
359
+ play["roles"].concat(@server.config['run_list'])
320
360
  end
321
361
 
322
362
  if @server.config['ansible_vars']
@@ -326,6 +366,7 @@ module MU
326
366
  if @server.windows?
327
367
  play["vars"] ||= {}
328
368
  play["vars"]["ansible_connection"] = "winrm"
369
+ play["vars"]['ansible_python_interpreter'] = "c:/bin/python/python310/python.exe"
329
370
  play["vars"]["ansible_winrm_scheme"] = "https"
330
371
  play["vars"]["ansible_winrm_transport"] = "ntlm"
331
372
  play["vars"]["ansible_winrm_server_cert_validation"] = "ignore" # XXX this sucks; use Mu_CA.pem if we can get it to work
@@ -355,18 +396,43 @@ module MU
355
396
  allvars = {
356
397
  "mu_deployment" => MU::Config.stripConfig(@server.deploy.deployment),
357
398
  "mu_service_name" => @config["name"],
399
+ "mu_name" => @server.mu_name,
400
+ "mu_deploy_id" => @server.deploy.deploy_id,
358
401
  "mu_canonical_ip" => @server.canonicalIP,
359
402
  "mu_admin_email" => $MU_CFG['mu_admin_email'],
360
- "mu_environment" => MU.environment.downcase
403
+ "mu_environment" => MU.environment.downcase,
404
+ "mu_vaults" => {}
361
405
  }
362
406
  allvars['mu_deployment']['ssh_public_key'] = @server.deploy.ssh_public_key
363
407
 
408
+ vaultdir = @ansible_path+"/vaults"
409
+ if Dir.exist?(vaultdir)
410
+ Dir.entries(vaultdir).each { |v|
411
+ next if !File.directory?(vaultdir+"/"+v)
412
+ next if [".", ".."].include?(v)
413
+ Dir.entries(vaultdir+"/"+v).each { |i|
414
+ next if File.directory?(vaultdir+"/"+v+"/"+i)
415
+ value = getSecret(vault: v, item: i, quiet: true)
416
+ next if !value # ignore corrupted data
417
+
418
+ # Ansible struggles to actually use this. The only thing that
419
+ # seems to work is writing it (decrypted) to a tmp file on the
420
+ # target host then reading that back, which is both ugly and
421
+ # insecure. None of these workarounds seem to do the thing:
422
+ # https://github.com/ansible/ansible/issues/24425
423
+ allvars["mu_vaults"][v] ||= {}
424
+ allvars["mu_vaults"][v].merge!(YAML.load(self.class.encryptString(value.to_yaml, i)))
425
+ }
426
+ }
427
+ end
428
+
364
429
  if @server.config['cloud'] == "AWS"
365
430
  allvars["ec2"] = MU.structToHash(@server.cloud_desc, stringify_keys: true)
366
431
  end
367
432
 
368
433
  if @server.windows?
369
434
  allvars['windows_admin_username'] = @config['windows_admin_username']
435
+ allvars['ansible_python_interpreter'] = "c:/bin/python/python310/python.exe"
370
436
  end
371
437
 
372
438
  if !@server.cloud.nil?
@@ -380,6 +446,9 @@ module MU
380
446
  }
381
447
 
382
448
  groupvars = allvars.dup
449
+ if @server.windows? and @server.mu_windows_name
450
+ groupvars['mu_windows_name'] = @server.mu_windows_name
451
+ end
383
452
  if @server.deploy.original_config.has_key?('parameters')
384
453
  groupvars["mu_parameters"] = @server.deploy.original_config['parameters']
385
454
  end
@@ -433,17 +502,23 @@ module MU
433
502
  found
434
503
  end
435
504
 
436
- # Encrypt a string using +ansible-vault encrypt_string+ and print the
437
- # the results to +STDOUT+.
438
- # @param name [String]: The variable name to use for the string's YAML key
505
+ # Encrypt a string using +ansible-vault encrypt_string+ and return +STDOUT+
439
506
  # @param string [String]: The string to encrypt
440
- def self.encryptString(name, string)
507
+ # @param name [String]: A name to use for the string's YAML key
508
+ def self.encryptString(string, name = nil)
441
509
  pwfile = vaultPasswordFile
442
510
  cmd = %Q{#{ansibleExecDir}/ansible-vault}
443
- if !system(cmd, "encrypt_string", string, "--name", name, "--vault-password-file", pwfile)
511
+
512
+ stdout, status = if name
513
+ Open3.capture2(cmd, "encrypt_string", string, "--name", name, "--vault-password-file", pwfile)
514
+ else
515
+ Open3.capture2(cmd, "encrypt_string", string, "--vault-password-file", pwfile)
516
+ end
517
+
518
+ if !status.success?
444
519
  raise MuError, "Failed Ansible command: #{cmd} encrypt_string <redacted> --name #{name} --vault-password-file"
445
520
  end
446
- output
521
+ stdout.strip
447
522
  end
448
523
 
449
524
  # Hunt down and return a path for a Python executable
@@ -630,6 +705,14 @@ module MU
630
705
  File.symlink(MU.myRoot+"/ansible/roles/"+role, roledir+"/"+role)
631
706
  }
632
707
 
708
+ coldir = "#{Etc.getpwuid(Process.uid).dir}/.ansible/collections/ansible_collections"
709
+ ["ansible.windows", "community.general.gem"].each { |coll|
710
+ %x{#{@ansible_execs}/ansible-galaxy collection list -p "#{coldir}"}
711
+ if $? != 0
712
+ system(%Q{#{@ansible_execs}/ansible-galaxy}, "collection", "install", coll, "-p", coldir)
713
+ end
714
+ }
715
+
633
716
  if @server.config['run_list']
634
717
  @server.config['run_list'].each { |role|
635
718
  found = false
@@ -655,6 +738,7 @@ module MU
655
738
  end
656
739
  }
657
740
  end
741
+
658
742
  end
659
743
 
660
744
  # Upload the certificate to a Chef Vault for this node
@@ -58,6 +58,9 @@ module MU
58
58
  require 'chef/knife/ssh'
59
59
  require 'mu/monkey_patches/chef_knife_ssh'
60
60
  require 'chef/knife/bootstrap'
61
+ require 'chef/knife/bootstrap/train_connector'
62
+ require 'chef/knife/bootstrap/chef_vault_handler'
63
+ require 'chef/knife/bootstrap/client_builder'
61
64
  require 'chef/knife/node_delete'
62
65
  require 'chef/knife/client_delete'
63
66
  require 'chef/knife/data_bag_delete'
@@ -96,11 +99,11 @@ module MU
96
99
  }
97
100
  end
98
101
 
99
- @knife = "cd #{MU.myRoot} && env -i HOME=#{Etc.getpwnam(MU.mu_user).dir} PATH=/opt/chef/embedded/bin:/usr/bin:/usr/sbin knife"
102
+ @@knife = "cd #{MU.myRoot} && env -i HOME=#{Etc.getpwnam(MU.mu_user).dir} PATH=/usr/local/ruby-current/bin:/opt/chef/embedded/bin:/usr/bin:/usr/sbin knife"
100
103
  # The canonical path to invoke Chef's *knife* utility with a clean environment.
101
104
  # @return [String]
102
- def self.knife;
103
- @knife;
105
+ def self.knife
106
+ @@knife
104
107
  end
105
108
 
106
109
  attr_reader :knife
@@ -218,7 +221,7 @@ module MU
218
221
  end
219
222
 
220
223
  # see {MU::Groomer::Chef.getSecret}
221
- def getSecret(vault: nil, item: nil, field: nil)
224
+ def getSecret(vault: @server.mu_name, item: nil, field: nil)
222
225
  self.class.getSecret(vault: vault, item: item, field: field)
223
226
  end
224
227
 
@@ -618,8 +621,10 @@ module MU
618
621
  kb.name_args = "#{canonical_addr}"
619
622
  kb.config[:distro] = 'chef-full'
620
623
  kb.config[:ssh_user] = ssh_user
624
+ kb.config[:ssh_verify_host_key] = :accept_new
621
625
  kb.config[:forward_agent] = ssh_user
622
626
  kb.config[:identity_file] = "#{Etc.getpwuid(Process.uid).dir}/.ssh/#{ssh_key_name}"
627
+ kb.config[:ssh_identity_file] = "#{Etc.getpwuid(Process.uid).dir}/.ssh/#{ssh_key_name}"
623
628
  else
624
629
  kb = ::Chef::Knife::BootstrapWindowsWinrm.new([@server.mu_name])
625
630
  kb.name_args = [@server.mu_name]
@@ -628,6 +633,7 @@ module MU
628
633
  kb.config[:winrm_port] = 5986
629
634
  kb.config[:session_timeout] = timeout
630
635
  kb.config[:operation_timeout] = timeout
636
+ # kb.config[:bootstrap_curl_options] = ""
631
637
  if retries % 2 == 0
632
638
  kb.config[:host] = canonical_addr
633
639
  kb.config[:winrm_authentication_protocol] = :basic
@@ -658,7 +664,9 @@ module MU
658
664
  kb.config[:json_attribs] = JSON.generate(json_attribs) if json_attribs.size > 1
659
665
  kb.config[:run_list] = run_list
660
666
  kb.config[:chef_node_name] = @server.mu_name
667
+ kb.config[:bootstrap_product] = "chef"
661
668
  kb.config[:bootstrap_version] = MU.chefVersion
669
+ kb.config[:channel] = "stable"
662
670
  # XXX key off of MU verbosity level
663
671
  kb.config[:log_level] = :debug
664
672
  # kb.config[:ssh_gateway] = "#{nat_ssh_user}@#{nat_ssh_host}" if !nat_ssh_host.nil? # Breaking bootsrap
@@ -898,7 +906,7 @@ retry
898
906
  vaults_to_clean.each { |vault|
899
907
  MU::MommaCat.lock("vault-#{vault['vault']}", false, true)
900
908
  MU.log "Purging unknown clients from #{vault['vault']} #{vault['item']}", MU::DEBUG
901
- output = %x{#{@knife} data bag show "#{vault['vault']}" "#{vault['item']}_keys" --format json}
909
+ output = %x{#{knife} data bag show "#{vault['vault']}" "#{vault['item']}_keys" --format json}
902
910
  # This is an ugly workaround for --clean-unknown-clients, which in
903
911
  # fact cleans known clients.
904
912
  if output
@@ -941,7 +949,7 @@ retry
941
949
  MU::MommaCat.lock("vault-#{vault}", false, true)
942
950
  MU.log "Granting #{host} access to #{vault} #{item}"
943
951
  begin
944
- ::Chef::Knife.run(['vault', 'update', vault, item, "--search", "name:#{host}"])
952
+ ::Chef::Knife.run(['vault', 'update', vault, item, "--clients", "#{host}"])
945
953
  rescue StandardError => e
946
954
  MU.log e.inspect, MU::ERR, details: caller
947
955
  end
@@ -1087,6 +1095,7 @@ retry
1087
1095
  def grantSecretAccess(vault, item)
1088
1096
  return if @secrets_granted["#{vault}:#{item}"] == item
1089
1097
  self.class.grantSecretAccess(@server.mu_name, vault, item)
1098
+ MU.log %Q{To retrieve secret #{vault}:#{item} - #{self.class.knife} vault show "#{vault}" "#{item}"}, MU::SUMMARY
1090
1099
  @secrets_granted["#{vault}:#{item}"] = item
1091
1100
  end
1092
1101
 
data/modules/mu/master.rb CHANGED
@@ -202,6 +202,7 @@ module MU
202
202
  else
203
203
  device.dup
204
204
  end
205
+
205
206
  alias_device = cryptfile ? "/dev/mapper/"+path.gsub(/[^0-9a-z_\-]/i, "_") : realdevice
206
207
 
207
208
  if !File.exist?(realdevice)
@@ -212,7 +213,7 @@ module MU
212
213
  cloud_id: MU.myInstanceId,
213
214
  kitten_cfg: {}
214
215
  )
215
- dummy_svr.addVolume(device, size)
216
+ dummy_svr.addVolume(dev: device, size: size)
216
217
  MU::Cloud::AWS::Server.tagVolumes(
217
218
  MU.myInstanceId,
218
219
  device: device,
@@ -228,7 +229,7 @@ module MU
228
229
  cloud_id: MU.myInstanceId,
229
230
  kitten_cfg: { 'project' => MU::Cloud::Google.myProject, 'availability_zone' => MU.myAZ }
230
231
  )
231
- dummy_svr.addVolume(device, size) # This will tag itself sensibly
232
+ dummy_svr.addVolume(dev: device, size: size) # This will tag itself sensibly
232
233
  else
233
234
  raise MuError, "Not in a familiar cloud, so I don't know how to create volumes for myself"
234
235
  end
@@ -271,14 +272,16 @@ module MU
271
272
  end
272
273
 
273
274
  %x{/usr/sbin/xfs_admin -l "#{alias_device}" > /dev/null 2>&1}
275
+
274
276
  if $?.exitstatus != 0
275
277
  MU.log "Formatting #{alias_device}", MU::NOTICE
276
278
  %x{/sbin/mkfs.xfs "#{alias_device}"}
277
279
  %x{/usr/sbin/xfs_admin -L "#{path.gsub(/[^0-9a-z_\-]/i, "_")}" "#{alias_device}"}
278
280
  end
279
281
  Dir.mkdir(path, 0700) if !Dir.exist?(path) # XXX recursive
282
+
280
283
  %x{/usr/sbin/xfs_info "#{alias_device}" > /dev/null 2>&1}
281
- if $?.exitstatus != 0
284
+ if $?.exitstatus == 0 and !File.open("/etc/mtab").read.match(/ #{path} /)
282
285
  MU.log "Mounting #{alias_device} to #{path}"
283
286
  %x{/bin/mount "#{alias_device}" "#{path}"}
284
287
  end
@@ -797,6 +800,7 @@ module MU
797
800
  nagios_threads = []
798
801
  nagios_threads << Thread.new {
799
802
  MU.dupGlobals(parent_thread_id)
803
+ Thread.current.thread_variable_set("syncMonitoringConfig", "<main>")
800
804
  realhome = Etc.getpwnam("nagios").dir
801
805
  [NAGIOS_HOME, "#{NAGIOS_HOME}/.ssh"].each { |dir|
802
806
  Dir.mkdir(dir, 0711) if !Dir.exist?(dir)
@@ -839,6 +843,7 @@ module MU
839
843
  MU.dupGlobals(parent_thread_id)
840
844
  threads << Thread.new {
841
845
  MU::MommaCat.setThreadContext(deploy)
846
+ Thread.current.thread_variable_set("syncMonitoringConfig",server.mu_name)
842
847
  MU.log "Adding #{server.mu_name} to #{NAGIOS_HOME}/.ssh/config", MU::DEBUG
843
848
  MU::Master.addHostToSSHConfig(
844
849
  server,
@@ -941,7 +946,7 @@ module MU
941
946
  return true if l =~ /^\/dev\/nvme\d/
942
947
  }
943
948
  else
944
- return true if File.exists?("/dev/nvme0n1")
949
+ return true if File.exist?("/dev/nvme0n1")
945
950
  end
946
951
  false
947
952
  end
@@ -213,6 +213,7 @@ module MU
213
213
  need_reload = false
214
214
  @cleanup_threads << Thread.new {
215
215
  MU.dupGlobals(parent_thread_id)
216
+ Thread.current.thread_variable_set("cleanTerminatedInstances", deploy_id)
216
217
  deploy = MU::MommaCat.getLitter(deploy_id, set_context_to_me: true)
217
218
  purged_this_deploy = 0
218
219
  MU.log "#{deploy_id} has some kittens in it", loglevel, details: deploy.kittens.keys
@@ -225,7 +226,8 @@ module MU
225
226
  servers.each_pair { |mu_name, server|
226
227
  server.describe
227
228
  if !server.cloud_id
228
- MU.log "Checking for presence of #{mu_name}, but unable to fetch its cloud_id", MU::WARN, details: server
229
+ MU.log "Checking for presence of instance '#{mu_name}', but unable to fetch its cloud_id", MU::WARN, server.class.name
230
+ pp servers.keys
229
231
  elsif !server.active?
230
232
  next if File.exist?(deploy_dir(deploy_id)+"/.cleanup-"+server.cloud_id)
231
233
  deletia << mu_name
@@ -313,7 +315,7 @@ module MU
313
315
  return 0
314
316
  end
315
317
 
316
- File.unlink(daemonPidFile) if File.exists?(daemonPidFile)
318
+ File.unlink(daemonPidFile) if File.exist?(daemonPidFile)
317
319
  MU.log "Starting Momma Cat on port #{MU.mommaCatPort}, logging to #{daemonLogFile}, PID file #{daemonPidFile}"
318
320
  origdir = Dir.getwd
319
321
  Dir.chdir(MU.myRoot+"/modules")
@@ -305,9 +305,8 @@ module MU
305
305
  zones = MU::Cloud::DNSZone.find(cloud_id: "platform-mu")
306
306
  mu_zone = zones.values.first if !zones.nil?
307
307
  end
308
-
309
308
  if !mu_zone.nil?
310
- MU::Cloud::DNSZone.genericMuDNSEntry(name: node.gsub(/[^a-z0-9!"\#$%&'\(\)\*\+,\-\/:;<=>\?@\[\]\^_`{\|}~\.]/, '-').gsub(/--|^-/, ''), target: server.canonicalIP, cloudclass: MU::Cloud::Server, sync_wait: sync_wait)
309
+ MU::Cloud::DNSZone.genericMuDNSEntry(name: node.gsub(/[^a-z0-9!"\#$%&'\(\)\*\+,\-\/:;<=>\?@\[\]\^_`{\|}~\.]/i, '-').gsub(/--|^-/, ''), target: server.canonicalIP, cloudclass: MU::Cloud::Server, sync_wait: sync_wait)
311
310
  else
312
311
  MU::Master.addInstanceToEtcHosts(server.canonicalIP, node)
313
312
  end
@@ -179,6 +179,7 @@ module MU
179
179
  # return [false, nil]
180
180
  def self.lock(id, nonblock = false, global = false, retries: 0, deploy_id: MU.deploy_id)
181
181
  raise MuError, "Can't pass a nil id to MU::MommaCat.lock" if id.nil?
182
+ called_by = caller[0]
182
183
 
183
184
  if !global
184
185
  lockdir = "#{deploy_dir(deploy_id)}/locks"
@@ -200,6 +201,10 @@ module MU
200
201
  @locks[Thread.current.object_id][id] = File.open("#{lockdir}/#{id}.lock", File::CREAT|File::RDWR, 0600)
201
202
  }
202
203
 
204
+ thr_to_s = Proc.new { |t|
205
+ "#{t.object_id} (#{t.thread_variables.map { |v| "#{v.to_s}: #{t.thread_variable_get(v).to_s}" }.join(", ")})"
206
+ }
207
+
203
208
  MU.log "Getting a lock on #{lockdir}/#{id}.lock (thread #{Thread.current.object_id})...", MU::DEBUG, details: caller
204
209
  show_relevant = Proc.new {
205
210
  @lock_semaphore.synchronize {
@@ -208,7 +213,7 @@ module MU
208
213
  if lockid == id
209
214
  thread = Thread.list.select { |t| t.object_id == thread_id }.first
210
215
  if thread.object_id != Thread.current.object_id
211
- MU.log "#{thread_id} sitting on #{id} (#{thread.thread_variables.map { |v| "#{v.to_s}: #{thread.thread_variable_get(v).to_s}" }.join(", ")})", MU::WARN, thread.backtrace
216
+ MU.log "Thread #{thr_to_s.call(thread)} sitting on #{id}, which is needed by #{thr_to_s.call(Thread.current)} at #{called_by}", MU::WARN, thread.backtrace
212
217
  end
213
218
  end
214
219
  }
@@ -585,7 +590,7 @@ module MU
585
590
  orig_cfg = findResourceConfig(type, res_name)
586
591
 
587
592
  if orig_cfg.nil?
588
- MU.log "Failed to locate original config for #{attrs[:cfg_name]} #{res_name} in #{@deploy_id}", MU::WARN if !["firewall_rules", "databases", "storage_pools", "cache_clusters", "alarms"].include?(type) # XXX shaddap
593
+ MU.log "Failed to locate original config for #{attrs[:cfg_name]} #{res_name}, seen in cached deployment.json for #{@deploy_id}", MU::DEBUG
589
594
  next
590
595
  end
591
596
 
@@ -150,6 +150,7 @@ module MU
150
150
  @deploy_id = deploy_id
151
151
  @mu_user = mu_user.dup
152
152
  @no_artifacts = no_artifacts
153
+ @ssh_key_generated = false
153
154
 
154
155
  # Make sure mu_user and chef_user are sane.
155
156
  if @mu_user == "root"
@@ -167,7 +168,6 @@ module MU
167
168
  @need_deploy_flush = false
168
169
  @node_cert_semaphore = Mutex.new
169
170
  @deployment = deployment_data
170
-
171
171
  @deployment['mu_public_ip'] = MU.mu_public_ip
172
172
  @private_key = nil
173
173
  @public_key = nil
@@ -175,6 +175,7 @@ module MU
175
175
  @secrets['instance_secret'] = Hash.new
176
176
  @secrets['windows_admin_password'] = Hash.new
177
177
  @ssh_key_name = ssh_key_name
178
+ @ssh_key_name ||= "deploy-#{@deploy_id}"
178
179
  @ssh_private_key = ssh_private_key
179
180
  @ssh_public_key = ssh_public_key
180
181
  @clouds = {}
@@ -195,6 +196,7 @@ module MU
195
196
  setDeploySecret
196
197
  MU::MommaCat.setThreadContext(self) if set_context_to_me
197
198
  save!
199
+ generatePasswords
198
200
  end
199
201
 
200
202
  @appname ||= MU.appname
@@ -202,6 +204,7 @@ module MU
202
204
  @environment ||= MU.environment
203
205
 
204
206
  loadDeploy(set_context_to_me: set_context_to_me)
207
+ @deployment['mu_all_ips'] ||= [MU.mu_public_ip, MU.my_private_ip].uniq
205
208
  if !deploy_secret.nil? and !authKey(deploy_secret)
206
209
  raise DeployInitializeError, "Client request did not include a valid deploy authorization secret. Verify that userdata runs correctly?"
207
210
  end
@@ -224,6 +227,29 @@ module MU
224
227
  # if !@@litter_semaphore.owned?
225
228
  end # end of initialize()
226
229
 
230
+ def generatePasswords
231
+ return if !@original_config['generate_passwords']
232
+
233
+ @original_config['generate_passwords'].each { |pw|
234
+ password = MU.generatePassword(safe_pattern: pw['safe_chars'], length: pw['minlength'])
235
+ MU.supportedGroomers.each { |g|
236
+ groomclass = MU.loadGroomer(g)
237
+ begin
238
+ groomclass.getSecret(vault: @deploy_id,
239
+ item: pw['itemname'],
240
+ field: 'password')
241
+ rescue MU::Groomer::MuNoSuchSecret
242
+ pwdata = { "password" => password }
243
+ pwdata["username"] = pw["username"] if pw["username"]
244
+ groomclass.saveSecret(vault: @deploy_id,
245
+ item: pw['itemname'],
246
+ data: pwdata,
247
+ permissions: (g == "Ansible"))
248
+ end
249
+ }
250
+ }
251
+ end
252
+
227
253
  # List all the cloud providers declared by resources in our deploy.
228
254
  def cloudsUsed
229
255
  seen = []
@@ -490,7 +516,7 @@ module MU
490
516
  # Return the parts and pieces of this deploy's node ssh key set. Generate
491
517
  # or load if that hasn't been done already.
492
518
  def SSHKey
493
- return [@ssh_key_name, @ssh_private_key, @ssh_public_key] if !@ssh_key_name.nil?
519
+ return [@ssh_key_name, @ssh_private_key, @ssh_public_key] if @ssh_key_generated
494
520
  if numKittens(types: ["Server", "ServerPool", "ContainerCluster"]) == 0
495
521
  return []
496
522
  end
@@ -535,6 +561,7 @@ module MU
535
561
  }
536
562
  end
537
563
 
564
+ @ssh_key_generated = true
538
565
  return [@ssh_key_name, @ssh_private_key, @ssh_public_key]
539
566
  end
540
567
 
@@ -549,8 +576,8 @@ module MU
549
576
  # @param triggering_node [MU::Cloud]: A cloud object calling this notify, usually on behalf of itself
550
577
  # @param remove [Boolean]: Remove this resource from the deploy structure, instead of adding it.
551
578
  # @return [void]
552
- def notify(type, key, data, mu_name: nil, remove: false, triggering_node: nil, delayed_save: false)
553
- no_write = (@no_artifacts or !caller.grep(/\/mommacat\.rb:\d+:in `notify'/).empty?)
579
+ def notify(type, key, data, mu_name: nil, remove: false, triggering_node: nil, delayed_save: false, no_write: nil)
580
+ no_write ||= (@no_artifacts or !caller.grep(/\/mommacat\.rb:\d+:in `notify'/).empty?)
554
581
 
555
582
  begin
556
583
  if !no_write
@@ -831,7 +858,7 @@ MAIL_HEAD_END
831
858
  next if sibling.config.has_key?("groom") and !sibling.config["groom"]
832
859
  threads << Thread.new {
833
860
  Thread.abort_on_exception = true
834
- Thread.current.thread_variable_set("name", "sync-"+sibling.mu_name.downcase)
861
+ Thread.current.thread_variable_set("syncLitterThread", sibling.mu_name)
835
862
  MU.setVar("syncLitterThread", true)
836
863
  begin
837
864
  sibling.groomer.saveDeployData
@@ -874,7 +901,7 @@ MAIL_HEAD_END
874
901
  sans << resource.mu_name.downcase if resource.mu_name and resource.mu_name != cert_cn
875
902
  # XXX were there other names we wanted to include?
876
903
  key = MU::Master::SSL.getKey(cert_cn, keysize: keysize)
877
- cert, pfx_cert = MU::Master::SSL.getCert(cert_cn, "/CN=#{cert_cn}/O=Mu/C=US", sans: sans, pfx: is_windows)
904
+ cert, pfx_cert = MU::Master::SSL.getCert(cert_cn, "/CN=#{cert_cn}/O=Mu/C=US", sans: sans, pfx: true)
878
905
  results[cert_cn] = [key, cert]
879
906
 
880
907
  winrm_cert = nil