cloud-mu 2.1.0beta → 3.0.0beta

Sign up to get free protection for your applications and to get access to all the features.
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