cloud-mu 2.1.0beta → 3.0.0beta

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.
Files changed (291) hide show
  1. checksums.yaml +5 -5
  2. data/Berksfile +4 -5
  3. data/Berksfile.lock +179 -0
  4. data/README.md +1 -6
  5. data/ansible/roles/geerlingguy.firewall/templates/firewall.bash.j2 +0 -0
  6. data/ansible/roles/mu-installer/README.md +33 -0
  7. data/ansible/roles/mu-installer/defaults/main.yml +2 -0
  8. data/ansible/roles/mu-installer/handlers/main.yml +2 -0
  9. data/ansible/roles/mu-installer/meta/main.yml +60 -0
  10. data/ansible/roles/mu-installer/tasks/main.yml +13 -0
  11. data/ansible/roles/mu-installer/tests/inventory +2 -0
  12. data/ansible/roles/mu-installer/tests/test.yml +5 -0
  13. data/ansible/roles/mu-installer/vars/main.yml +2 -0
  14. data/bin/mu-adopt +125 -0
  15. data/bin/mu-aws-setup +4 -4
  16. data/bin/mu-azure-setup +265 -0
  17. data/bin/mu-azure-tests +43 -0
  18. data/bin/mu-cleanup +20 -8
  19. data/bin/mu-configure +224 -98
  20. data/bin/mu-deploy +8 -3
  21. data/bin/mu-gcp-setup +16 -8
  22. data/bin/mu-gen-docs +92 -8
  23. data/bin/mu-load-config.rb +52 -12
  24. data/bin/mu-momma-cat +36 -0
  25. data/bin/mu-node-manage +34 -27
  26. data/bin/mu-self-update +2 -2
  27. data/bin/mu-ssh +12 -8
  28. data/bin/mu-upload-chef-artifacts +11 -4
  29. data/bin/mu-user-manage +3 -0
  30. data/cloud-mu.gemspec +8 -11
  31. data/cookbooks/firewall/libraries/helpers_iptables.rb +2 -2
  32. data/cookbooks/firewall/metadata.json +1 -1
  33. data/cookbooks/firewall/recipes/default.rb +5 -9
  34. data/cookbooks/mu-firewall/attributes/default.rb +2 -0
  35. data/cookbooks/mu-firewall/metadata.rb +1 -1
  36. data/cookbooks/mu-glusterfs/templates/default/mu-gluster-client.erb +0 -0
  37. data/cookbooks/mu-master/Berksfile +2 -2
  38. data/cookbooks/mu-master/files/default/check_mem.pl +0 -0
  39. data/cookbooks/mu-master/files/default/cloudamatic.png +0 -0
  40. data/cookbooks/mu-master/metadata.rb +5 -4
  41. data/cookbooks/mu-master/recipes/389ds.rb +1 -1
  42. data/cookbooks/mu-master/recipes/basepackages.rb +30 -10
  43. data/cookbooks/mu-master/recipes/default.rb +59 -7
  44. data/cookbooks/mu-master/recipes/firewall-holes.rb +1 -1
  45. data/cookbooks/mu-master/recipes/init.rb +65 -47
  46. data/cookbooks/mu-master/recipes/{eks-kubectl.rb → kubectl.rb} +4 -10
  47. data/cookbooks/mu-master/recipes/sssd.rb +2 -1
  48. data/cookbooks/mu-master/recipes/update_nagios_only.rb +6 -6
  49. data/cookbooks/mu-master/templates/default/web_app.conf.erb +2 -2
  50. data/cookbooks/mu-master/templates/mods/ldap.conf.erb +4 -0
  51. data/cookbooks/mu-php54/Berksfile +1 -2
  52. data/cookbooks/mu-php54/metadata.rb +4 -5
  53. data/cookbooks/mu-php54/recipes/default.rb +1 -1
  54. data/cookbooks/mu-splunk/templates/default/splunk-init.erb +0 -0
  55. data/cookbooks/mu-tools/Berksfile +3 -2
  56. data/cookbooks/mu-tools/files/default/Mu_CA.pem +33 -0
  57. data/cookbooks/mu-tools/libraries/helper.rb +20 -8
  58. data/cookbooks/mu-tools/metadata.rb +5 -2
  59. data/cookbooks/mu-tools/recipes/apply_security.rb +2 -3
  60. data/cookbooks/mu-tools/recipes/eks.rb +1 -1
  61. data/cookbooks/mu-tools/recipes/gcloud.rb +5 -30
  62. data/cookbooks/mu-tools/recipes/nagios.rb +1 -1
  63. data/cookbooks/mu-tools/recipes/rsyslog.rb +1 -0
  64. data/cookbooks/mu-tools/recipes/selinux.rb +19 -0
  65. data/cookbooks/mu-tools/recipes/split_var_partitions.rb +0 -1
  66. data/cookbooks/mu-tools/recipes/windows-client.rb +256 -122
  67. data/cookbooks/mu-tools/resources/disk.rb +3 -1
  68. data/cookbooks/mu-tools/templates/amazon/sshd_config.erb +1 -1
  69. data/cookbooks/mu-tools/templates/default/etc_hosts.erb +1 -1
  70. data/cookbooks/mu-tools/templates/default/{kubeconfig.erb → kubeconfig-eks.erb} +0 -0
  71. data/cookbooks/mu-tools/templates/default/kubeconfig-gke.erb +27 -0
  72. data/cookbooks/mu-tools/templates/windows-10/sshd_config.erb +137 -0
  73. data/cookbooks/mu-utility/recipes/nat.rb +4 -0
  74. data/extras/alpha.png +0 -0
  75. data/extras/beta.png +0 -0
  76. data/extras/clean-stock-amis +2 -2
  77. data/extras/generate-stock-images +131 -0
  78. data/extras/git-fix-permissions-hook +0 -0
  79. data/extras/image-generators/AWS/centos6.yaml +17 -0
  80. data/extras/image-generators/{aws → AWS}/centos7-govcloud.yaml +0 -0
  81. data/extras/image-generators/{aws → AWS}/centos7.yaml +0 -0
  82. data/extras/image-generators/{aws → AWS}/rhel7.yaml +0 -0
  83. data/extras/image-generators/{aws → AWS}/win2k12.yaml +0 -0
  84. data/extras/image-generators/{aws → AWS}/win2k16.yaml +0 -0
  85. data/extras/image-generators/{aws → AWS}/windows.yaml +0 -0
  86. data/extras/image-generators/{gcp → Google}/centos6.yaml +1 -0
  87. data/extras/image-generators/Google/centos7.yaml +18 -0
  88. data/extras/python_rpm/build.sh +0 -0
  89. data/extras/release.png +0 -0
  90. data/extras/ruby_rpm/build.sh +0 -0
  91. data/extras/ruby_rpm/muby.spec +1 -1
  92. data/install/README.md +43 -5
  93. data/install/deprecated-bash-library.sh +0 -0
  94. data/install/installer +1 -1
  95. data/install/jenkinskeys.rb +0 -0
  96. data/install/mu-master.yaml +55 -0
  97. data/modules/mommacat.ru +41 -7
  98. data/modules/mu.rb +444 -149
  99. data/modules/mu/adoption.rb +500 -0
  100. data/modules/mu/cleanup.rb +235 -158
  101. data/modules/mu/cloud.rb +675 -138
  102. data/modules/mu/clouds/aws.rb +156 -24
  103. data/modules/mu/clouds/aws/alarm.rb +4 -14
  104. data/modules/mu/clouds/aws/bucket.rb +60 -18
  105. data/modules/mu/clouds/aws/cache_cluster.rb +8 -20
  106. data/modules/mu/clouds/aws/collection.rb +12 -22
  107. data/modules/mu/clouds/aws/container_cluster.rb +209 -118
  108. data/modules/mu/clouds/aws/database.rb +120 -45
  109. data/modules/mu/clouds/aws/dnszone.rb +7 -18
  110. data/modules/mu/clouds/aws/endpoint.rb +5 -15
  111. data/modules/mu/clouds/aws/firewall_rule.rb +144 -72
  112. data/modules/mu/clouds/aws/folder.rb +4 -11
  113. data/modules/mu/clouds/aws/function.rb +6 -16
  114. data/modules/mu/clouds/aws/group.rb +4 -12
  115. data/modules/mu/clouds/aws/habitat.rb +11 -13
  116. data/modules/mu/clouds/aws/loadbalancer.rb +40 -28
  117. data/modules/mu/clouds/aws/log.rb +5 -13
  118. data/modules/mu/clouds/aws/msg_queue.rb +9 -24
  119. data/modules/mu/clouds/aws/nosqldb.rb +4 -12
  120. data/modules/mu/clouds/aws/notifier.rb +6 -13
  121. data/modules/mu/clouds/aws/role.rb +69 -40
  122. data/modules/mu/clouds/aws/search_domain.rb +17 -20
  123. data/modules/mu/clouds/aws/server.rb +184 -94
  124. data/modules/mu/clouds/aws/server_pool.rb +33 -38
  125. data/modules/mu/clouds/aws/storage_pool.rb +5 -12
  126. data/modules/mu/clouds/aws/user.rb +59 -33
  127. data/modules/mu/clouds/aws/userdata/linux.erb +18 -30
  128. data/modules/mu/clouds/aws/userdata/windows.erb +9 -9
  129. data/modules/mu/clouds/aws/vpc.rb +214 -145
  130. data/modules/mu/clouds/azure.rb +978 -44
  131. data/modules/mu/clouds/azure/container_cluster.rb +413 -0
  132. data/modules/mu/clouds/azure/firewall_rule.rb +500 -0
  133. data/modules/mu/clouds/azure/habitat.rb +167 -0
  134. data/modules/mu/clouds/azure/loadbalancer.rb +205 -0
  135. data/modules/mu/clouds/azure/role.rb +211 -0
  136. data/modules/mu/clouds/azure/server.rb +810 -0
  137. data/modules/mu/clouds/azure/user.rb +257 -0
  138. data/modules/mu/clouds/azure/userdata/README.md +4 -0
  139. data/modules/mu/clouds/azure/userdata/linux.erb +137 -0
  140. data/modules/mu/clouds/azure/userdata/windows.erb +275 -0
  141. data/modules/mu/clouds/azure/vpc.rb +782 -0
  142. data/modules/mu/clouds/cloudformation.rb +12 -9
  143. data/modules/mu/clouds/cloudformation/firewall_rule.rb +5 -13
  144. data/modules/mu/clouds/cloudformation/server.rb +10 -1
  145. data/modules/mu/clouds/cloudformation/server_pool.rb +1 -0
  146. data/modules/mu/clouds/cloudformation/vpc.rb +0 -2
  147. data/modules/mu/clouds/google.rb +554 -117
  148. data/modules/mu/clouds/google/bucket.rb +173 -32
  149. data/modules/mu/clouds/google/container_cluster.rb +1112 -157
  150. data/modules/mu/clouds/google/database.rb +24 -47
  151. data/modules/mu/clouds/google/firewall_rule.rb +344 -89
  152. data/modules/mu/clouds/google/folder.rb +156 -79
  153. data/modules/mu/clouds/google/group.rb +272 -82
  154. data/modules/mu/clouds/google/habitat.rb +177 -52
  155. data/modules/mu/clouds/google/loadbalancer.rb +9 -34
  156. data/modules/mu/clouds/google/role.rb +1211 -0
  157. data/modules/mu/clouds/google/server.rb +491 -227
  158. data/modules/mu/clouds/google/server_pool.rb +233 -48
  159. data/modules/mu/clouds/google/user.rb +479 -125
  160. data/modules/mu/clouds/google/userdata/linux.erb +3 -3
  161. data/modules/mu/clouds/google/userdata/windows.erb +9 -9
  162. data/modules/mu/clouds/google/vpc.rb +381 -223
  163. data/modules/mu/config.rb +689 -214
  164. data/modules/mu/config/bucket.rb +1 -1
  165. data/modules/mu/config/cache_cluster.rb +1 -1
  166. data/modules/mu/config/cache_cluster.yml +0 -4
  167. data/modules/mu/config/container_cluster.rb +18 -9
  168. data/modules/mu/config/database.rb +6 -23
  169. data/modules/mu/config/firewall_rule.rb +9 -15
  170. data/modules/mu/config/folder.rb +22 -21
  171. data/modules/mu/config/habitat.rb +22 -21
  172. data/modules/mu/config/loadbalancer.rb +2 -2
  173. data/modules/mu/config/role.rb +9 -40
  174. data/modules/mu/config/server.rb +26 -5
  175. data/modules/mu/config/server_pool.rb +1 -1
  176. data/modules/mu/config/storage_pool.rb +2 -2
  177. data/modules/mu/config/user.rb +4 -0
  178. data/modules/mu/config/vpc.rb +350 -110
  179. data/modules/mu/defaults/{amazon_images.yaml → AWS.yaml} +37 -39
  180. data/modules/mu/defaults/Azure.yaml +17 -0
  181. data/modules/mu/defaults/Google.yaml +24 -0
  182. data/modules/mu/defaults/README.md +1 -1
  183. data/modules/mu/deploy.rb +168 -125
  184. data/modules/mu/groomer.rb +2 -1
  185. data/modules/mu/groomers/ansible.rb +104 -32
  186. data/modules/mu/groomers/chef.rb +96 -44
  187. data/modules/mu/kittens.rb +20602 -0
  188. data/modules/mu/logger.rb +38 -11
  189. data/modules/mu/master.rb +90 -8
  190. data/modules/mu/master/chef.rb +2 -3
  191. data/modules/mu/master/ldap.rb +0 -1
  192. data/modules/mu/master/ssl.rb +250 -0
  193. data/modules/mu/mommacat.rb +917 -513
  194. data/modules/scratchpad.erb +1 -1
  195. data/modules/tests/super_complex_bok.yml +0 -0
  196. data/modules/tests/super_simple_bok.yml +0 -0
  197. data/roles/mu-master.json +2 -1
  198. data/spec/azure_creds +5 -0
  199. data/spec/mu.yaml +56 -0
  200. data/spec/mu/clouds/azure_spec.rb +164 -27
  201. data/spec/spec_helper.rb +5 -0
  202. data/test/clean_up.py +0 -0
  203. data/test/exec_inspec.py +0 -0
  204. data/test/exec_mu_install.py +0 -0
  205. data/test/exec_retry.py +0 -0
  206. data/test/smoke_test.rb +0 -0
  207. metadata +90 -118
  208. data/cookbooks/mu-jenkins/Berksfile +0 -14
  209. data/cookbooks/mu-jenkins/CHANGELOG.md +0 -13
  210. data/cookbooks/mu-jenkins/LICENSE +0 -37
  211. data/cookbooks/mu-jenkins/README.md +0 -105
  212. data/cookbooks/mu-jenkins/attributes/default.rb +0 -42
  213. data/cookbooks/mu-jenkins/files/default/cleanup_deploy_config.xml +0 -73
  214. data/cookbooks/mu-jenkins/files/default/deploy_config.xml +0 -44
  215. data/cookbooks/mu-jenkins/metadata.rb +0 -21
  216. data/cookbooks/mu-jenkins/recipes/default.rb +0 -195
  217. data/cookbooks/mu-jenkins/recipes/node-ssh-config.rb +0 -54
  218. data/cookbooks/mu-jenkins/recipes/public_key.rb +0 -24
  219. data/cookbooks/mu-jenkins/templates/default/example_job.config.xml.erb +0 -24
  220. data/cookbooks/mu-jenkins/templates/default/org.jvnet.hudson.plugins.SSHBuildWrapper.xml.erb +0 -14
  221. data/cookbooks/mu-jenkins/templates/default/ssh_config.erb +0 -6
  222. data/cookbooks/nagios/Berksfile +0 -11
  223. data/cookbooks/nagios/CHANGELOG.md +0 -589
  224. data/cookbooks/nagios/CONTRIBUTING.md +0 -11
  225. data/cookbooks/nagios/LICENSE +0 -37
  226. data/cookbooks/nagios/README.md +0 -328
  227. data/cookbooks/nagios/TESTING.md +0 -2
  228. data/cookbooks/nagios/attributes/config.rb +0 -171
  229. data/cookbooks/nagios/attributes/default.rb +0 -228
  230. data/cookbooks/nagios/chefignore +0 -102
  231. data/cookbooks/nagios/definitions/command.rb +0 -33
  232. data/cookbooks/nagios/definitions/contact.rb +0 -33
  233. data/cookbooks/nagios/definitions/contactgroup.rb +0 -33
  234. data/cookbooks/nagios/definitions/host.rb +0 -33
  235. data/cookbooks/nagios/definitions/hostdependency.rb +0 -33
  236. data/cookbooks/nagios/definitions/hostescalation.rb +0 -34
  237. data/cookbooks/nagios/definitions/hostgroup.rb +0 -33
  238. data/cookbooks/nagios/definitions/nagios_conf.rb +0 -38
  239. data/cookbooks/nagios/definitions/resource.rb +0 -33
  240. data/cookbooks/nagios/definitions/service.rb +0 -33
  241. data/cookbooks/nagios/definitions/servicedependency.rb +0 -33
  242. data/cookbooks/nagios/definitions/serviceescalation.rb +0 -34
  243. data/cookbooks/nagios/definitions/servicegroup.rb +0 -33
  244. data/cookbooks/nagios/definitions/timeperiod.rb +0 -33
  245. data/cookbooks/nagios/libraries/base.rb +0 -314
  246. data/cookbooks/nagios/libraries/command.rb +0 -91
  247. data/cookbooks/nagios/libraries/contact.rb +0 -230
  248. data/cookbooks/nagios/libraries/contactgroup.rb +0 -112
  249. data/cookbooks/nagios/libraries/custom_option.rb +0 -36
  250. data/cookbooks/nagios/libraries/data_bag_helper.rb +0 -23
  251. data/cookbooks/nagios/libraries/default.rb +0 -90
  252. data/cookbooks/nagios/libraries/host.rb +0 -412
  253. data/cookbooks/nagios/libraries/hostdependency.rb +0 -181
  254. data/cookbooks/nagios/libraries/hostescalation.rb +0 -173
  255. data/cookbooks/nagios/libraries/hostgroup.rb +0 -119
  256. data/cookbooks/nagios/libraries/nagios.rb +0 -282
  257. data/cookbooks/nagios/libraries/resource.rb +0 -59
  258. data/cookbooks/nagios/libraries/service.rb +0 -455
  259. data/cookbooks/nagios/libraries/servicedependency.rb +0 -215
  260. data/cookbooks/nagios/libraries/serviceescalation.rb +0 -195
  261. data/cookbooks/nagios/libraries/servicegroup.rb +0 -144
  262. data/cookbooks/nagios/libraries/timeperiod.rb +0 -160
  263. data/cookbooks/nagios/libraries/users_helper.rb +0 -54
  264. data/cookbooks/nagios/metadata.rb +0 -25
  265. data/cookbooks/nagios/recipes/_load_databag_config.rb +0 -153
  266. data/cookbooks/nagios/recipes/_load_default_config.rb +0 -241
  267. data/cookbooks/nagios/recipes/apache.rb +0 -48
  268. data/cookbooks/nagios/recipes/default.rb +0 -204
  269. data/cookbooks/nagios/recipes/nginx.rb +0 -82
  270. data/cookbooks/nagios/recipes/pagerduty.rb +0 -143
  271. data/cookbooks/nagios/recipes/server_package.rb +0 -40
  272. data/cookbooks/nagios/recipes/server_source.rb +0 -164
  273. data/cookbooks/nagios/templates/default/apache2.conf.erb +0 -96
  274. data/cookbooks/nagios/templates/default/cgi.cfg.erb +0 -266
  275. data/cookbooks/nagios/templates/default/commands.cfg.erb +0 -13
  276. data/cookbooks/nagios/templates/default/contacts.cfg.erb +0 -37
  277. data/cookbooks/nagios/templates/default/hostgroups.cfg.erb +0 -25
  278. data/cookbooks/nagios/templates/default/hosts.cfg.erb +0 -15
  279. data/cookbooks/nagios/templates/default/htpasswd.users.erb +0 -6
  280. data/cookbooks/nagios/templates/default/nagios.cfg.erb +0 -22
  281. data/cookbooks/nagios/templates/default/nginx.conf.erb +0 -62
  282. data/cookbooks/nagios/templates/default/pagerduty.cgi.erb +0 -185
  283. data/cookbooks/nagios/templates/default/resource.cfg.erb +0 -27
  284. data/cookbooks/nagios/templates/default/servicedependencies.cfg.erb +0 -15
  285. data/cookbooks/nagios/templates/default/servicegroups.cfg.erb +0 -14
  286. data/cookbooks/nagios/templates/default/services.cfg.erb +0 -14
  287. data/cookbooks/nagios/templates/default/templates.cfg.erb +0 -31
  288. data/cookbooks/nagios/templates/default/timeperiods.cfg.erb +0 -13
  289. data/extras/image-generators/aws/centos6.yaml +0 -18
  290. data/modules/mu/defaults/google_images.yaml +0 -16
  291. data/roles/mu-master-jenkins.json +0 -24
@@ -36,21 +36,24 @@ module MU
36
36
  @verbosity = MU::Logger::NORMAL
37
37
  @quiet = false
38
38
  @html = false
39
+ @color = true
39
40
  @handle = STDOUT
40
41
 
41
42
  @@log_semaphere = Mutex.new
42
43
 
43
44
  # @param verbosity [Integer]: See {MU::Logger.QUIET}, {MU::Logger.NORMAL}, {MU::Logger.LOUD}
44
45
  # @param html [Boolean]: Enable web-friendly log output.
45
- def initialize(verbosity=MU::Logger::NORMAL, html=false, handle=STDOUT)
46
+ def initialize(verbosity=MU::Logger::NORMAL, html=false, handle=STDOUT, color=true)
46
47
  @verbosity = verbosity
47
48
  @html = html
48
49
  @handle = handle
50
+ @color = color
49
51
  @summary = []
50
52
  end
51
53
 
52
54
  attr_reader :summary
53
55
  attr_accessor :verbosity
56
+ attr_accessor :color
54
57
  attr_accessor :quiet
55
58
  attr_accessor :html
56
59
  attr_accessor :handle
@@ -65,7 +68,8 @@ module MU
65
68
  details: nil,
66
69
  html: @html,
67
70
  verbosity: @verbosity,
68
- handle: @handle
71
+ handle: @handle,
72
+ color: @color
69
73
  )
70
74
  verbosity = MU::Logger::NORMAL if verbosity.nil?
71
75
  return if verbosity == MU::Logger::SILENT
@@ -98,12 +102,14 @@ module MU
98
102
  # We get passed literal quoted newlines sometimes, fix 'em. Get Windows'
99
103
  # ugly line feeds too.
100
104
  if !details.nil?
105
+ details = details.dup # in case it's frozen or something
101
106
  details.gsub!(/\\n/, "\n")
102
107
  details.gsub!(/(\\r|\r)/, "")
103
108
  end
104
109
 
105
110
  msg = msg.first if msg.is_a?(Array)
106
111
  msg = "" if msg == nil
112
+ msg = msg.to_s if !msg.is_a?(String) and msg.respond_to?(:to_s)
107
113
 
108
114
  @@log_semaphere.synchronize {
109
115
  case level
@@ -114,9 +120,12 @@ module MU
114
120
  if @html
115
121
  html_out "#{time} - #{caller_name} - #{msg}", "orange"
116
122
  html_out " #{details}" if details
117
- else
123
+ elsif color
118
124
  handle.puts "#{time} - #{caller_name} - #{msg}".yellow.on_black
119
125
  handle.puts "#{details}".white.on_black if details
126
+ else
127
+ handle.puts "#{time} - #{caller_name} - #{msg}"
128
+ handle.puts "#{details}" if details
120
129
  end
121
130
  Syslog.log(Syslog::LOG_DEBUG, msg.gsub(/%/, ''))
122
131
  Syslog.log(Syslog::LOG_DEBUG, details.gsub(/%/, '')) if details
@@ -125,14 +134,18 @@ module MU
125
134
  if verbosity >= MU::Logger::NORMAL
126
135
  if @html
127
136
  html_out "#{time} - #{caller_name} - #{msg}", "green"
128
- else
137
+ elsif color
129
138
  handle.puts "#{time} - #{caller_name} - #{msg}".green.on_black
139
+ else
140
+ handle.puts "#{time} - #{caller_name} - #{msg}"
130
141
  end
131
142
  if verbosity >= MU::Logger::LOUD
132
143
  if @html
133
144
  html_out " #{details}"
134
- else
145
+ elsif color
135
146
  handle.puts "#{details}".white.on_black if details
147
+ else
148
+ handle.puts "#{details}" if details
136
149
  end
137
150
  end
138
151
  Syslog.log(Syslog::LOG_NOTICE, msg.gsub(/%/, ''))
@@ -141,14 +154,18 @@ module MU
141
154
  when NOTICE
142
155
  if @html
143
156
  html_out "#{time} - #{caller_name} - #{msg}", "yellow"
144
- else
157
+ elsif color
145
158
  handle.puts "#{time} - #{caller_name} - #{msg}".yellow.on_black
159
+ else
160
+ handle.puts "#{time} - #{caller_name} - #{msg}"
146
161
  end
147
162
  if verbosity >= MU::Logger::LOUD
148
163
  if @html
149
164
  html_out "#{caller_name} - #{msg}"
150
- else
165
+ elsif color
151
166
  handle.puts "#{details}".white.on_black if details
167
+ else
168
+ handle.puts "#{details}" if details
152
169
  end
153
170
  end
154
171
  Syslog.log(Syslog::LOG_NOTICE, msg.gsub(/%/, ''))
@@ -156,14 +173,18 @@ module MU
156
173
  when WARN
157
174
  if @html
158
175
  html_out "#{time} - #{caller_name} - #{msg}", "orange"
159
- else
176
+ elsif color
160
177
  handle.puts "#{time} - #{caller_name} - #{msg}".light_red.on_black
178
+ else
179
+ handle.puts "#{time} - #{caller_name} - #{msg}"
161
180
  end
162
181
  if verbosity >= MU::Logger::LOUD
163
182
  if @html
164
183
  html_out "#{caller_name} - #{msg}"
165
- else
184
+ elsif color
166
185
  handle.puts "#{details}".white.on_black if details
186
+ else
187
+ handle.puts "#{details}" if details
167
188
  end
168
189
  end
169
190
  Syslog.log(Syslog::LOG_WARNING, msg.gsub(/%/, ''))
@@ -172,9 +193,12 @@ module MU
172
193
  if @html
173
194
  html_out "#{time} - #{caller_name} - #{msg}", "red"
174
195
  html_out " #{details}" if details
175
- else
196
+ elsif color
176
197
  handle.puts "#{time} - #{caller_name} - #{msg}".red.on_black
177
198
  handle.puts "#{details}".white.on_black if details
199
+ else
200
+ handle.puts "#{time} - #{caller_name} - #{msg}"
201
+ handle.puts "#{details}" if details
178
202
  end
179
203
  Syslog.log(Syslog::LOG_ERR, msg.gsub(/%/, ''))
180
204
  Syslog.log(Syslog::LOG_ERR, details.gsub(/%/, '')) if details
@@ -182,9 +206,12 @@ module MU
182
206
  if @html
183
207
  html_out "#{time} - #{caller_name} - #{msg}"
184
208
  html_out " #{details}" if details
185
- else
209
+ elsif color
186
210
  handle.puts "#{time} - #{caller_name} - #{msg}".white.on_black
187
211
  handle.puts "#{details}".white.on_black if details
212
+ else
213
+ handle.puts "#{time} - #{caller_name} - #{msg}"
214
+ handle.puts "#{details}" if details
188
215
  end
189
216
  Syslog.log(Syslog::LOG_NOTICE, msg.gsub(/%/, ''))
190
217
  Syslog.log(Syslog::LOG_NOTICE, details.gsub(/%/, '')) if details
@@ -1,4 +1,3 @@
1
- #!/usr/local/ruby-current/bin/ruby
2
1
  # Copyright:: Copyright (c) 2014 eGlobalTech, Inc., all rights reserved
3
2
  #
4
3
  # Licensed under the BSD-3 license (the "License");
@@ -23,6 +22,7 @@ module MU
23
22
  require 'fileutils'
24
23
  autoload :Chef, 'mu/master/chef'
25
24
  autoload :LDAP, 'mu/master/ldap'
25
+ autoload :SSL, 'mu/master/ssl'
26
26
 
27
27
  # @param users [Hash]: User metadata of the type returned by listUsers
28
28
  def self.printUsersToTerminal(users = MU::Master.listUsers)
@@ -169,7 +169,7 @@ module MU
169
169
  itemname = Password.pronounceable(32)
170
170
  # Make sure this itemname isn't already in use
171
171
  MU::Groomer::Chef.getSecret(vault: "scratchpad", item: itemname)
172
- rescue MU::Groomer::Chef::MuNoSuchSecret
172
+ rescue MU::Groomer::MuNoSuchSecret
173
173
  MU::Groomer::Chef.saveSecret(vault: "scratchpad", item: itemname, data: data)
174
174
  return itemname
175
175
  end while true
@@ -195,7 +195,7 @@ module MU
195
195
  end
196
196
  alias_device = cryptfile ? "/dev/mapper/"+path.gsub(/[^0-9a-z_\-]/i, "_") : realdevice
197
197
 
198
- if !File.exists?(realdevice)
198
+ if !File.exist?(realdevice)
199
199
  MU.log "Creating #{path} volume"
200
200
  if MU::Cloud::AWS.hosted?
201
201
  dummy_svr = MU::Cloud::AWS::Server.new(
@@ -250,7 +250,7 @@ module MU
250
250
  keyfile.close
251
251
 
252
252
  # we can assume that mu-master::init installed cryptsetup-luks
253
- if !File.exists?(alias_device)
253
+ if !File.exist?(alias_device)
254
254
  MU.log "Initializing crypto on #{alias_device}", MU::NOTICE
255
255
  %x{/sbin/cryptsetup luksFormat #{realdevice} #{keyfile.path} --batch-mode}
256
256
  %x{/sbin/cryptsetup luksOpen #{realdevice} #{alias_device.gsub(/.*?\/([^\/]+)$/, '\1')} --key-file #{keyfile.path}}
@@ -264,7 +264,7 @@ module MU
264
264
  %x{/sbin/mkfs.xfs "#{alias_device}"}
265
265
  %x{/usr/sbin/xfs_admin -L "#{path.gsub(/[^0-9a-z_\-]/i, "_")}" "#{alias_device}"}
266
266
  end
267
- Dir.mkdir(path, 0700) if !Dir.exists?(path) # XXX recursive
267
+ Dir.mkdir(path, 0700) if !Dir.exist?(path) # XXX recursive
268
268
  %x{/usr/sbin/xfs_info "#{alias_device}" > /dev/null 2>&1}
269
269
  if $?.exitstatus != 0
270
270
  MU.log "Mounting #{alias_device} to #{path}"
@@ -292,7 +292,7 @@ module MU
292
292
 
293
293
  # Remove Scratchpad entries which have exceeded their maximum age.
294
294
  def self.cleanExpiredScratchpads
295
- return if !$MU_CFG['scratchpad'].has_key?('max_age') or $MU_CFG['scratchpad']['max_age'] < 1
295
+ return if !$MU_CFG['scratchpad'] or !$MU_CFG['scratchpad'].has_key?('max_age') or $MU_CFG['scratchpad']['max_age'] < 1
296
296
  @scratchpad_semaphore.synchronize {
297
297
  entries = MU::Groomer::Chef.getSecret(vault: "scratchpad")
298
298
  entries.each { |pad|
@@ -347,8 +347,8 @@ module MU
347
347
  ldap_users['mu'] = {}
348
348
  ldap_users['mu']['admin'] = true
349
349
  ldap_users['mu']['non_ldap'] = true
350
- ldap_users.each_pair { |username, data|
351
- key = username.to_s
350
+ ldap_users.each_pair { |uname, data|
351
+ key = uname.to_s
352
352
  all_user_data[key] = {}
353
353
  userdir = $MU_CFG['installdir']+"/var/users/#{key}"
354
354
  if !Dir.exist?(userdir)
@@ -369,6 +369,88 @@ module MU
369
369
  all_user_data
370
370
  end
371
371
 
372
+
373
+ @@kubectl_path = nil
374
+ # Locate a working +kubectl+ executable and return its fully-qualified
375
+ # path.
376
+ def self.kubectl
377
+ return @@kubectl_path if @@kubectl_path
378
+
379
+ paths = ["/opt/mu/bin"]+ENV['PATH'].split(/:/)
380
+ best = nil
381
+ best_version = nil
382
+ paths.uniq.each { |path|
383
+ if File.exist?(path+"/kubectl")
384
+ version = %x{#{path}/kubectl version --short --client}.chomp.sub(/.*Client version:\s+v/i, '')
385
+ next if !$?.success?
386
+ if !best_version or MU.version_sort(best_version, version) > 0
387
+ best_version = version
388
+ best = path+"/kubectl"
389
+ end
390
+ end
391
+ }
392
+ if !best
393
+ MU.log "Failed to find a working kubectl executable in any path", MU::WARN, details: paths.uniq.sort
394
+ return nil
395
+ else
396
+ MU.log "Kubernetes commands will use #{best} (#{best_version})"
397
+ end
398
+
399
+ @@kubectl_path = best
400
+ @@kubectl_path
401
+ end
402
+
403
+ # Given an array of hashes representing Kubernetes resources,
404
+ def self.applyKubernetesResources(name, blobs = [], kubeconfig: nil, outputdir: nil)
405
+ use_tmp = false
406
+ if !outputdir
407
+ require 'tempfile'
408
+ use_tmp = true
409
+ end
410
+
411
+ count = 0
412
+ blobs.each { |blob|
413
+ f = nil
414
+ blobfile = if use_tmp
415
+ f = Tempfile.new("k8s-resource-#{count.to_s}-#{name}")
416
+ f.puts blob.to_yaml
417
+ f.close
418
+ f.path
419
+ else
420
+ path = outputdir+"/k8s-resource-#{count.to_s}-#{name}"
421
+ File.open(path, "w") { |fh|
422
+ fh.puts blob.to_yaml
423
+ }
424
+ path
425
+ end
426
+ next if !kubectl
427
+ done = false
428
+ retries = 0
429
+ begin
430
+ %x{#{kubectl} --kubeconfig "#{kubeconfig}" get -f #{blobfile} > /dev/null 2>&1}
431
+ arg = $?.exitstatus == 0 ? "apply" : "create"
432
+ cmd = %Q{#{kubectl} --kubeconfig "#{kubeconfig}" #{arg} -f #{blobfile}}
433
+ MU.log "Applying Kubernetes resource #{count.to_s} with kubectl #{arg}", MU::NOTICE, details: cmd
434
+ output = %x{#{cmd} 2>&1}
435
+ if $?.exitstatus == 0
436
+ MU.log "Kubernetes resource #{count.to_s} #{arg} was successful: #{output}", details: blob.to_yaml
437
+ done = true
438
+ else
439
+ MU.log "Kubernetes resource #{count.to_s} #{arg} failed: #{output}", MU::WARN, details: blob.to_yaml
440
+ if retries < 5
441
+ sleep 5
442
+ else
443
+ MU.log "Giving up on Kubernetes resource #{count.to_s} #{arg}"
444
+ done = true
445
+ end
446
+ retries += 1
447
+ end
448
+ f.unlink if use_tmp
449
+ end while !done
450
+ count += 1
451
+ }
452
+ end
453
+
372
454
  # Update Mu's local cache/metadata for the given user, fixing permissions
373
455
  # and updating stored values. Create a single-user group for the user, as
374
456
  # well.
@@ -1,4 +1,3 @@
1
- #!/usr/local/ruby-current/bin/ruby
2
1
  # Copyright:: Copyright (c) 2014 eGlobalTech, Inc., all rights reserved
3
2
  #
4
3
  # Licensed under the BSD-3 license (the "License");
@@ -112,7 +111,7 @@ module MU
112
111
  f.puts "chef_server_url 'https://#{$MU_CFG["public_address"]}/organizations/#{chef_user}'"
113
112
  f.puts "validation_client_name '#{chef_user}-validator'"
114
113
  }
115
- if !File.exists?("#{chefdir}/client.rb") or
114
+ if !File.exist?("#{chefdir}/client.rb") or
116
115
  File.read("#{chefdir}/client.rb") != File.read("#{chefdir}/client.rb.tmp.#{Process.pid}")
117
116
  File.rename(chefdir+"/client.rb.tmp.#{Process.pid}", chefdir+"/client.rb")
118
117
  FileUtils.chown_R(user, user+".mu-user", Etc.getpwnam(user).dir+"/.chef")
@@ -143,7 +142,7 @@ module MU
143
142
  # f.puts "verify_api_cert false"
144
143
  # f.puts "ssl_verify_mode :verify_none"
145
144
  }
146
- if !File.exists?("#{chefdir}/knife.rb") or
145
+ if !File.exist?("#{chefdir}/knife.rb") or
147
146
  File.read("#{chefdir}/knife.rb") != File.read("#{chefdir}/knife.rb.tmp.#{Process.pid}")
148
147
  File.rename(chefdir+"/knife.rb.tmp.#{Process.pid}", chefdir+"/knife.rb")
149
148
  FileUtils.chown_R(user, user+".mu-user", Etc.getpwnam(user).dir+"/.chef")
@@ -1,4 +1,3 @@
1
- #!/usr/local/ruby-current/bin/ruby
2
1
  # Copyright:: Copyright (c) 2014 eGlobalTech, Inc., all rights reserved
3
2
  #
4
3
  # Licensed under the BSD-3 license (the "License");
@@ -0,0 +1,250 @@
1
+ # Copyright:: Copyright (c) 2019 eGlobalTech, Inc., all rights reserved
2
+ #
3
+ # Licensed under the BSD-3 license (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License in the root of the project or at
6
+ #
7
+ # http://egt-labs.com/mu/LICENSE.html
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module MU
16
+ class Master
17
+ # Create and manage our own internal SSL signing authority
18
+ class SSL
19
+
20
+ # List of Mu services for which we'll generate SSL certs signed by our
21
+ # authority.
22
+ SERVICES = ["rsyslog", "mommacat", "ldap", "consul", "vault"]
23
+
24
+ # Exception class for when we can't find the +openssl+ command
25
+ class MuSSLNotFound < MU::MuError;end
26
+
27
+ # TODO set file/dir ownerships to honor for_user if we were invoked as root
28
+
29
+ # @param for_user [String]
30
+ def self.bootstrap(for_user: MU.mu_user)
31
+ ssldir = MU.dataDir(for_user)+"/ssl"
32
+ Dir.mkdir(ssldir, 0755) if !Dir.exist?(ssldir)
33
+
34
+ alt_names = [MU.mu_public_ip, MU.my_private_ip, MU.mu_public_addr, Socket.gethostbyname(Socket.gethostname).first, "localhost", "127.0.0.1"].uniq
35
+ alt_names.reject! { |s| s.nil? }
36
+
37
+ getCert("Mu_CA", "/CN=#{MU.mu_public_addr}/OU=Mu Server at #{MU.mu_public_addr}/O=eGlobalTech/C=US", sans: alt_names, ca: true)
38
+
39
+ SERVICES.each { |service|
40
+ getCert(service, "/CN=#{MU.mu_public_addr}/OU=Mu #{service}/O=eGlobalTech/C=US", sans: alt_names)
41
+ }
42
+
43
+ end
44
+
45
+ # @param name [String]
46
+ # @param for_user [String]
47
+ # @return [OpenSSL::PKey::RSA]
48
+ def self.getKey(name, for_user: MU.mu_user, keysize: 4096)
49
+ ssldir = MU.dataDir(for_user)+"/ssl"
50
+ if !File.exist?(ssldir+"/"+name+".key")
51
+ key = OpenSSL::PKey::RSA.new keysize
52
+ File.write(ssldir+"/"+name+".key", key)
53
+ end
54
+ File.chmod(0400, ssldir+"/"+name+".key")
55
+ OpenSSL::PKey::RSA.new(File.read(ssldir+"/"+name+".key"))
56
+ end
57
+
58
+ # @param for_user [String]
59
+ # @return [Integer]
60
+ def self.incrementCASerial(for_user: MU.mu_user)
61
+ ssldir = MU.dataDir(for_user)+"/ssl"
62
+ cur = 0
63
+ if File.exist?(ssldir+"/serial")
64
+ cur = File.read(ssldir+"/serial").chomp.to_i
65
+ end
66
+ File.open("#{ssldir}/serial", File::CREAT|File::RDWR, 0600) { |f|
67
+ f.flock(File::LOCK_EX)
68
+ cur += 1
69
+ f.rewind
70
+ f.truncate(0)
71
+ f.puts cur
72
+ f.flush
73
+ f.flock(File::LOCK_UN)
74
+ }
75
+ cur
76
+ end
77
+
78
+
79
+ # Given a Certificate Signing Request, sign it with our internal CA and
80
+ # write the resulting signed certificate. Only works on local files.
81
+ # @param csr_path [String]: The CSR to sign, as a file.
82
+ def self.sign(csr_path, sans = [], for_user: MU.mu_user)
83
+ certdir = File.dirname(csr_path)
84
+ certname = File.basename(csr_path, ".csr")
85
+ if File.exist?("#{certdir}/#{certname}.crt")
86
+ MU.log "Not re-signing SSL certificate request #{csr_path}, #{certdir}/#{certname}.crt already exists", MU::DEBUG
87
+ return
88
+ end
89
+ MU.log "Signing SSL certificate request #{csr_path} with #{MU.mySSLDir}/Mu_CA.pem"
90
+
91
+ begin
92
+ csr = OpenSSL::X509::Request.new File.read csr_path
93
+ rescue Exception => e
94
+ MU.log e.message, MU::ERR, details: File.read(csr_path)
95
+ raise e
96
+ end
97
+
98
+ cakey = getKey("Mu_CA")
99
+ cacert = getCert("Mu_CA", ca: true).first
100
+
101
+ cert = OpenSSL::X509::Certificate.new
102
+ cert.serial = incrementCASerial(for_user: for_user)
103
+ cert.version = 0x2
104
+ cert.not_before = Time.now
105
+ cert.not_after = Time.now + 180000000
106
+ cert.subject = csr.subject
107
+ cert.public_key = csr.public_key
108
+ cert.issuer = cacert.subject
109
+ ef = OpenSSL::X509::ExtensionFactory.new
110
+ ef.issuer_certificate = cacert
111
+ ef.subject_certificate = cert
112
+ ef.subject_request = csr
113
+ if !sans.nil? and !sans.empty? and
114
+ !formatSANS(sans).nil? and !formatSANS(sans).empty?
115
+ cert.add_extension(ef.create_extension("subjectAltName",formatSANS(sans),false))
116
+ end
117
+ cert.add_extension(ef.create_extension("keyUsage","nonRepudiation,digitalSignature,keyEncipherment", false))
118
+ cert.add_extension(ef.create_extension("extendedKeyUsage","clientAuth,serverAuth,codeSigning,emailProtection",false))
119
+ cert.sign cakey, OpenSSL::Digest::SHA256.new
120
+
121
+ File.open("#{certdir}/#{certname}.crt", 'w', 0644) { |f|
122
+ f.write cert.to_pem
123
+ }
124
+
125
+ cert
126
+ end
127
+
128
+ # @param name [String]
129
+ # @param cn_str [String]
130
+ # @param sans [Array<String>]
131
+ # @param ca [Array<String>]
132
+ # @param for_user [String]
133
+ # @return [OpenSSL::X509::Certificate]
134
+ def self.getReq(name, cn_str = nil, sans: [], ca: false, for_user: MU.mu_user)
135
+ end
136
+
137
+ # @param name [String]
138
+ # @param cn_str [String]
139
+ # @param sans [Array<String>]
140
+ # @param ca [Array<String>]
141
+ # @param for_user [String]
142
+ # @param pfx [Boolean]
143
+ # @return [OpenSSL::X509::Certificate]
144
+ def self.getCert(name, cn_str = nil, sans: [], ca: false, for_user: MU.mu_user, pfx: false)
145
+ ssldir = MU.dataDir(for_user)+"/ssl"
146
+ filename = ca ? "#{ssldir}/#{name}.pem" : "#{ssldir}/#{name}.crt"
147
+ keyfile = "#{ssldir}/#{name}.key"
148
+ pfxfile = "#{ssldir}/#{name}.pfx"
149
+ pfx_cert = nil
150
+
151
+ if File.exist?(filename)
152
+ pfx_cert = toPfx(filename, keyfile, pfxfile) if pfx
153
+ cert = OpenSSL::X509::Certificate.new(File.read(filename))
154
+ return [cert, pfx_cert]
155
+ end
156
+
157
+ if cn_str.nil?
158
+ raise MuError, "Can't generate an SSL cert for #{name} without a CN"
159
+ end
160
+
161
+ key = getKey(name, for_user: for_user)
162
+
163
+ puts cn_str
164
+ cn = OpenSSL::X509::Name.parse(cn_str)
165
+
166
+ # If we're generating our local CA, we're not really doing a CSR, but
167
+ # the operation is close to identical.
168
+ csr = if ca
169
+ MU.log "Generating Mu CA certificate", MU::NOTICE, details: filename
170
+ csr = OpenSSL::X509::Certificate.new
171
+ csr.not_before = Time.now
172
+ csr.not_after = Time.now + 180000000
173
+ csr
174
+ else
175
+ MU.log "Generating Mu-signed certificate for #{name}", MU::NOTICE, details: filename
176
+ OpenSSL::X509::Request.new
177
+ end
178
+
179
+ csr.version = 0x2 # by which we mean '3'
180
+ csr.subject = cn
181
+ csr.public_key = key.public_key
182
+
183
+
184
+ # If we're the CA certificate, declare ourselves our own issuer and
185
+ # write, instead of going through the rest of the motions.
186
+ if ca
187
+ csr.issuer = csr.subject
188
+ ef = OpenSSL::X509::ExtensionFactory.new
189
+ csr.serial = 1
190
+ ef.subject_certificate = csr
191
+ ef.issuer_certificate = csr
192
+ csr.add_extension(ef.create_extension("subjectAltName",formatSANS(sans),false))
193
+ csr.add_extension(ef.create_extension("basicConstraints", "CA:TRUE", true))
194
+ csr.add_extension(ef.create_extension("keyUsage","keyCertSign, cRLSign", true))
195
+ csr.add_extension(ef.create_extension("subjectKeyIdentifier", "hash", false))
196
+ csr.add_extension(ef.create_extension("authorityKeyIdentifier", "keyid:always", false))
197
+ end
198
+
199
+ csr.sign key, OpenSSL::Digest::SHA256.new
200
+
201
+ cert = if !ca
202
+ File.open("#{ssldir}/#{name}.csr", 'w', 0644) { |f|
203
+ f.write csr.to_pem
204
+ }
205
+ sign("#{ssldir}/#{name}.csr", sans, for_user: for_user)
206
+ else
207
+ csr
208
+ end
209
+
210
+ File.open(filename, 'w', 0644) { |f|
211
+ f.write cert.to_pem
212
+ }
213
+ pfx_cert = toPfx(filename, keyfile, pfxfile) if pfx
214
+
215
+ if MU.mu_user != "mu" and Process.uid == 0
216
+ owner_uid = Etc.getpwnam(for_user).uid
217
+ File.chown(owner_uid, nil, filename)
218
+ File.chown(owner_uid, nil, pfxfile) if pfx
219
+ end
220
+
221
+
222
+ [cert, pfx_cert]
223
+ end
224
+
225
+ private
226
+
227
+ def self.toPfx(certfile, keyfile, pfxfile)
228
+ cacert = getCert("Mu_CA", ca: true).first
229
+ cert = OpenSSL::X509::Certificate.new(File.read(certfile))
230
+ key = OpenSSL::PKey::RSA.new(File.read(keyfile))
231
+ pfx = OpenSSL::PKCS12.create(nil, nil, key, cert, [cacert], nil, nil, nil, nil)
232
+ File.open(pfxfile, 'w', 0644) { |f|
233
+ f.write pfx.to_der
234
+ }
235
+ pfx
236
+ end
237
+
238
+ def self.formatSANS(sans)
239
+ sans.map { |s|
240
+ if s.match(/^\d+\.\d+\.\d+\.\d+$/)
241
+ "IP:"+s
242
+ else
243
+ "DNS:"+s
244
+ end
245
+ }.join(",")
246
+ end
247
+
248
+ end
249
+ end
250
+ end