cloud-mu 1.9.0.pre.beta

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 (618) hide show
  1. checksums.yaml +7 -0
  2. data/Berksfile +56 -0
  3. data/Berksfile.lock +250 -0
  4. data/Jenkinsfile +184 -0
  5. data/LICENSE.md +37 -0
  6. data/README.md +26 -0
  7. data/bin/mu-aws-setup +376 -0
  8. data/bin/mu-cleanup +68 -0
  9. data/bin/mu-configure +1133 -0
  10. data/bin/mu-deploy +166 -0
  11. data/bin/mu-firewall-allow-clients +30 -0
  12. data/bin/mu-gcp-setup +200 -0
  13. data/bin/mu-gen-docs +34 -0
  14. data/bin/mu-gen-env +42 -0
  15. data/bin/mu-load-config.rb +158 -0
  16. data/bin/mu-node-manage +683 -0
  17. data/bin/mu-self-update +228 -0
  18. data/bin/mu-ssh +23 -0
  19. data/bin/mu-tunnel-nagios +144 -0
  20. data/bin/mu-upload-chef-artifacts +757 -0
  21. data/bin/mu-user-manage +275 -0
  22. data/cookbooks/awscli/LICENSE +37 -0
  23. data/cookbooks/awscli/README.md +58 -0
  24. data/cookbooks/awscli/attributes/default.rb +1 -0
  25. data/cookbooks/awscli/libraries/instance_metadata.rb +21 -0
  26. data/cookbooks/awscli/metadata.rb +20 -0
  27. data/cookbooks/awscli/recipes/default.rb +56 -0
  28. data/cookbooks/awscli/templates/default/config.erb +18 -0
  29. data/cookbooks/mu-activedirectory/CHANGELOG.md +13 -0
  30. data/cookbooks/mu-activedirectory/LICENSE +37 -0
  31. data/cookbooks/mu-activedirectory/README.md +6 -0
  32. data/cookbooks/mu-activedirectory/attributes/default.rb +98 -0
  33. data/cookbooks/mu-activedirectory/files/default/password-auth +32 -0
  34. data/cookbooks/mu-activedirectory/files/default/sshd_pol.pp +0 -0
  35. data/cookbooks/mu-activedirectory/files/default/sshd_pol.te +32 -0
  36. data/cookbooks/mu-activedirectory/files/default/syslogd_oddjobd.pp +0 -0
  37. data/cookbooks/mu-activedirectory/files/default/syslogd_oddjobd.te +10 -0
  38. data/cookbooks/mu-activedirectory/files/default/system-auth +34 -0
  39. data/cookbooks/mu-activedirectory/files/default/winbindpol.pp +0 -0
  40. data/cookbooks/mu-activedirectory/files/default/winbindpol.te +37 -0
  41. data/cookbooks/mu-activedirectory/libraries/config.rb +106 -0
  42. data/cookbooks/mu-activedirectory/libraries/helper.rb +86 -0
  43. data/cookbooks/mu-activedirectory/metadata.rb +17 -0
  44. data/cookbooks/mu-activedirectory/providers/domain.rb +152 -0
  45. data/cookbooks/mu-activedirectory/providers/domain_controller.rb +89 -0
  46. data/cookbooks/mu-activedirectory/providers/domain_node.rb +275 -0
  47. data/cookbooks/mu-activedirectory/recipes/default.rb +8 -0
  48. data/cookbooks/mu-activedirectory/recipes/domain-controller.rb +44 -0
  49. data/cookbooks/mu-activedirectory/recipes/domain-node.rb +50 -0
  50. data/cookbooks/mu-activedirectory/recipes/domain.rb +43 -0
  51. data/cookbooks/mu-activedirectory/recipes/sssd.rb +185 -0
  52. data/cookbooks/mu-activedirectory/resources/domain.rb +25 -0
  53. data/cookbooks/mu-activedirectory/resources/domain_controller.rb +25 -0
  54. data/cookbooks/mu-activedirectory/resources/domain_node.rb +20 -0
  55. data/cookbooks/mu-activedirectory/templates/default/dhclient-eth0.conf.erb +4 -0
  56. data/cookbooks/mu-activedirectory/templates/default/interface +0 -0
  57. data/cookbooks/mu-activedirectory/templates/default/krb5.conf.erb +23 -0
  58. data/cookbooks/mu-activedirectory/templates/default/ntp.conf.erb +56 -0
  59. data/cookbooks/mu-activedirectory/templates/default/smb.conf.erb +33 -0
  60. data/cookbooks/mu-activedirectory/templates/default/sssd.conf.erb +60 -0
  61. data/cookbooks/mu-activedirectory/templates/windows/Backup.xml.erb +20 -0
  62. data/cookbooks/mu-activedirectory/templates/windows/bkupInfo.xml.erb +1 -0
  63. data/cookbooks/mu-activedirectory/templates/windows/gpreprt.xml.erb +198 -0
  64. data/cookbooks/mu-activedirectory/templates/windows/gptmpl.inf.erb +12 -0
  65. data/cookbooks/mu-activedirectory/templates/windows/manifest.xml.erb +1 -0
  66. data/cookbooks/mu-firewall/CHANGELOG.md +11 -0
  67. data/cookbooks/mu-firewall/LICENSE +37 -0
  68. data/cookbooks/mu-firewall/README.md +5 -0
  69. data/cookbooks/mu-firewall/attributes/default.rb +3 -0
  70. data/cookbooks/mu-firewall/metadata.rb +16 -0
  71. data/cookbooks/mu-firewall/recipes/default.rb +10 -0
  72. data/cookbooks/mu-glusterfs/CHANGELOG.md +13 -0
  73. data/cookbooks/mu-glusterfs/LICENSE +37 -0
  74. data/cookbooks/mu-glusterfs/README.md +5 -0
  75. data/cookbooks/mu-glusterfs/attributes/default.rb +34 -0
  76. data/cookbooks/mu-glusterfs/metadata.rb +17 -0
  77. data/cookbooks/mu-glusterfs/recipes/client.rb +62 -0
  78. data/cookbooks/mu-glusterfs/recipes/default.rb +16 -0
  79. data/cookbooks/mu-glusterfs/recipes/samba.rb +57 -0
  80. data/cookbooks/mu-glusterfs/recipes/server.rb +200 -0
  81. data/cookbooks/mu-glusterfs/templates/default/mu-gluster-client.erb +71 -0
  82. data/cookbooks/mu-glusterfs/templates/default/smb.conf.erb +14 -0
  83. data/cookbooks/mu-jenkins/CHANGELOG.md +13 -0
  84. data/cookbooks/mu-jenkins/LICENSE +37 -0
  85. data/cookbooks/mu-jenkins/README.md +105 -0
  86. data/cookbooks/mu-jenkins/attributes/default.rb +42 -0
  87. data/cookbooks/mu-jenkins/files/default/cleanup_deploy_config.xml +73 -0
  88. data/cookbooks/mu-jenkins/files/default/deploy_config.xml +44 -0
  89. data/cookbooks/mu-jenkins/metadata.rb +21 -0
  90. data/cookbooks/mu-jenkins/recipes/default.rb +195 -0
  91. data/cookbooks/mu-jenkins/recipes/node-ssh-config.rb +54 -0
  92. data/cookbooks/mu-jenkins/recipes/public_key.rb +24 -0
  93. data/cookbooks/mu-jenkins/templates/default/example_job.config.xml.erb +24 -0
  94. data/cookbooks/mu-jenkins/templates/default/org.jvnet.hudson.plugins.SSHBuildWrapper.xml.erb +14 -0
  95. data/cookbooks/mu-jenkins/templates/default/ssh_config.erb +6 -0
  96. data/cookbooks/mu-master/CHANGELOG.md +13 -0
  97. data/cookbooks/mu-master/LICENSE +37 -0
  98. data/cookbooks/mu-master/README.md +6 -0
  99. data/cookbooks/mu-master/attributes/default.rb +95 -0
  100. data/cookbooks/mu-master/files/default/0-mu-log-server.conf +19 -0
  101. data/cookbooks/mu-master/files/default/addRSA.ldif +8 -0
  102. data/cookbooks/mu-master/files/default/check_mem.pl +197 -0
  103. data/cookbooks/mu-master/files/default/cloudamatic.png +0 -0
  104. data/cookbooks/mu-master/files/default/dirsrv_admin.pp +0 -0
  105. data/cookbooks/mu-master/files/default/dirsrv_admin.te +13 -0
  106. data/cookbooks/mu-master/files/default/nagios_selinux.pp +0 -0
  107. data/cookbooks/mu-master/files/default/nagios_selinux.te +51 -0
  108. data/cookbooks/mu-master/files/default/nagios_selinux_7.pp +0 -0
  109. data/cookbooks/mu-master/files/default/nagios_selinux_7.te +17 -0
  110. data/cookbooks/mu-master/files/default/pam_sshd +18 -0
  111. data/cookbooks/mu-master/files/default/ssl_enable.ldif +18 -0
  112. data/cookbooks/mu-master/files/default/syslogd_oddjobd.pp +0 -0
  113. data/cookbooks/mu-master/files/default/syslogd_oddjobd.te +10 -0
  114. data/cookbooks/mu-master/files/default/vimrc +19 -0
  115. data/cookbooks/mu-master/libraries/mu.rb +29 -0
  116. data/cookbooks/mu-master/metadata.rb +30 -0
  117. data/cookbooks/mu-master/providers/user.rb +41 -0
  118. data/cookbooks/mu-master/recipes/389ds.rb +164 -0
  119. data/cookbooks/mu-master/recipes/basepackages.rb +58 -0
  120. data/cookbooks/mu-master/recipes/caching_nameserver.rb +37 -0
  121. data/cookbooks/mu-master/recipes/default.rb +451 -0
  122. data/cookbooks/mu-master/recipes/eks-kubectl.rb +41 -0
  123. data/cookbooks/mu-master/recipes/firewall-holes.rb +70 -0
  124. data/cookbooks/mu-master/recipes/init.rb +542 -0
  125. data/cookbooks/mu-master/recipes/ssl-certs.rb +109 -0
  126. data/cookbooks/mu-master/recipes/sssd.rb +89 -0
  127. data/cookbooks/mu-master/recipes/update_nagios_only.rb +242 -0
  128. data/cookbooks/mu-master/recipes/vault.rb +111 -0
  129. data/cookbooks/mu-master/resources/user.rb +19 -0
  130. data/cookbooks/mu-master/templates/default/389-directory-setup.inf.erb +28 -0
  131. data/cookbooks/mu-master/templates/default/chef-server.rb.erb +18 -0
  132. data/cookbooks/mu-master/templates/default/dhclient-eth0.conf.erb +9 -0
  133. data/cookbooks/mu-master/templates/default/mu-momma-cat.erb +149 -0
  134. data/cookbooks/mu-master/templates/default/mu.rc.erb +9 -0
  135. data/cookbooks/mu-master/templates/default/openssl.cnf.erb +354 -0
  136. data/cookbooks/mu-master/templates/default/sssd.conf.erb +44 -0
  137. data/cookbooks/mu-master/templates/default/web_app.conf.erb +90 -0
  138. data/cookbooks/mu-mongo/CHANGELOG.md +13 -0
  139. data/cookbooks/mu-mongo/LICENSE +37 -0
  140. data/cookbooks/mu-mongo/README.md +5 -0
  141. data/cookbooks/mu-mongo/attributes/default.rb +22 -0
  142. data/cookbooks/mu-mongo/files/default/keyfile +16 -0
  143. data/cookbooks/mu-mongo/files/default/remove_nodes.js +5 -0
  144. data/cookbooks/mu-mongo/metadata.rb +17 -0
  145. data/cookbooks/mu-mongo/recipes/default.rb +149 -0
  146. data/cookbooks/mu-mongo/recipes/yum-update-rule.rb +18 -0
  147. data/cookbooks/mu-mongo/templates/default/mongo_create_openfema_db.js.erb +2 -0
  148. data/cookbooks/mu-mongo/templates/default/mongo_init.js.erb +1 -0
  149. data/cookbooks/mu-mongo/templates/default/mongo_logrotate.erb +14 -0
  150. data/cookbooks/mu-mongo/templates/default/mongo_replset_addnodes.js.erb +6 -0
  151. data/cookbooks/mu-mongo/templates/default/replset_init.js.erb +2 -0
  152. data/cookbooks/mu-openvpn/CHANGELOG.md +13 -0
  153. data/cookbooks/mu-openvpn/LICENSE +37 -0
  154. data/cookbooks/mu-openvpn/README.md +6 -0
  155. data/cookbooks/mu-openvpn/attributes/default.rb +119 -0
  156. data/cookbooks/mu-openvpn/metadata.rb +18 -0
  157. data/cookbooks/mu-openvpn/recipes/default.rb +108 -0
  158. data/cookbooks/mu-openvpn/templates/default/users.json.erb +42 -0
  159. data/cookbooks/mu-php54/CHANGELOG.md +12 -0
  160. data/cookbooks/mu-php54/LICENSE +37 -0
  161. data/cookbooks/mu-php54/README.md +0 -0
  162. data/cookbooks/mu-php54/files/centos/php.ini +1802 -0
  163. data/cookbooks/mu-php54/files/ubuntu/php.ini +1870 -0
  164. data/cookbooks/mu-php54/metadata.rb +21 -0
  165. data/cookbooks/mu-php54/recipes/default.rb +97 -0
  166. data/cookbooks/mu-splunk/CHANGELOG.md +37 -0
  167. data/cookbooks/mu-splunk/LICENSE +37 -0
  168. data/cookbooks/mu-splunk/README.md +451 -0
  169. data/cookbooks/mu-splunk/attributes/default.rb +95 -0
  170. data/cookbooks/mu-splunk/attributes/upgrade.rb +49 -0
  171. data/cookbooks/mu-splunk/definitions/splunk_installer.rb +103 -0
  172. data/cookbooks/mu-splunk/files/default/splunk-nocheck +10 -0
  173. data/cookbooks/mu-splunk/libraries/helpers.rb +72 -0
  174. data/cookbooks/mu-splunk/libraries/splunk_app_provider.rb +156 -0
  175. data/cookbooks/mu-splunk/libraries/splunk_app_resource.rb +43 -0
  176. data/cookbooks/mu-splunk/metadata.json +30 -0
  177. data/cookbooks/mu-splunk/metadata.rb +17 -0
  178. data/cookbooks/mu-splunk/recipes/client.rb +143 -0
  179. data/cookbooks/mu-splunk/recipes/default.rb +31 -0
  180. data/cookbooks/mu-splunk/recipes/disabled.rb +41 -0
  181. data/cookbooks/mu-splunk/recipes/install_forwarder.rb +23 -0
  182. data/cookbooks/mu-splunk/recipes/install_server.rb +23 -0
  183. data/cookbooks/mu-splunk/recipes/server.rb +53 -0
  184. data/cookbooks/mu-splunk/recipes/service.rb +95 -0
  185. data/cookbooks/mu-splunk/recipes/setup_auth.rb +49 -0
  186. data/cookbooks/mu-splunk/recipes/setup_ssl.rb +63 -0
  187. data/cookbooks/mu-splunk/recipes/upgrade.rb +94 -0
  188. data/cookbooks/mu-splunk/recipes/user.rb +34 -0
  189. data/cookbooks/mu-splunk/templates/default/base_logs_unix_inputs.conf.erb +26 -0
  190. data/cookbooks/mu-splunk/templates/default/inputs.conf.erb +13 -0
  191. data/cookbooks/mu-splunk/templates/default/outputs.conf.erb +9 -0
  192. data/cookbooks/mu-splunk/templates/default/splunk-init.erb +74 -0
  193. data/cookbooks/mu-splunk/templates/default/system-web.conf.erb +7 -0
  194. data/cookbooks/mu-tools/CHANGELOG.md +12 -0
  195. data/cookbooks/mu-tools/LICENSE +37 -0
  196. data/cookbooks/mu-tools/README.md +188 -0
  197. data/cookbooks/mu-tools/attributes/default.rb +142 -0
  198. data/cookbooks/mu-tools/attributes/ebs_rolling_snapshots.rb +3 -0
  199. data/cookbooks/mu-tools/files/amazon/etc/freshclam.conf +235 -0
  200. data/cookbooks/mu-tools/files/centos/CentOS-Base.repo +52 -0
  201. data/cookbooks/mu-tools/files/centos/etc/bashrc +93 -0
  202. data/cookbooks/mu-tools/files/centos/etc/freshclam.conf +235 -0
  203. data/cookbooks/mu-tools/files/centos/etc/login.defs +72 -0
  204. data/cookbooks/mu-tools/files/centos/etc/profile +77 -0
  205. data/cookbooks/mu-tools/files/centos/etc/security/limits.conf +57 -0
  206. data/cookbooks/mu-tools/files/centos/etc/sysconfig/init +19 -0
  207. data/cookbooks/mu-tools/files/centos/etc/sysctl.conf +82 -0
  208. data/cookbooks/mu-tools/files/centos-6/README_MU +0 -0
  209. data/cookbooks/mu-tools/files/centos-6/etc/audit/stig.rules +173 -0
  210. data/cookbooks/mu-tools/files/centos-6/etc/bashrc +90 -0
  211. data/cookbooks/mu-tools/files/centos-6/etc/login.defs +70 -0
  212. data/cookbooks/mu-tools/files/centos-6/etc/pam.d/su +12 -0
  213. data/cookbooks/mu-tools/files/centos-6/etc/profile +83 -0
  214. data/cookbooks/mu-tools/files/centos-6/etc/securetty +12 -0
  215. data/cookbooks/mu-tools/files/centos-6/etc/sysconfig/init +30 -0
  216. data/cookbooks/mu-tools/files/centos-6/etc/sysctl.conf +40 -0
  217. data/cookbooks/mu-tools/files/default/Mu_CA.pem +34 -0
  218. data/cookbooks/mu-tools/files/default/PSWindowsUpdate.zip +0 -0
  219. data/cookbooks/mu-tools/files/default/ebs_snapshots.py +123 -0
  220. data/cookbooks/mu-tools/files/default/etc/BANNER +0 -0
  221. data/cookbooks/mu-tools/files/default/etc/BANNER-FEDERAL +19 -0
  222. data/cookbooks/mu-tools/files/default/gpo_no_uac.zip +0 -0
  223. data/cookbooks/mu-tools/files/default/mypol.pp +0 -0
  224. data/cookbooks/mu-tools/files/default/mypol.te +37 -0
  225. data/cookbooks/mu-tools/files/default/nrpe_c7.pp +0 -0
  226. data/cookbooks/mu-tools/files/default/nrpe_c7.te +31 -0
  227. data/cookbooks/mu-tools/files/default/nrpe_check_disk.pp +0 -0
  228. data/cookbooks/mu-tools/files/default/nrpe_check_disk.te +11 -0
  229. data/cookbooks/mu-tools/files/default/nrpe_disk.pp +0 -0
  230. data/cookbooks/mu-tools/files/default/nrpe_disk.te +10 -0
  231. data/cookbooks/mu-tools/files/default/nrpe_file.pp +0 -0
  232. data/cookbooks/mu-tools/files/default/nrpe_file.te +31 -0
  233. data/cookbooks/mu-tools/files/default/ntrights +0 -0
  234. data/cookbooks/mu-tools/files/default/serverclass.conf +18 -0
  235. data/cookbooks/mu-tools/files/default/splunk-apps/base_logs_unix/local/app.conf +1 -0
  236. data/cookbooks/mu-tools/files/default/splunk-apps/base_logs_unix/local/inputs.conf +13 -0
  237. data/cookbooks/mu-tools/files/default/splunk-apps/base_logs_windows/local/app.conf +1 -0
  238. data/cookbooks/mu-tools/files/default/splunk-apps/base_logs_windows/local/inputs.conf +8 -0
  239. data/cookbooks/mu-tools/files/default/sshd_pol.pp +0 -0
  240. data/cookbooks/mu-tools/files/default/sshd_pol.te +32 -0
  241. data/cookbooks/mu-tools/files/redhat/etc/bashrc +93 -0
  242. data/cookbooks/mu-tools/files/redhat/etc/freshclam.conf +235 -0
  243. data/cookbooks/mu-tools/files/redhat/etc/login.defs +72 -0
  244. data/cookbooks/mu-tools/files/redhat/etc/profile +77 -0
  245. data/cookbooks/mu-tools/files/redhat/etc/security/limits.conf +57 -0
  246. data/cookbooks/mu-tools/files/redhat/etc/sysconfig/init +19 -0
  247. data/cookbooks/mu-tools/files/redhat/etc/sysctl.conf +82 -0
  248. data/cookbooks/mu-tools/files/redhat-6/README_MU +0 -0
  249. data/cookbooks/mu-tools/files/redhat-6/etc/audit/stig.rules +173 -0
  250. data/cookbooks/mu-tools/files/redhat-6/etc/bashrc +90 -0
  251. data/cookbooks/mu-tools/files/redhat-6/etc/login.defs +70 -0
  252. data/cookbooks/mu-tools/files/redhat-6/etc/pam.d/su +12 -0
  253. data/cookbooks/mu-tools/files/redhat-6/etc/profile +83 -0
  254. data/cookbooks/mu-tools/files/redhat-6/etc/securetty +12 -0
  255. data/cookbooks/mu-tools/files/redhat-6/etc/sysconfig/init +30 -0
  256. data/cookbooks/mu-tools/files/redhat-6/etc/sysctl.conf +40 -0
  257. data/cookbooks/mu-tools/files/redhat-7.1/etc/freshclam.conf +235 -0
  258. data/cookbooks/mu-tools/files/ubuntu-12.04/etc/bash.bashrc +64 -0
  259. data/cookbooks/mu-tools/files/ubuntu-12.04/etc/common-session +30 -0
  260. data/cookbooks/mu-tools/files/ubuntu-12.04/etc/login.defs +338 -0
  261. data/cookbooks/mu-tools/files/ubuntu-12.04/etc/profile +30 -0
  262. data/cookbooks/mu-tools/files/ubuntu-12.04/etc/security/limits.conf +56 -0
  263. data/cookbooks/mu-tools/files/ubuntu-12.04/etc/sysctl.conf +60 -0
  264. data/cookbooks/mu-tools/libraries/helper.rb +292 -0
  265. data/cookbooks/mu-tools/metadata.rb +28 -0
  266. data/cookbooks/mu-tools/recipes/add_admin_ssh_keys.rb +35 -0
  267. data/cookbooks/mu-tools/recipes/apply_security.rb +440 -0
  268. data/cookbooks/mu-tools/recipes/aws_api.rb +23 -0
  269. data/cookbooks/mu-tools/recipes/base_repositories.rb +31 -0
  270. data/cookbooks/mu-tools/recipes/cisbenchmark.rb +59 -0
  271. data/cookbooks/mu-tools/recipes/clamav.rb +53 -0
  272. data/cookbooks/mu-tools/recipes/cloudinit.rb +58 -0
  273. data/cookbooks/mu-tools/recipes/configure_oracle_tools.rb +81 -0
  274. data/cookbooks/mu-tools/recipes/disable-requiretty.rb +22 -0
  275. data/cookbooks/mu-tools/recipes/ebs_rolling_snapshots.rb +75 -0
  276. data/cookbooks/mu-tools/recipes/efs.rb +70 -0
  277. data/cookbooks/mu-tools/recipes/eks.rb +160 -0
  278. data/cookbooks/mu-tools/recipes/gcloud.rb +98 -0
  279. data/cookbooks/mu-tools/recipes/google_api.rb +25 -0
  280. data/cookbooks/mu-tools/recipes/maldet.rb +67 -0
  281. data/cookbooks/mu-tools/recipes/nagios.rb +19 -0
  282. data/cookbooks/mu-tools/recipes/newclient.rb +23 -0
  283. data/cookbooks/mu-tools/recipes/nrpe.rb +115 -0
  284. data/cookbooks/mu-tools/recipes/python_pip.rb +35 -0
  285. data/cookbooks/mu-tools/recipes/retrieve_application.rb +51 -0
  286. data/cookbooks/mu-tools/recipes/rsyslog.rb +65 -0
  287. data/cookbooks/mu-tools/recipes/set_local_fw.rb +57 -0
  288. data/cookbooks/mu-tools/recipes/set_mu_hostname.rb +81 -0
  289. data/cookbooks/mu-tools/recipes/split_var_partitions.rb +86 -0
  290. data/cookbooks/mu-tools/recipes/splunk-client.rb +69 -0
  291. data/cookbooks/mu-tools/recipes/splunk-server.rb +104 -0
  292. data/cookbooks/mu-tools/recipes/store_inspec_attr.rb +8 -0
  293. data/cookbooks/mu-tools/recipes/updates.rb +96 -0
  294. data/cookbooks/mu-tools/recipes/windows-client.rb +202 -0
  295. data/cookbooks/mu-tools/resources/aws_windows.rb +33 -0
  296. data/cookbooks/mu-tools/resources/disk.rb +88 -0
  297. data/cookbooks/mu-tools/resources/mommacat_request.rb +11 -0
  298. data/cookbooks/mu-tools/resources/scheduled_tasks.rb +29 -0
  299. data/cookbooks/mu-tools/resources/sshd_service.rb +45 -0
  300. data/cookbooks/mu-tools/resources/windows_users.rb +242 -0
  301. data/cookbooks/mu-tools/templates/amazon/sshd_config.erb +168 -0
  302. data/cookbooks/mu-tools/templates/centos-6/sshd_config.erb +212 -0
  303. data/cookbooks/mu-tools/templates/centos-7/sshd_config.erb +215 -0
  304. data/cookbooks/mu-tools/templates/default/0-mu-log-client.conf.erb +13 -0
  305. data/cookbooks/mu-tools/templates/default/conf.maldet.erb +137 -0
  306. data/cookbooks/mu-tools/templates/default/etc_hosts.erb +30 -0
  307. data/cookbooks/mu-tools/templates/default/etc_pamd_password-auth.erb +14 -0
  308. data/cookbooks/mu-tools/templates/default/etc_pamd_system-auth.erb +14 -0
  309. data/cookbooks/mu-tools/templates/default/etc_sysconfig_network.erb +12 -0
  310. data/cookbooks/mu-tools/templates/default/kubeconfig.erb +29 -0
  311. data/cookbooks/mu-tools/templates/default/kubelet.service.erb +35 -0
  312. data/cookbooks/mu-tools/templates/default/maldet_scanall.sh.erb +15 -0
  313. data/cookbooks/mu-tools/templates/default/nrpe.cfg.erb +233 -0
  314. data/cookbooks/mu-tools/templates/redhat-6/sshd_config.erb +213 -0
  315. data/cookbooks/mu-tools/templates/redhat-7/sshd_config.erb +215 -0
  316. data/cookbooks/mu-tools/templates/ubuntu-12.04/sshd_config.erb +146 -0
  317. data/cookbooks/mu-tools/templates/ubuntu-14.04/sshd_config.erb +145 -0
  318. data/cookbooks/mu-tools/templates/windows/Backup.xml.erb +20 -0
  319. data/cookbooks/mu-tools/templates/windows/bkupInfo.xml.erb +1 -0
  320. data/cookbooks/mu-tools/templates/windows/gpreprt.xml.erb +214 -0
  321. data/cookbooks/mu-tools/templates/windows/gptmpl.inf.erb +12 -0
  322. data/cookbooks/mu-tools/templates/windows/manifest.xml.erb +1 -0
  323. data/cookbooks/mu-tools/templates/windows/set_ad_dns_scheduled_task.ps1.erb +6 -0
  324. data/cookbooks/mu-tools/templates/windows/sshd_config.erb +136 -0
  325. data/cookbooks/mu-utility/CHANGELOG.md +12 -0
  326. data/cookbooks/mu-utility/LICENSE +37 -0
  327. data/cookbooks/mu-utility/README.md +6 -0
  328. data/cookbooks/mu-utility/attributes/default.rb +1 -0
  329. data/cookbooks/mu-utility/libraries/matchers.rb +21 -0
  330. data/cookbooks/mu-utility/metadata.rb +16 -0
  331. data/cookbooks/mu-utility/recipes/apt.rb +23 -0
  332. data/cookbooks/mu-utility/recipes/cleanup_image_helper.rb +118 -0
  333. data/cookbooks/mu-utility/recipes/iptables.rb +26 -0
  334. data/cookbooks/mu-utility/recipes/luks.rb +18 -0
  335. data/cookbooks/mu-utility/recipes/nat.rb +104 -0
  336. data/cookbooks/mu-utility/recipes/php.rb +33 -0
  337. data/cookbooks/mu-utility/recipes/rdp_gateway.rb +83 -0
  338. data/cookbooks/mu-utility/recipes/remi.rb +44 -0
  339. data/cookbooks/mu-utility/recipes/vim.rb +26 -0
  340. data/cookbooks/mu-utility/recipes/windows_basics.rb +37 -0
  341. data/cookbooks/mu-utility/recipes/zip.rb +26 -0
  342. data/cookbooks/mu-utility/templates/default/BundleConfig.xml.erb +34 -0
  343. data/cookbooks/mu-utility/templates/default/config.xml.erb +60 -0
  344. data/cookbooks/nagios/Berksfile +8 -0
  345. data/cookbooks/nagios/CHANGELOG.md +589 -0
  346. data/cookbooks/nagios/CONTRIBUTING.md +11 -0
  347. data/cookbooks/nagios/LICENSE +37 -0
  348. data/cookbooks/nagios/README.md +328 -0
  349. data/cookbooks/nagios/TESTING.md +2 -0
  350. data/cookbooks/nagios/attributes/config.rb +171 -0
  351. data/cookbooks/nagios/attributes/default.rb +228 -0
  352. data/cookbooks/nagios/chefignore +102 -0
  353. data/cookbooks/nagios/definitions/command.rb +33 -0
  354. data/cookbooks/nagios/definitions/contact.rb +33 -0
  355. data/cookbooks/nagios/definitions/contactgroup.rb +33 -0
  356. data/cookbooks/nagios/definitions/host.rb +33 -0
  357. data/cookbooks/nagios/definitions/hostdependency.rb +33 -0
  358. data/cookbooks/nagios/definitions/hostescalation.rb +34 -0
  359. data/cookbooks/nagios/definitions/hostgroup.rb +33 -0
  360. data/cookbooks/nagios/definitions/nagios_conf.rb +38 -0
  361. data/cookbooks/nagios/definitions/resource.rb +33 -0
  362. data/cookbooks/nagios/definitions/service.rb +33 -0
  363. data/cookbooks/nagios/definitions/servicedependency.rb +33 -0
  364. data/cookbooks/nagios/definitions/serviceescalation.rb +34 -0
  365. data/cookbooks/nagios/definitions/servicegroup.rb +33 -0
  366. data/cookbooks/nagios/definitions/timeperiod.rb +33 -0
  367. data/cookbooks/nagios/libraries/base.rb +314 -0
  368. data/cookbooks/nagios/libraries/command.rb +91 -0
  369. data/cookbooks/nagios/libraries/contact.rb +230 -0
  370. data/cookbooks/nagios/libraries/contactgroup.rb +112 -0
  371. data/cookbooks/nagios/libraries/custom_option.rb +36 -0
  372. data/cookbooks/nagios/libraries/data_bag_helper.rb +23 -0
  373. data/cookbooks/nagios/libraries/default.rb +90 -0
  374. data/cookbooks/nagios/libraries/host.rb +412 -0
  375. data/cookbooks/nagios/libraries/hostdependency.rb +181 -0
  376. data/cookbooks/nagios/libraries/hostescalation.rb +173 -0
  377. data/cookbooks/nagios/libraries/hostgroup.rb +119 -0
  378. data/cookbooks/nagios/libraries/nagios.rb +282 -0
  379. data/cookbooks/nagios/libraries/resource.rb +59 -0
  380. data/cookbooks/nagios/libraries/service.rb +455 -0
  381. data/cookbooks/nagios/libraries/servicedependency.rb +215 -0
  382. data/cookbooks/nagios/libraries/serviceescalation.rb +195 -0
  383. data/cookbooks/nagios/libraries/servicegroup.rb +144 -0
  384. data/cookbooks/nagios/libraries/timeperiod.rb +160 -0
  385. data/cookbooks/nagios/libraries/users_helper.rb +54 -0
  386. data/cookbooks/nagios/metadata.rb +25 -0
  387. data/cookbooks/nagios/recipes/_load_databag_config.rb +153 -0
  388. data/cookbooks/nagios/recipes/_load_default_config.rb +241 -0
  389. data/cookbooks/nagios/recipes/apache.rb +48 -0
  390. data/cookbooks/nagios/recipes/default.rb +204 -0
  391. data/cookbooks/nagios/recipes/nginx.rb +82 -0
  392. data/cookbooks/nagios/recipes/pagerduty.rb +143 -0
  393. data/cookbooks/nagios/recipes/server_package.rb +40 -0
  394. data/cookbooks/nagios/recipes/server_source.rb +164 -0
  395. data/cookbooks/nagios/templates/default/apache2.conf.erb +96 -0
  396. data/cookbooks/nagios/templates/default/cgi.cfg.erb +266 -0
  397. data/cookbooks/nagios/templates/default/commands.cfg.erb +13 -0
  398. data/cookbooks/nagios/templates/default/contacts.cfg.erb +37 -0
  399. data/cookbooks/nagios/templates/default/hostgroups.cfg.erb +25 -0
  400. data/cookbooks/nagios/templates/default/hosts.cfg.erb +15 -0
  401. data/cookbooks/nagios/templates/default/htpasswd.users.erb +6 -0
  402. data/cookbooks/nagios/templates/default/nagios.cfg.erb +22 -0
  403. data/cookbooks/nagios/templates/default/nginx.conf.erb +62 -0
  404. data/cookbooks/nagios/templates/default/pagerduty.cgi.erb +185 -0
  405. data/cookbooks/nagios/templates/default/resource.cfg.erb +27 -0
  406. data/cookbooks/nagios/templates/default/servicedependencies.cfg.erb +15 -0
  407. data/cookbooks/nagios/templates/default/servicegroups.cfg.erb +14 -0
  408. data/cookbooks/nagios/templates/default/services.cfg.erb +14 -0
  409. data/cookbooks/nagios/templates/default/templates.cfg.erb +31 -0
  410. data/cookbooks/nagios/templates/default/timeperiods.cfg.erb +13 -0
  411. data/cookbooks/s3fs/CHANGELOG.md +13 -0
  412. data/cookbooks/s3fs/LICENSE +37 -0
  413. data/cookbooks/s3fs/README.md +6 -0
  414. data/cookbooks/s3fs/attributes/default.rb +15 -0
  415. data/cookbooks/s3fs/files/default/fuse-2.9.3.zip +0 -0
  416. data/cookbooks/s3fs/metadata.rb +16 -0
  417. data/cookbooks/s3fs/recipes/default.rb +91 -0
  418. data/data_bags/demo/app.json +7 -0
  419. data/data_bags/nagios_services/chef.json +6 -0
  420. data/data_bags/nagios_services/linux_diskspace.json +5 -0
  421. data/data_bags/nagios_services/momma_cat.json +6 -0
  422. data/data_bags/nagios_services/mu-master-memory.json +5 -0
  423. data/data_bags/nagios_services/nagios_ui.json +6 -0
  424. data/data_bags/nagios_services/node_ssh.json +6 -0
  425. data/data_bags/nagios_services/ssh.json +6 -0
  426. data/demo/lambda_test.yaml +29 -0
  427. data/environments/DEV.json +8 -0
  428. data/environments/PROD.json +8 -0
  429. data/environments/dev.json +8 -0
  430. data/environments/development.json +8 -0
  431. data/environments/prod.json +8 -0
  432. data/extras/README.md +1 -0
  433. data/extras/admin-role-binding.yaml +16 -0
  434. data/extras/admin-user.yaml +6 -0
  435. data/extras/aws-auth-cm.yaml.erb +12 -0
  436. data/extras/clean-stock-amis +48 -0
  437. data/extras/git-fix-permissions-hook +12 -0
  438. data/extras/gitlab-eks-helper.sh.erb +20 -0
  439. data/extras/image-generators/README.md +2 -0
  440. data/extras/image-generators/aws/centos6.yaml +18 -0
  441. data/extras/image-generators/aws/centos7-govcloud.yaml +24 -0
  442. data/extras/image-generators/aws/centos7.yaml +17 -0
  443. data/extras/image-generators/aws/rhel7.yaml +17 -0
  444. data/extras/image-generators/aws/win2k12.yaml +16 -0
  445. data/extras/image-generators/aws/win2k16.yaml +16 -0
  446. data/extras/image-generators/aws/windows.yaml +18 -0
  447. data/extras/image-generators/gcp/centos6.yaml +17 -0
  448. data/extras/lambda_waf_domain_blacklist.py +103 -0
  449. data/extras/platform_berksfile_base +50 -0
  450. data/extras/ruby_rpm/build.sh +17 -0
  451. data/extras/ruby_rpm/muby.spec +44 -0
  452. data/extras/vault_tools/README.md +6 -0
  453. data/extras/vault_tools/export_vaults.sh +3 -0
  454. data/extras/vault_tools/recreate_vaults.sh +5 -0
  455. data/extras/vault_tools/test_vaults.sh +5 -0
  456. data/install/README.md +8 -0
  457. data/install/cfn_create_mu_master.json +1034 -0
  458. data/install/chef-server.rb.erb +19 -0
  459. data/install/deprecated-bash-library.sh +1891 -0
  460. data/install/images/Usage.png +0 -0
  461. data/install/installer +71 -0
  462. data/install/jenkinskeys.rb +8 -0
  463. data/install/user-dot-murc.erb +14 -0
  464. data/modules/html.erb +19 -0
  465. data/modules/mommacat.ru +426 -0
  466. data/modules/mu/cleanup.rb +339 -0
  467. data/modules/mu/cloud.rb +1446 -0
  468. data/modules/mu/clouds/README.md +201 -0
  469. data/modules/mu/clouds/aws/alarm.rb +319 -0
  470. data/modules/mu/clouds/aws/cache_cluster.rb +1010 -0
  471. data/modules/mu/clouds/aws/collection.rb +373 -0
  472. data/modules/mu/clouds/aws/container_cluster.rb +667 -0
  473. data/modules/mu/clouds/aws/database.rb +1836 -0
  474. data/modules/mu/clouds/aws/dnszone.rb +911 -0
  475. data/modules/mu/clouds/aws/firewall_rule.rb +641 -0
  476. data/modules/mu/clouds/aws/folder.rb +92 -0
  477. data/modules/mu/clouds/aws/function.rb +349 -0
  478. data/modules/mu/clouds/aws/group.rb +251 -0
  479. data/modules/mu/clouds/aws/loadbalancer.rb +888 -0
  480. data/modules/mu/clouds/aws/log.rb +363 -0
  481. data/modules/mu/clouds/aws/msg_queue.rb +480 -0
  482. data/modules/mu/clouds/aws/notification.rb +139 -0
  483. data/modules/mu/clouds/aws/role.rb +656 -0
  484. data/modules/mu/clouds/aws/search_domain.rb +646 -0
  485. data/modules/mu/clouds/aws/server.rb +2294 -0
  486. data/modules/mu/clouds/aws/server_pool.rb +1388 -0
  487. data/modules/mu/clouds/aws/storage_pool.rb +495 -0
  488. data/modules/mu/clouds/aws/user.rb +382 -0
  489. data/modules/mu/clouds/aws/userdata/README.md +4 -0
  490. data/modules/mu/clouds/aws/userdata/linux.erb +179 -0
  491. data/modules/mu/clouds/aws/userdata/windows.erb +278 -0
  492. data/modules/mu/clouds/aws/vpc.rb +1943 -0
  493. data/modules/mu/clouds/aws.rb +1009 -0
  494. data/modules/mu/clouds/cloudformation/alarm.rb +146 -0
  495. data/modules/mu/clouds/cloudformation/cache_cluster.rb +167 -0
  496. data/modules/mu/clouds/cloudformation/collection.rb +117 -0
  497. data/modules/mu/clouds/cloudformation/database.rb +278 -0
  498. data/modules/mu/clouds/cloudformation/dnszone.rb +274 -0
  499. data/modules/mu/clouds/cloudformation/firewall_rule.rb +308 -0
  500. data/modules/mu/clouds/cloudformation/loadbalancer.rb +193 -0
  501. data/modules/mu/clouds/cloudformation/log.rb +170 -0
  502. data/modules/mu/clouds/cloudformation/server.rb +370 -0
  503. data/modules/mu/clouds/cloudformation/server_pool.rb +279 -0
  504. data/modules/mu/clouds/cloudformation/vpc.rb +322 -0
  505. data/modules/mu/clouds/cloudformation.rb +733 -0
  506. data/modules/mu/clouds/docker.rb +30 -0
  507. data/modules/mu/clouds/google/container_cluster.rb +290 -0
  508. data/modules/mu/clouds/google/database.rb +152 -0
  509. data/modules/mu/clouds/google/firewall_rule.rb +267 -0
  510. data/modules/mu/clouds/google/group.rb +164 -0
  511. data/modules/mu/clouds/google/loadbalancer.rb +479 -0
  512. data/modules/mu/clouds/google/server.rb +1510 -0
  513. data/modules/mu/clouds/google/server_pool.rb +274 -0
  514. data/modules/mu/clouds/google/user.rb +266 -0
  515. data/modules/mu/clouds/google/userdata/README.md +4 -0
  516. data/modules/mu/clouds/google/userdata/linux.erb +137 -0
  517. data/modules/mu/clouds/google/userdata/windows.erb +275 -0
  518. data/modules/mu/clouds/google/vpc.rb +890 -0
  519. data/modules/mu/clouds/google.rb +811 -0
  520. data/modules/mu/config/README.md +11 -0
  521. data/modules/mu/config/alarm.rb +271 -0
  522. data/modules/mu/config/cache_cluster.rb +172 -0
  523. data/modules/mu/config/collection.rb +87 -0
  524. data/modules/mu/config/container_cluster.rb +103 -0
  525. data/modules/mu/config/container_cluster.yml +36 -0
  526. data/modules/mu/config/database.rb +458 -0
  527. data/modules/mu/config/database.yml +26 -0
  528. data/modules/mu/config/dnszone.rb +327 -0
  529. data/modules/mu/config/firewall_rule.rb +118 -0
  530. data/modules/mu/config/folder.rb +70 -0
  531. data/modules/mu/config/function.rb +140 -0
  532. data/modules/mu/config/group.rb +64 -0
  533. data/modules/mu/config/loadbalancer.rb +482 -0
  534. data/modules/mu/config/log.rb +47 -0
  535. data/modules/mu/config/log.yml +6 -0
  536. data/modules/mu/config/msg_queue.rb +47 -0
  537. data/modules/mu/config/msg_queue.yml +9 -0
  538. data/modules/mu/config/notification.rb +44 -0
  539. data/modules/mu/config/project.rb +71 -0
  540. data/modules/mu/config/role.rb +102 -0
  541. data/modules/mu/config/search_domain.rb +61 -0
  542. data/modules/mu/config/search_domain.yml +25 -0
  543. data/modules/mu/config/server.rb +587 -0
  544. data/modules/mu/config/server.yml +8 -0
  545. data/modules/mu/config/server_pool.rb +216 -0
  546. data/modules/mu/config/server_pool.yml +71 -0
  547. data/modules/mu/config/storage_pool.rb +145 -0
  548. data/modules/mu/config/user.rb +78 -0
  549. data/modules/mu/config/vpc.rb +743 -0
  550. data/modules/mu/config/vpc.yml +6 -0
  551. data/modules/mu/config.rb +2000 -0
  552. data/modules/mu/defaults/README.md +2 -0
  553. data/modules/mu/defaults/amazon_images.yaml +121 -0
  554. data/modules/mu/defaults/google_images.yaml +16 -0
  555. data/modules/mu/deploy.rb +686 -0
  556. data/modules/mu/groomer.rb +123 -0
  557. data/modules/mu/groomers/README.md +58 -0
  558. data/modules/mu/groomers/chef.rb +1024 -0
  559. data/modules/mu/kittens.rb +11319 -0
  560. data/modules/mu/logger.rb +208 -0
  561. data/modules/mu/master/README.md +27 -0
  562. data/modules/mu/master/chef.rb +471 -0
  563. data/modules/mu/master/ldap.rb +1005 -0
  564. data/modules/mu/master.rb +415 -0
  565. data/modules/mu/mommacat.rb +2703 -0
  566. data/modules/mu-load-config.rb +1 -0
  567. data/modules/mu.rb +724 -0
  568. data/modules/scratchpad.erb +1 -0
  569. data/modules/tests/super_complex_bok.yml +41 -0
  570. data/modules/tests/super_simple_bok.yml +40 -0
  571. data/mu.gemspec +62 -0
  572. data/roles/demo-dbservice-configure.json +19 -0
  573. data/roles/demo-portal-configure.json +19 -0
  574. data/roles/mu-master-jenkins.json +24 -0
  575. data/roles/mu-master-nagios-only.json +13 -0
  576. data/roles/mu-master.json +12 -0
  577. data/roles/mu-node.json +19 -0
  578. data/roles/mu-splunk-server.json +13 -0
  579. data/roles/mu-splunk.json +13 -0
  580. data/test/clean_up.py +25 -0
  581. data/test/demo-test-profile/README.md +3 -0
  582. data/test/demo-test-profile/controls/flask.rb +84 -0
  583. data/test/demo-test-profile/inspec.lock +7 -0
  584. data/test/demo-test-profile/inspec.yml +11 -0
  585. data/test/etco-test-profile/README.md +3 -0
  586. data/test/etco-test-profile/controls/all-in-one.rb +182 -0
  587. data/test/etco-test-profile/inspec.lock +7 -0
  588. data/test/etco-test-profile/inspec.yml +11 -0
  589. data/test/exec_inspec.py +246 -0
  590. data/test/exec_mu_install.py +241 -0
  591. data/test/exec_retry.py +44 -0
  592. data/test/mu-master-test/README.md +3 -0
  593. data/test/mu-master-test/controls/all_in_one.rb +557 -0
  594. data/test/mu-master-test/inspec.lock +3 -0
  595. data/test/mu-master-test/inspec.yml +11 -0
  596. data/test/mu-tools-test/README.md +3 -0
  597. data/test/mu-tools-test/controls/base.rb +265 -0
  598. data/test/mu-tools-test/inspec.lock +3 -0
  599. data/test/mu-tools-test/inspec.yml +8 -0
  600. data/test/simple-server-php-test/README.md +3 -0
  601. data/test/simple-server-php-test/controls/apachephp.rb +25 -0
  602. data/test/simple-server-php-test/controls/example.rb +19 -0
  603. data/test/simple-server-php-test/inspec.lock +7 -0
  604. data/test/simple-server-php-test/inspec.yml +12 -0
  605. data/test/simple-server-rails-test/README.md +3 -0
  606. data/test/simple-server-rails-test/controls/rails.rb +188 -0
  607. data/test/simple-server-rails-test/inspec.lock +7 -0
  608. data/test/simple-server-rails-test/inspec.yml +11 -0
  609. data/test/simple-windows-test/README.md +3 -0
  610. data/test/simple-windows-test/controls/windows.rb +20 -0
  611. data/test/simple-windows-test/inspec.lock +7 -0
  612. data/test/simple-windows-test/inspec.yml +11 -0
  613. data/test/smoke_test.rb +75 -0
  614. data/test/wordpress-test/README.md +3 -0
  615. data/test/wordpress-test/controls/wordpress.rb +97 -0
  616. data/test/wordpress-test/inspec.lock +7 -0
  617. data/test/wordpress-test/inspec.yml +11 -0
  618. metadata +979 -0
@@ -0,0 +1,1943 @@
1
+ # Copyright:: Copyright (c) 2014 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 Cloud
17
+ class AWS
18
+
19
+ # Creation of Virtual Private Clouds and associated artifacts (routes, subnets, etc).
20
+ class VPC < MU::Cloud::VPC
21
+
22
+ @deploy = nil
23
+ @config = nil
24
+ attr_reader :mu_name
25
+ attr_reader :cloud_id
26
+ attr_reader :config
27
+
28
+ # @param mommacat [MU::MommaCat]: A {MU::Mommacat} object containing the deploy of which this resource is/will be a member.
29
+ # @param kitten_cfg [Hash]: The fully parsed and resolved {MU::Config} resource descriptor as defined in {MU::Config::BasketofKittens::vpcs}
30
+ def initialize(mommacat: nil, kitten_cfg: nil, mu_name: nil, cloud_id: nil)
31
+ @deploy = mommacat
32
+ @config = MU::Config.manxify(kitten_cfg)
33
+ @subnets = []
34
+ @subnetcachesemaphore = Mutex.new
35
+ @cloud_id = cloud_id
36
+ if !mu_name.nil?
37
+ @mu_name = mu_name
38
+ loadSubnets if !@cloud_id.nil?
39
+ elsif @config['scrub_mu_isms']
40
+ @mu_name = @config['name']
41
+ else
42
+ @mu_name = @deploy.getResourceName(@config['name'])
43
+ end
44
+ end
45
+
46
+ # Called automatically by {MU::Deploy#createResources}
47
+ def create
48
+ MU.log "Creating VPC #{@mu_name}", details: @config
49
+ resp = MU::Cloud::AWS.ec2(@config['region']).create_vpc(cidr_block: @config['ip_block']).vpc
50
+ vpc_id = @config['vpc_id'] = resp.vpc_id
51
+
52
+ MU::MommaCat.createStandardTags(vpc_id, region: @config['region'])
53
+ MU::MommaCat.createTag(vpc_id, "Name", @mu_name, region: @config['region'])
54
+
55
+ if @config['tags']
56
+ @config['tags'].each { |tag|
57
+ MU::MommaCat.createTag(vpc_id, tag['key'], tag['value'], region: @config['region'])
58
+ }
59
+ end
60
+
61
+ if @config['optional_tags']
62
+ MU::MommaCat.listOptionalTags.each { |key, value|
63
+ MU::MommaCat.createTag(vpc_id, key, value, region: @config['region'])
64
+ }
65
+ end
66
+
67
+ if resp.state != "available"
68
+ begin
69
+ MU.log "Waiting for VPC #{@mu_name} (#{vpc_id}) to be available", MU::NOTICE
70
+ sleep 5
71
+ resp = MU::Cloud::AWS.ec2(@config['region']).describe_vpcs(vpc_ids: [vpc_id]).vpcs.first
72
+ end while resp.state != "available"
73
+ # There's a default route table that comes with. Let's tag it.
74
+ resp = MU::Cloud::AWS.ec2(@config['region']).describe_route_tables(
75
+ filters: [
76
+ {
77
+ name: "vpc-id",
78
+ values: [vpc_id]
79
+ }
80
+ ]
81
+ )
82
+ resp.route_tables.each { |rtb|
83
+ MU::MommaCat.createTag(rtb.route_table_id, "Name", @mu_name+"-#DEFAULTPRIV", region: @config['region'])
84
+ if @config['tags']
85
+ @config['tags'].each { |tag|
86
+ MU::MommaCat.createTag(rtb.route_table_id, tag['key'], tag['value'], region: @config['region'])
87
+ }
88
+ end
89
+
90
+ MU::MommaCat.createStandardTags(rtb.route_table_id, region: @config['region'])
91
+
92
+ if @config['optional_tags']
93
+ MU::MommaCat.listOptionalTags.each { |key, value|
94
+ MU::MommaCat.createTag(rtb.route_table_id, key, value, region: @config['region'])
95
+ }
96
+ end
97
+ }
98
+ end
99
+ @config['vpc_id'] = vpc_id
100
+ @cloud_id = vpc_id
101
+
102
+ if @config['create_internet_gateway']
103
+ MU.log "Creating Internet Gateway #{@mu_name}"
104
+ resp = MU::Cloud::AWS.ec2(@config['region']).create_internet_gateway
105
+ internet_gateway_id = resp.internet_gateway.internet_gateway_id
106
+ sleep 5
107
+ MU::MommaCat.createStandardTags(internet_gateway_id, region: @config['region'])
108
+ MU::MommaCat.createTag(internet_gateway_id, "Name", @mu_name, region: @config['region'])
109
+ if @config['tags']
110
+ @config['tags'].each { |tag|
111
+ MU::MommaCat.createTag(internet_gateway_id, tag['key'], tag['value'], region: @config['region'])
112
+ }
113
+ end
114
+
115
+ if @config['optional_tags']
116
+ MU::MommaCat.listOptionalTags.each { |key, value|
117
+ MU::MommaCat.createTag(internet_gateway_id, key, value, region: @config['region'])
118
+ }
119
+ end
120
+
121
+ MU::Cloud::AWS.ec2(@config['region']).attach_internet_gateway(vpc_id: vpc_id, internet_gateway_id: internet_gateway_id)
122
+ @config['internet_gateway_id'] = internet_gateway_id
123
+ end
124
+
125
+ route_table_ids = []
126
+ if !@config['route_tables'].nil?
127
+ @config['route_tables'].each { |rtb|
128
+ rtb = createRouteTable(rtb)
129
+ route_table_ids << rtb['route_table_id']
130
+ }
131
+ end
132
+
133
+ if @config['endpoint']
134
+ config = {
135
+ :vpc_id => @cloud_id,
136
+ :service_name => @config['endpoint'],
137
+ :route_table_ids => route_table_ids
138
+ }
139
+
140
+ if @config['endpoint_policy'] && !@config['endpoint_policy'].empty?
141
+ statement = {:Statement => @config['endpoint_policy']}
142
+ config[:policy_document] = statement.to_json
143
+ end
144
+
145
+ resp = MU::Cloud::AWS.ec2(@config['region']).create_vpc_endpoint(config).vpc_endpoint
146
+ endpoint_id = resp.vpc_endpoint_id
147
+ MU.log "Creating VPC endpoint #{endpoint_id}"
148
+ attempts = 0
149
+
150
+ while resp.state == "pending"
151
+ MU.log "Waiting for VPC endpoint #{endpoint_id} to become available" if attempts % 5 == 0
152
+ sleep 10
153
+ begin
154
+ resp = MU::Cloud::AWS.ec2(@config['region']).describe_vpc_endpoints(vpc_endpoint_ids: [endpoint_id]).vpc_endpoints.first
155
+ rescue Aws::EmptyStructure, NoMethodError
156
+ sleep 5
157
+ retry
158
+ end
159
+ raise MuError, "Timed out while waiting for VPC endpoint #{endpoint_id}: #{resp}" if attempts > 30
160
+ attempts += 1
161
+ end
162
+
163
+ raise MuError, "VPC endpoint failed #{endpoint_id}: #{resp}" if resp.state == "failed"
164
+ end
165
+
166
+ if @config["enable_traffic_logging"]
167
+ loggroup = @deploy.findLitterMate(name: @config['name']+"loggroup", type: "logs")
168
+ logrole = @deploy.findLitterMate(name: @config['name']+"logrole", type: "roles")
169
+
170
+ MU.log "Enabling traffic logging on VPC #{@mu_name} to log group #{loggroup.mu_name}"
171
+ MU::Cloud::AWS.ec2(@config['region']).create_flow_logs(
172
+ resource_ids: [@cloud_id],
173
+ resource_type: "VPC",
174
+ traffic_type: "ALL",
175
+ log_group_name: loggroup.mu_name,
176
+ deliver_logs_permission_arn: logrole.cloudobj.arn
177
+ )
178
+ end
179
+
180
+ nat_gateways = []
181
+ if !@config['subnets'].nil?
182
+ allocation_ids = []
183
+ subnet_semaphore = Mutex.new
184
+ subnetthreads = Array.new
185
+ parent_thread_id = Thread.current.object_id
186
+ azs = []
187
+ @config['subnets'].each { |subnet|
188
+ subnet_name = @config['name']+"-"+subnet['name']
189
+ MU.log "Creating Subnet #{subnet_name} (#{subnet['ip_block']})", details: subnet
190
+ azs = MU::Cloud::AWS.listAZs if azs.size == 0
191
+ if !subnet['availability_zone'].nil?
192
+ az = subnet['availability_zone']
193
+ else
194
+ az = azs.pop
195
+ end
196
+
197
+ subnetthreads << Thread.new {
198
+ MU.dupGlobals(parent_thread_id)
199
+ resp = MU::Cloud::AWS.ec2(@config['region']).create_subnet(
200
+ vpc_id: vpc_id,
201
+ cidr_block: subnet['ip_block'],
202
+ availability_zone: az
203
+ ).subnet
204
+ subnet_id = subnet['subnet_id'] = resp.subnet_id
205
+ MU::MommaCat.createStandardTags(subnet_id, region: @config['region'])
206
+ MU::MommaCat.createTag(subnet_id, "Name", @mu_name+"-"+subnet['name'], region: @config['region'])
207
+ if @config['tags']
208
+ @config['tags'].each { |tag|
209
+ MU::MommaCat.createTag(subnet_id, tag['key'], tag['value'], region: @config['region'])
210
+ }
211
+ end
212
+
213
+ if @config['optional_tags']
214
+ MU::MommaCat.listOptionalTags.each { |key, value|
215
+ MU::MommaCat.createTag(subnet_id, key, value, region: @config['region'])
216
+ }
217
+ end
218
+
219
+ retries = 0
220
+ begin
221
+ if resp.state != "available"
222
+ begin
223
+ MU.log "Waiting for Subnet #{subnet_name} (#{subnet_id}) to be available", MU::NOTICE
224
+ sleep 5
225
+ resp = MU::Cloud::AWS.ec2(@config['region']).describe_subnets(subnet_ids: [subnet_id]).subnets.first
226
+ rescue Aws::EC2::Errors::InvalidSubnetIDNotFound => e
227
+ sleep 10
228
+ retry
229
+ end while resp.state != "available"
230
+ end
231
+ rescue NoMethodError => e
232
+ if retries <= 3
233
+ MU.log "Got bogus Aws::EmptyResponse error on #{subnet_id} (retries used: #{retries}/3)", MU::WARN
234
+ retries = retries + 1
235
+ sleep 5
236
+ resp = MU::Cloud::AWS.ec2(@config['region']).describe_subnets(subnet_ids: [subnet_id]).subnets.first
237
+ retry
238
+ else
239
+ raise e
240
+ end
241
+ end
242
+
243
+ if !subnet['route_table'].nil?
244
+ routes = {}
245
+ @config['route_tables'].each { |tbl|
246
+ routes[tbl['name']] = tbl
247
+ }
248
+ if routes.nil? or routes[subnet['route_table']].nil?
249
+ MU.log "Subnet #{subnet_name} references non-existent route #{subnet['route_table']}", MU::ERR, details: @deploy.deployment['vpcs']
250
+ raise MuError, "deploy failure"
251
+ end
252
+ MU.log "Associating Route Table '#{subnet['route_table']}' (#{routes[subnet['route_table']]['route_table_id']}) with #{subnet_name}"
253
+ retries = 0
254
+ begin
255
+ MU::Cloud::AWS.ec2(@config['region']).associate_route_table(
256
+ route_table_id: routes[subnet['route_table']]['route_table_id'],
257
+ subnet_id: subnet_id
258
+ )
259
+ rescue Aws::EC2::Errors::InvalidRouteTableIDNotFound => e
260
+ retries = retries + 1
261
+ if retries < 10
262
+ sleep 10
263
+ retry
264
+ else
265
+ raise MuError, e.inspect
266
+ end
267
+ end
268
+ end
269
+ retries = 0
270
+ begin
271
+ resp = MU::Cloud::AWS.ec2(@config['region']).describe_subnets(subnet_ids: [subnet_id]).subnets.first
272
+ rescue Aws::EC2::Errors::InvalidSubnetIDNotFound => e
273
+ if retries < 10
274
+ MU.log "Got #{e.inspect}, waiting and retrying", MU::WARN
275
+ sleep 10
276
+ retries = retries + 1
277
+ retry
278
+ end
279
+ raise MuError, e.inspect, e.backtrace
280
+ end
281
+
282
+ if subnet['is_public'] && subnet['create_nat_gateway']
283
+ MU::MommaCat.lock("nat-gateway-eipalloc")
284
+ filters = [{name: "domain", values: ["vpc"]}]
285
+ eips = MU::Cloud::AWS.ec2(@config['region']).describe_addresses(filters: filters).addresses
286
+ allocation_id = nil
287
+ eips.each { |eip|
288
+ next if !eip.association_id.nil? and !eip.association_id.empty?
289
+ if (eip.private_ip_address.nil? || eip.private_ip_address.empty?) and MU::MommaCat.lock(eip.allocation_id, true, true)
290
+ if !allocation_ids.include?(eip.allocation_id)
291
+ allocation_id = eip.allocation_id
292
+ break
293
+ end
294
+ end
295
+ }
296
+
297
+ if allocation_id.nil?
298
+ allocation_id = MU::Cloud::AWS.ec2(@config['region']).allocate_address(domain: "vpc").allocation_id
299
+ MU::MommaCat.lock(allocation_id, false, true)
300
+ end
301
+
302
+ allocation_ids << allocation_id
303
+ resp = MU::Cloud::AWS.ec2(@config['region']).create_nat_gateway(
304
+ subnet_id: subnet['subnet_id'],
305
+ allocation_id: allocation_id
306
+ ).nat_gateway
307
+
308
+ nat_gateway_id = resp.nat_gateway_id
309
+ attempts = 0
310
+ MU::MommaCat.unlock("nat-gateway-eipalloc")
311
+ while resp.state == "pending"
312
+ MU.log "Waiting for nat gateway #{nat_gateway_id} () to become available (EIP allocation: #{allocation_id})" if attempts % 5 == 0
313
+ sleep 30
314
+ begin
315
+ resp = MU::Cloud::AWS.ec2(@config['region']).describe_nat_gateways(nat_gateway_ids: [nat_gateway_id]).nat_gateways.first
316
+ rescue Aws::EmptyStructure, NoMethodError
317
+ sleep 5
318
+ retry
319
+ end
320
+ if attempts > 30
321
+ MU::MommaCat.unlock(allocation_id, true)
322
+ raise MuError, "Timed out while waiting for NAT Gateway #{nat_gateway_id}: #{resp}"
323
+ end
324
+ attempts += 1
325
+ end
326
+ MU::MommaCat.unlock(allocation_id, true)
327
+
328
+ raise MuError, "NAT Gateway failed #{nat_gateway_id}: #{resp}" if resp.state == "failed"
329
+ nat_gateways << {'id' => nat_gateway_id, 'availability_zone' => subnet['availability_zone']}
330
+ end
331
+
332
+ if subnet.has_key?("map_public_ips")
333
+ retries = 0
334
+ begin
335
+ resp = MU::Cloud::AWS.ec2(@config['region']).modify_subnet_attribute(
336
+ subnet_id: subnet_id,
337
+ map_public_ip_on_launch: {
338
+ value: subnet['map_public_ips'],
339
+ }
340
+ )
341
+ rescue Aws::EC2::Errors::InvalidSubnetIDNotFound => e
342
+ if retries < 10
343
+ MU.log "Got #{e.inspect} while trying to enable map_public_ips on subnet, waiting and retrying", MU::WARN
344
+ sleep 10
345
+ retries += 1
346
+ retry
347
+ end
348
+ raise MuError, "Got #{e.inspect}, #{e.backtrace} while trying to enable map_public_ips on subnet"
349
+ end
350
+ end
351
+
352
+ if subnet["enable_traffic_logging"]
353
+ loggroup = @deploy.findLitterMate(name: @config['name']+"loggroup", type: "logs")
354
+ logrole = @deploy.findLitterMate(name: @config['name']+"logrole", type: "roles")
355
+ MU.log "Enabling traffic logging on Subnet #{subnet_name} in VPC #{@mu_name} to log group #{loggroup.mu_name}"
356
+ MU::Cloud::AWS.ec2(@config['region']).create_flow_logs(
357
+ resource_ids: [subnet_id],
358
+ resource_type: "Subnet",
359
+ traffic_type: subnet["traffic_type_to_log"],
360
+ log_group_name: loggroup.mu_name,
361
+ deliver_logs_permission_arn: logrole.cloudobj.arn
362
+ )
363
+ end
364
+ }
365
+ }
366
+
367
+ subnetthreads.each { |t|
368
+ t.join
369
+ }
370
+
371
+ notify
372
+ end
373
+
374
+ if !nat_gateways.empty?
375
+ nat_gateways.each { |gateway|
376
+ @config['subnets'].each { |subnet|
377
+ if subnet['is_public'] == false && subnet['availability_zone'] == gateway['availability_zone']
378
+ @config['route_tables'].each { |rtb|
379
+ if rtb['name'] == subnet['route_table']
380
+ rtb['routes'].each { |route|
381
+ if route['gateway'] == '#NAT'
382
+ route_config = {
383
+ :route_table_id => rtb['route_table_id'],
384
+ :destination_cidr_block => route['destination_network'],
385
+ :nat_gateway_id => gateway['id']
386
+ }
387
+
388
+ MU.log "Creating route for #{route['destination_network']} through NAT gatway #{gateway['id']}", details: route_config
389
+ begin
390
+ resp = MU::Cloud::AWS.ec2(@config['region']).create_route(route_config)
391
+ rescue Aws::EC2::Errors::RouteAlreadyExists => e
392
+ MU.log "Attempt to create duplicate route to #{route['destination_network']} for #{gateway['id']} in #{rtb['route_table_id']}", MU::WARN
393
+ end
394
+ end
395
+ }
396
+ end
397
+ }
398
+ end
399
+ }
400
+ }
401
+ end
402
+
403
+ if @config['enable_dns_support']
404
+ MU.log "Enabling DNS support in #{@mu_name}"
405
+ MU::Cloud::AWS.ec2(@config['region']).modify_vpc_attribute(
406
+ vpc_id: vpc_id,
407
+ enable_dns_support: {value: @config['enable_dns_support']}
408
+ )
409
+ end
410
+ if @config['enable_dns_hostnames']
411
+ MU.log "Enabling DNS hostnames in #{@mu_name}"
412
+ MU::Cloud::AWS.ec2(@config['region']).modify_vpc_attribute(
413
+ vpc_id: vpc_id,
414
+ enable_dns_hostnames: {value: @config['enable_dns_hostnames']}
415
+ )
416
+ end
417
+
418
+ if @config['dhcp']
419
+ MU.log "Setting custom DHCP options in #{@mu_name}", details: @config['dhcp']
420
+ dhcpopts = []
421
+
422
+ if @config['dhcp']['netbios_type']
423
+ dhcpopts << {key: "netbios-node-type", values: [@config['dhcp']['netbios_type'].to_s]}
424
+ end
425
+ if @config['dhcp']['domains']
426
+ dhcpopts << {key: "domain-name", values: @config['dhcp']['domains']}
427
+ end
428
+ if @config['dhcp']['dns_servers']
429
+ dhcpopts << {key: "domain-name-servers", values: @config['dhcp']['dns_servers']}
430
+ end
431
+ if @config['dhcp']['ntp_servers']
432
+ dhcpopts << {key: "ntp-servers", values: @config['dhcp']['ntp_servers']}
433
+ end
434
+ if @config['dhcp']['netbios_servers']
435
+ dhcpopts << {key: "netbios-name-servers", values: @config['dhcp']['netbios_servers']}
436
+ end
437
+
438
+ resp = MU::Cloud::AWS.ec2(@config['region']).create_dhcp_options(
439
+ dhcp_configurations: dhcpopts
440
+ )
441
+ dhcpopt_id = resp.dhcp_options.dhcp_options_id
442
+ MU::MommaCat.createStandardTags(dhcpopt_id, region: @config['region'])
443
+ MU::MommaCat.createTag(dhcpopt_id, "Name", @mu_name, region: @config['region'])
444
+
445
+ if @config['tags']
446
+ @config['tags'].each { |tag|
447
+ MU::MommaCat.createTag(dhcpopt_id, tag['key'], tag['value'], region: @config['region'])
448
+ }
449
+ end
450
+
451
+ if @config['optional_tags']
452
+ MU::MommaCat.listOptionalTags.each { |key, value|
453
+ MU::MommaCat.createTag(dhcpopt_id, key, value, region: @config['region'])
454
+ }
455
+ end
456
+
457
+ MU::Cloud::AWS.ec2(@config['region']).associate_dhcp_options(dhcp_options_id: dhcpopt_id, vpc_id: vpc_id)
458
+ end
459
+ notify
460
+
461
+ if !MU::Cloud::AWS.isGovCloud?(@config['region'])
462
+ mu_zone = MU::Cloud::DNSZone.find(cloud_id: "platform-mu").values.first
463
+ if !mu_zone.nil?
464
+ MU::Cloud::AWS::DNSZone.toggleVPCAccess(id: mu_zone.id, vpc_id: vpc_id, region: @config['region'])
465
+ end
466
+ end
467
+ loadSubnets
468
+
469
+ MU.log "VPC #{@mu_name} created", details: @config
470
+ end
471
+
472
+ # Canonical Amazon Resource Number for this resource
473
+ # @return [String]
474
+ def arn
475
+ "arn:"+(MU::Cloud::AWS.isGovCloud?(@config["region"]) ? "aws-us-gov" : "aws")+":ec2:"+@config['region']+":"+MU.account_number+":vpc/"+@cloud_id
476
+ end
477
+
478
+ # Describe this VPC
479
+ # @return [Hash]
480
+ def notify
481
+ @config
482
+ end
483
+
484
+ # Called automatically by {MU::Deploy#createResources}
485
+ def groom
486
+ vpc_name = @deploy.getResourceName(@config['name'])
487
+
488
+ # Generate peering connections
489
+ if !@config['peers'].nil? and @config['peers'].size > 0
490
+ @config['peers'].each { |peer|
491
+ peer_obj = nil
492
+ begin
493
+ if peer['account'].nil? or peer['account'] == MU.account_number
494
+ tag_key, tag_value = peer['vpc']['tag'].split(/=/, 2) if !peer['vpc']['tag'].nil?
495
+ if peer['vpc']['deploy_id'].nil? and peer['vpc']['vpc_id'].nil? and tag_key.nil?
496
+ peer['vpc']['deploy_id'] = @deploy.deploy_id
497
+ end
498
+ peer_obj = MU::MommaCat.findStray(
499
+ "AWS",
500
+ "vpcs",
501
+ deploy_id: peer['vpc']['deploy_id'],
502
+ cloud_id: peer['vpc']['vpc_id'],
503
+ name: peer['vpc']['vpc_name'],
504
+ tag_key: tag_key,
505
+ tag_value: tag_value,
506
+ dummy_ok: true,
507
+ region: peer['vpc']['region']
508
+ )
509
+ raise MuError, "No result looking for #{@mu_name}'s peer VPCs (#{peer['vpc']})" if peer_obj.nil? or peer_obj.first.nil?
510
+ peer_obj = peer_obj.first
511
+ peer_id = peer_obj.cloud_id
512
+
513
+ MU.log "Setting peering connection from VPC #{@config['name']} (#{@cloud_id}) to #{peer_id}"
514
+ resp = MU::Cloud::AWS.ec2(@config['region']).create_vpc_peering_connection(
515
+ vpc_id: @cloud_id,
516
+ peer_vpc_id: peer_id
517
+ )
518
+ else
519
+ peer_id = peer['vpc']['vpc_id']
520
+ MU.log "Setting peering connection from VPC #{@config['name']} (#{@cloud_id}) to #{peer_id} in account #{peer['account']}", MU::INFO, details: peer
521
+ resp = MU::Cloud::AWS.ec2(@config['region']).create_vpc_peering_connection(
522
+ vpc_id: @cloud_id,
523
+ peer_vpc_id: peer_id,
524
+ peer_owner_id: peer['account']
525
+ )
526
+ end
527
+ rescue Aws::EC2::Errors::VpcPeeringConnectionAlreadyExists => e
528
+ MU.log "Attempt to create duplicate peering connection to #{peer_id} from VPC #{@config['name']}", MU::WARN
529
+ end
530
+ peering_name = @deploy.getResourceName(@config['name']+"-PEER-"+peer_id)
531
+
532
+ peering_id = resp.vpc_peering_connection.vpc_peering_connection_id
533
+ MU::MommaCat.createStandardTags(peering_id, region: @config['region'])
534
+ MU::MommaCat.createTag(peering_id, "Name", peering_name, region: @config['region'])
535
+
536
+ if @config['optional_tags']
537
+ MU::MommaCat.listOptionalTags.each { |key, value|
538
+ MU::MommaCat.createTag(peering_id, key, value, region: @config['region'])
539
+ }
540
+ end
541
+
542
+ if @config['tags']
543
+ @config['tags'].each { |tag|
544
+ MU::MommaCat.createTag(peering_id, tag['key'], tag['value'], region: @config['region'])
545
+ }
546
+ end
547
+
548
+ # Create routes to our new friend.
549
+ MU::Cloud::AWS::VPC.listAllSubnetRouteTables(@cloud_id, region: @config['region']).each { |rtb_id|
550
+ my_route_config = {
551
+ :route_table_id => rtb_id,
552
+ :destination_cidr_block => peer_obj.cloud_desc.cidr_block,
553
+ :vpc_peering_connection_id => peering_id
554
+ }
555
+ begin
556
+ resp = MU::Cloud::AWS.ec2(@config['region']).create_route(my_route_config)
557
+ rescue Aws::EC2::Errors::RouteAlreadyExists => e
558
+ rtbdesc = MU::Cloud::AWS.ec2(@config['region']).describe_route_tables(
559
+ route_table_ids: [rtb_id]
560
+ ).route_tables.first
561
+ rtbdesc.routes.each { |r|
562
+ if r.destination_cidr_block == peer_obj.cloud_desc.cidr_block
563
+ if r.vpc_peering_connection_id != peering_id
564
+ MU.log "Attempt to create duplicate route to #{peer_obj.cloud_desc.cidr_block} from VPC #{@config['name']}", MU::ERR, details: r
565
+ raise MuError, "Can't create route via #{peering_id}, a route to #{peer_obj.cloud_desc.cidr_block} already exists"
566
+ else
567
+ break # this is fine, the route simply already exists
568
+ end
569
+ end
570
+ }
571
+ end
572
+ }
573
+
574
+ begin
575
+ cnxn = MU::Cloud::AWS.ec2(@config['region']).describe_vpc_peering_connections(
576
+ vpc_peering_connection_ids: [peering_id]
577
+ ).vpc_peering_connections.first
578
+
579
+ if cnxn.status.code == "pending-acceptance"
580
+ if ((!peer_obj.nil? and !peer_obj.deploydata.nil? and peer_obj.deploydata['auto_accept_peers']) or $MU_CFG['allow_invade_foreign_vpcs'])
581
+ MU.log "Auto-accepting peering connection from VPC #{@config['name']} (#{@cloud_id}) to #{peer_id}", MU::NOTICE
582
+ begin
583
+ MU::Cloud::AWS.ec2(@config['region']).accept_vpc_peering_connection(
584
+ vpc_peering_connection_id: peering_id
585
+ )
586
+ rescue Aws::EC2::Errors::VpcPeeringConnectionAlreadyExists => e
587
+ MU.log "Attempt to create duplicate peering connection to #{peer_id} from VPC #{@config['name']}", MU::WARN
588
+ end
589
+
590
+ # Create routes back from our new friend to us.
591
+ MU::Cloud::AWS::VPC.listAllSubnetRouteTables(peer_id, region: peer['vpc']['region']).each { |rtb_id|
592
+ peer_route_config = {
593
+ :route_table_id => rtb_id,
594
+ :destination_cidr_block => @config['ip_block'],
595
+ :vpc_peering_connection_id => peering_id
596
+ }
597
+ begin
598
+ resp = MU::Cloud::AWS.ec2(@config['region']).create_route(peer_route_config)
599
+ rescue Aws::EC2::Errors::RouteAlreadyExists => e
600
+ MU.log "Attempt to create duplicate route to #{@config['ip_block']} from VPC #{peer_id}", MU::WARN
601
+ end
602
+ }
603
+ else
604
+ MU.log "VPC #{peer_id} is not managed by this Mu server or is not configured to auto-accept peering requests. You must accept the peering request for '#{@config['name']}' (#{@cloud_id}) by hand.", MU::WARN, details: "In the AWS Console, go to VPC => Peering Connections and look in the Actions drop-down. You can also set 'Invade Foreign VPCs' to 'true' using mu-configure to auto-accept all peering connections within this account, regardless of whether this Mu server owns the VPCs. This setting is per-user."
605
+ end
606
+ end
607
+
608
+ if cnxn.status.code == "failed" or cnxn.status.code == "rejected" or cnxn.status.code == "expired" or cnxn.status.code == "deleted"
609
+ MU.log "VPC peering connection from VPC #{@config['name']} (#{@cloud_id}) to #{peer_id} #{cnxn.status.code}: #{cnxn.status.message}", MU::ERR
610
+ begin
611
+ MU::Cloud::AWS.ec2(@config['region']).delete_vpc_peering_connection(
612
+ vpc_peering_connection_id: peering_id
613
+ )
614
+ rescue Aws::EC2::Errors::InvalidStateTransition => e
615
+ # XXX apparently this is normal?
616
+ end
617
+ raise MuError, "VPC peering connection from VPC #{@config['name']} (#{@cloud_id}) to #{peer_id} #{cnxn.status.code}: #{cnxn.status.message}"
618
+ end
619
+ end while cnxn.status.code != "active" and !(cnxn.status.code == "pending-acceptance" and (peer_obj.nil? or peer_obj.deploydata.nil? or !peer_obj.deploydata['auto_accept_peers']))
620
+
621
+ }
622
+ end
623
+
624
+ # Add any routes that reference instances, which would've been created
625
+ # in Server objects' create phases.
626
+ if !@config['route_tables'].nil?
627
+ @config['route_tables'].each { |rtb|
628
+ route_table_id = rtb['route_table_id']
629
+
630
+ rtb['routes'].each { |route|
631
+ if !route['nat_host_id'].nil? or !route['nat_host_name'].nil?
632
+ route_config = {
633
+ :route_table_id => route_table_id,
634
+ :destination_cidr_block => route['destination_network']
635
+ }
636
+
637
+ nat_instance = findBastion(
638
+ nat_name: route["nat_host_name"],
639
+ nat_cloud_id: route["nat_host_id"]
640
+ )
641
+ if nat_instance.nil?
642
+ raise MuError, "VPC #{vpc_name} is configured to use #{route} as a route, but I can't find a matching bastion host!"
643
+ end
644
+ route_config[:instance_id] = nat_instance.cloud_id
645
+
646
+ MU.log "Creating route for #{route['destination_network']} through NAT host #{nat_instance.cloud_id}", details: route_config
647
+ resp = MU::Cloud::AWS.ec2(@config['region']).create_route(route_config)
648
+ end
649
+ }
650
+
651
+ }
652
+ end
653
+
654
+ end
655
+
656
+ # Locate an existing VPC or VPCs and return an array containing matching AWS resource descriptors for those that match.
657
+ # @param cloud_id [String]: The cloud provider's identifier for this resource.
658
+ # @param region [String]: The cloud provider region
659
+ # @param tag_key [String]: A tag key to search.
660
+ # @param tag_value [String]: The value of the tag specified by tag_key to match when searching by tag.
661
+ # @return [Array<Hash<String,OpenStruct>>]: The cloud provider's complete descriptions of matching VPCs
662
+ def self.find(cloud_id: nil, region: MU.curRegion, tag_key: "Name", tag_value: nil, flags: {})
663
+
664
+ retries = 0
665
+ map = {}
666
+ begin
667
+ sleep 5 if retries < 0
668
+
669
+ if tag_value
670
+ MU.log "Searching for VPC by tag:#{tag_key}=#{tag_value}", MU::DEBUG
671
+ resp = MU::Cloud::AWS.ec2(region).describe_vpcs(
672
+ filters: [
673
+ {name: "tag:#{tag_key}", values: [tag_value]}
674
+ ]
675
+ )
676
+ if resp.data.vpcs.nil? or resp.data.vpcs.size == 0
677
+ return nil
678
+ elsif resp.data.vpcs.size >= 1
679
+ resp.data.vpcs.each { |vpc|
680
+ map[vpc.vpc_id] = vpc
681
+ }
682
+ return map
683
+ end
684
+ end
685
+
686
+ if !cloud_id.nil?
687
+ MU.log "Searching for VPC id '#{cloud_id}' in #{region}", MU::DEBUG
688
+ begin
689
+ resp = MU::Cloud::AWS.ec2(region).describe_vpcs(vpc_ids: [cloud_id.to_s])
690
+ resp.vpcs.each { |vpc|
691
+ map[vpc.vpc_id] = vpc
692
+ }
693
+ return map
694
+ rescue Aws::EC2::Errors::InvalidVpcIDNotFound => e
695
+ end
696
+ end
697
+
698
+ retries = retries + 1
699
+ end while retries < 5
700
+
701
+ return map
702
+ end
703
+
704
+ # Return an array of MU::Cloud::AWS::VPC::Subnet objects describe the
705
+ # member subnets of this VPC.
706
+ #
707
+ # @return [Array<MU::Cloud::AWS::VPC::Subnet>]
708
+ def subnets
709
+ if @subnets.nil? or @subnets.size == 0
710
+ return loadSubnets
711
+ end
712
+ return @subnets
713
+ end
714
+
715
+ # Describe subnets associated with this VPC. We'll compose identifying
716
+ # information similar to what MU::Cloud.describe builds for first-class
717
+ # resources.
718
+ # XXX this is weaksauce. Subnets should be objects with their own methods
719
+ # that work like first-class objects. How would we enforce that?
720
+ # @return [Array<Hash>]: A list of cloud provider identifiers of subnets associated with this VPC.
721
+ def loadSubnets
722
+ if @cloud_id
723
+ resp = MU::Cloud::AWS.ec2(@config['region']).describe_subnets(
724
+ filters: [
725
+ { name: "vpc-id", values: [@cloud_id] }
726
+ ]
727
+ )
728
+ if resp.nil? or resp.subnets.nil? or resp.subnets.size == 0
729
+ MU.log "Got empty results when trying to list subnets in #{@cloud_id}", MU::WARN
730
+ return []
731
+ end
732
+ end
733
+
734
+ @subnetcachesemaphore.synchronize {
735
+ @subnets ||= []
736
+ ext_ids = @subnets.each.collect { |s| s.cloud_id }
737
+
738
+ # If we're a plain old Mu resource, load our config and deployment
739
+ # metadata. Like ya do.
740
+ if !@config.nil? and @config.has_key?("subnets")
741
+ @config['subnets'].each { |subnet|
742
+ subnet['mu_name'] = @mu_name+"-"+subnet['name'] if !subnet.has_key?("mu_name")
743
+ subnet['region'] = @config['region']
744
+ if !resp.nil? and !resp.data.nil? and !resp.data.subnets.nil?
745
+ resp.data.subnets.each { |desc|
746
+ if desc.cidr_block == subnet["ip_block"]
747
+ subnet["tags"] = MU.structToHash(desc.tags)
748
+ subnet["cloud_id"] = desc.subnet_id
749
+ break
750
+ end
751
+ }
752
+ end
753
+
754
+ if subnet["cloud_id"] and !ext_ids.include?(subnet["cloud_id"])
755
+ @subnets << MU::Cloud::AWS::VPC::Subnet.new(self, subnet)
756
+ elsif !subnet["cloud_id"]
757
+ resp.data.subnets.each { |desc|
758
+ if desc.cidr_block == subnet["ip_block"]
759
+ subnet['cloud_id'] = desc.subnet_id
760
+ @subnets << MU::Cloud::AWS::VPC::Subnet.new(self, subnet)
761
+ end
762
+ }
763
+ end
764
+
765
+ }
766
+ end
767
+
768
+ # Of course we might be loading up a dummy subnet object from a
769
+ # foreign or non-Mu-created VPC and subnet. So make something up.
770
+ if !resp.nil? and @subnets.empty?
771
+ resp.data.subnets.each { |desc|
772
+ subnet = {}
773
+ subnet["ip_block"] = desc.cidr_block
774
+ subnet["name"] = subnet["ip_block"].gsub(/[\.\/]/, "_")
775
+ subnet['mu_name'] = @mu_name+"-"+subnet['name']
776
+ subnet["tags"] = MU.structToHash(desc.tags)
777
+ subnet["cloud_id"] = desc.subnet_id
778
+ subnet['region'] = @config['region']
779
+ if !ext_ids.include?(desc.subnet_id)
780
+ @subnets << MU::Cloud::AWS::VPC::Subnet.new(self, subnet)
781
+ end
782
+ }
783
+ end
784
+ return @subnets
785
+ }
786
+ end
787
+
788
+ # Given some search criteria try locating a NAT Gaateway in this VPC.
789
+ # @param nat_cloud_id [String]: The cloud provider's identifier for this NAT.
790
+ # @param nat_filter_key [String]: A cloud provider filter to help identify the resource, used in conjunction with nat_filter_value.
791
+ # @param nat_filter_value [String]: A cloud provider filter to help identify the resource, used in conjunction with nat_filter_key.
792
+ # @param region [String]: The cloud provider region of the target instance.
793
+ def findNat(nat_cloud_id: nil, nat_filter_key: nil, nat_filter_value: nil, region: MU.curRegion)
794
+ # Discard the nat_cloud_id if it's an AWS instance ID
795
+ nat_cloud_id = nil if nat_cloud_id && nat_cloud_id.start_with?("i-")
796
+
797
+ if @gateways.nil?
798
+ @gateways =
799
+ if nat_cloud_id
800
+ MU::Cloud::AWS.ec2(region).describe_nat_gateways(nat_gateway_ids: [nat_cloud_id])
801
+ elsif nat_filter_key && nat_filter_value
802
+ MU::Cloud::AWS.ec2(region).describe_nat_gateways(
803
+ filter: [
804
+ {
805
+ name: nat_filter_key,
806
+ values: [nat_filter_value]
807
+ }
808
+ ]
809
+ ).nat_gateways
810
+ end
811
+ end
812
+
813
+ @gateways ? @gateways.first : nil
814
+ end
815
+
816
+ # Given some search criteria for a {MU::Cloud::Server}, see if we can
817
+ # locate a NAT host in this VPC.
818
+ # @param nat_name [String]: The name of the resource as defined in its 'name' Basket of Kittens field, typically used in conjunction with deploy_id.
819
+ # @param nat_cloud_id [String]: The cloud provider's identifier for this NAT.
820
+ # @param nat_tag_key [String]: A cloud provider tag to help identify the resource, used in conjunction with tag_value.
821
+ # @param nat_tag_value [String]: A cloud provider tag to help identify the resource, used in conjunction with tag_key.
822
+ # @param nat_ip [String]: An IP address associated with the NAT instance.
823
+ def findBastion(nat_name: nil, nat_cloud_id: nil, nat_tag_key: nil, nat_tag_value: nil, nat_ip: nil)
824
+ nat = nil
825
+ deploy_id = nil
826
+ nat_name = nat_name.to_s if !nat_name.nil? and nat_name.class.to_s == "MU::Config::Tail"
827
+ nat_ip = nat_ip.to_s if !nat_ip.nil? and nat_ip.class.to_s == "MU::Config::Tail"
828
+ nat_cloud_id = nat_cloud_id.to_s if !nat_cloud_id.nil? and nat_cloud_id.class.to_s == "MU::Config::Tail"
829
+ nat_tag_key = nat_tag_key.to_s if !nat_tag_key.nil? and nat_tag_key.class.to_s == "MU::Config::Tail"
830
+ nat_tag_value = nat_tag_value.to_s if !nat_tag_value.nil? and nat_tag_value.class.to_s == "MU::Config::Tail"
831
+
832
+ # If we're searching by name, assume it's part of this here deploy.
833
+ if nat_cloud_id.nil? and !@deploy.nil?
834
+ deploy_id = @deploy.deploy_id
835
+ end
836
+ found = MU::MommaCat.findStray(
837
+ @config['cloud'],
838
+ "server",
839
+ name: nat_name,
840
+ region: @config['region'],
841
+ cloud_id: nat_cloud_id,
842
+ deploy_id: deploy_id,
843
+ tag_key: nat_tag_key,
844
+ tag_value: nat_tag_value,
845
+ allow_multi: true,
846
+ dummy_ok: true,
847
+ calling_deploy: @deploy
848
+ )
849
+
850
+ return nil if found.nil? || found.empty?
851
+ if found.size > 1
852
+ found.each { |nat|
853
+ # Try some AWS-specific criteria
854
+ cloud_desc = nat.cloud_desc
855
+ if !nat_host_ip.nil? and
856
+ (cloud_desc.private_ip_address == nat_host_ip or cloud_desc.public_ip_address == nat_host_ip)
857
+ return nat
858
+ elsif cloud_desc.vpc_id == @cloud_id
859
+ # XXX Strictly speaking we could have different NATs in different
860
+ # subnets, so this can be wrong in corner cases. Why you'd
861
+ # architect something that obnoxiously, I have no idea.
862
+ return nat
863
+ end
864
+ }
865
+ elsif found.size == 1
866
+ return found.first
867
+ end
868
+ return nil
869
+ end
870
+
871
+ # Check for a subnet in this VPC matching one or more of the specified
872
+ # criteria, and return it if found.
873
+ def getSubnet(cloud_id: nil, name: nil, tag_key: nil, tag_value: nil, ip_block: nil)
874
+ if !cloud_id and !name and !tag_key and !tag_value and !ip_block
875
+ raise MuError, "getSubnet called with no non-nil arguments"
876
+ end
877
+ subnets
878
+
879
+ @subnets.each { |subnet|
880
+ if !cloud_id.nil? and !subnet.cloud_id.nil? and subnet.cloud_id.to_s == cloud_id.to_s
881
+ return subnet
882
+ elsif !name.nil? and !subnet.name.nil? and subnet.name.to_s == name.to_s
883
+ return subnet
884
+ elsif !ip_block.nil? and !subnet.ip_block.nil? and subnet.ip_block.to_s == ip_block.to_s
885
+ return subnet
886
+ end
887
+ }
888
+ return nil
889
+ end
890
+
891
+ # Get the subnets associated with an instance.
892
+ # @param instance_id [String]: The cloud identifier of the instance
893
+ # @param instance [String]: A cloud descriptor for the instance, to save us an API call if we already have it
894
+ # @param region [String]: The cloud provider region of the target instance
895
+ # @return [Array<String>]
896
+ def self.getInstanceSubnets(instance_id: nil, instance: nil, region: MU.curRegion)
897
+ return [] if instance_id.nil? and instance.nil?
898
+ my_subnets = []
899
+
900
+ if instance.nil?
901
+ begin
902
+ instance = MU::Cloud::AWS.ec2(region).describe_instances(instance_ids: [instance_id]).reservations.first.instances.first
903
+ rescue NoMethodError, Aws::EC2::Errors::InvalidInstanceIDNotFound => e
904
+ MU.log "Failed to identify instance #{instance_id} in MU::Cloud::AWS::VPC.getInstanceSubnets", MU::WARN
905
+ return []
906
+ end
907
+ end
908
+ my_subnets << instance.subnet_id if !instance.subnet_id.nil?
909
+ if !instance.network_interfaces.nil?
910
+ instance.network_interfaces.each { |iface|
911
+ my_subnets << iface.subnet_id if !iface.subnet_id.nil?
912
+ }
913
+ end
914
+ return my_subnets.uniq.sort
915
+ end
916
+
917
+ @route_cache = {}
918
+ @rtb_cache = {}
919
+ @rtb_cache_semaphore = Mutex.new
920
+ # Check whether we (the Mu Master) have a direct route to a particular
921
+ # subnet. Useful for skipping hops through bastion hosts to get directly
922
+ # at child nodes in peered VPCs and the like.
923
+ # @param target_instance [OpenStruct]: The cloud descriptor of the instance to check.
924
+ # @param region [String]: The cloud provider region of the target subnet.
925
+ # @return [Boolean]
926
+ def self.haveRouteToInstance?(target_instance, region: MU.curRegion)
927
+ return false if target_instance.nil?
928
+ return false if MU.myCloud != "AWS"
929
+ instance_id = target_instance.instance_id
930
+ # XXX check if I'm even in AWS before all this bullshit
931
+ target_vpc_id = target_instance.vpc_id
932
+ my_vpc_id = MU.myCloudDescriptor.vpc_id
933
+ if (target_vpc_id && !target_vpc_id.empty?) && (my_vpc_id && !my_vpc_id.empty?)
934
+ # If the master and the node are in the same vpc then more likely than not there is a route...
935
+ if target_vpc_id == my_vpc_id
936
+ MU.log "I share a VPC with #{instance_id}, I can route to it directly", MU::DEBUG
937
+ @route_cache[instance_id] = true
938
+ return true
939
+ end
940
+ end
941
+
942
+ return @route_cache[instance_id] if @route_cache.has_key?(instance_id) && @route_cache[instance_id]
943
+ my_subnets = MU::Cloud::AWS::VPC.getInstanceSubnets(instance: MU.myCloudDescriptor)
944
+ target_subnets = MU::Cloud::AWS::VPC.getInstanceSubnets(instance: target_instance, region: region)
945
+ # XXX make sure accounts for being in different regions
946
+
947
+ resp = nil
948
+ my_subnets_key = my_subnets.join(",")
949
+ target_subnets_key = target_subnets.join(",")
950
+ MU::Cloud::AWS::VPC.update_route_tables_cache(my_subnets_key, region: MU.myRegion)
951
+ MU::Cloud::AWS::VPC.update_route_tables_cache(target_subnets_key, region: region)
952
+
953
+ if MU::Cloud::AWS::VPC.have_route_peered_vpc?(my_subnets_key, target_subnets_key, instance_id)
954
+ return true
955
+ else
956
+ # The cache can be out of date at times, check again without it
957
+ MU::Cloud::AWS::VPC.update_route_tables_cache(my_subnets_key, use_cache: false, region: MU.myRegion)
958
+ MU::Cloud::AWS::VPC.update_route_tables_cache(target_subnets_key, use_cache: false, region: region)
959
+
960
+ return MU::Cloud::AWS::VPC.have_route_peered_vpc?(my_subnets_key, target_subnets_key, instance_id)
961
+ end
962
+
963
+ @route_cache[instance_id] = false
964
+ return false
965
+ end
966
+
967
+ # updates the route table cache (@rtb_cache).
968
+ # @param subnet_key [String]: The subnet/subnets route tables will be extracted from.
969
+ # @param use_cache [Boolean]: If to use the existing cache and add records to cache only if missing, or to also replace exising records in cache.
970
+ # @param region [String]: The cloud provider region of the target subnet.
971
+ def self.update_route_tables_cache(subnet_key, use_cache: true, region: MU.curRegion)
972
+ @rtb_cache_semaphore.synchronize {
973
+ update =
974
+ if !use_cache
975
+ true
976
+ elsif use_cache && !@rtb_cache.has_key?(subnet_key)
977
+ true
978
+ else
979
+ false
980
+ end
981
+
982
+ if update
983
+ route_tables = MU::Cloud::AWS::VPC.get_route_tables(subnet_ids: subnet_key.split(","), region: region)
984
+
985
+ if route_tables.empty? && !subnet_key.empty?
986
+ vpc_id = MU::Cloud::AWS.ec2(region).describe_subnets(subnet_ids: subnet_key.split(",")).subnets.first.vpc_id
987
+ MU.log "No route table associations found for #{subnet_key}, falling back to the default table for #{vpc_id}", MU::NOTICE
988
+ route_tables = MU::Cloud::AWS::VPC.get_route_tables(vpc_ids: [vpc_id], region: region)
989
+ end
990
+
991
+ @rtb_cache[subnet_key] = route_tables
992
+ end
993
+ }
994
+ end
995
+
996
+ # Checks if the MU master has a route to a subnet in a peered VPC. Can be used on any subnets
997
+ # @param source_subnets_key [String]: The subnet/subnets on one side of the peered VPC.
998
+ # @param target_subnets_key [String]: The subnet/subnets on the other side of the peered VPC.
999
+ # @param instance_id [String]: The instance ID in the target subnet/subnets.
1000
+ # @return [Boolean]
1001
+ def self.have_route_peered_vpc?(source_subnets_key, target_subnets_key, instance_id)
1002
+ my_routes = []
1003
+ vpc_peer_mapping = {}
1004
+
1005
+ @rtb_cache[source_subnets_key].each { |route_table|
1006
+ route_table.routes.each { |route|
1007
+ if route.destination_cidr_block != "0.0.0.0/0" and route.state == "active" and !route.destination_cidr_block.nil?
1008
+ my_routes << NetAddr::IPv4Net.parse(route.destination_cidr_block)
1009
+ if !route.vpc_peering_connection_id.nil?
1010
+ vpc_peer_mapping[route.vpc_peering_connection_id] = route.destination_cidr_block
1011
+ end
1012
+ end
1013
+ }
1014
+ }
1015
+ my_routes.uniq!
1016
+
1017
+ target_routes = []
1018
+ @rtb_cache[target_subnets_key].each { |route_table|
1019
+ route_table.routes.each { |route|
1020
+ next if route.destination_cidr_block == "0.0.0.0/0" or route.state != "active" or route.destination_cidr_block.nil?
1021
+ cidr = NetAddr::IPv4Net.parse(route.destination_cidr_block)
1022
+ shared_ip_space = false
1023
+ my_routes.each { |my_cidr|
1024
+ if my_cidr.contains(NetAddr::IPv4Net.parse(route.destination_cidr_block).nth(2)) or my_cidr.cmp(cidr)
1025
+ shared_ip_space = true
1026
+ break
1027
+ end
1028
+ }
1029
+
1030
+ if shared_ip_space && !route.vpc_peering_connection_id.nil? && vpc_peer_mapping.has_key?(route.vpc_peering_connection_id)
1031
+ MU.log "I share a VPC peering connection (#{route.vpc_peering_connection_id}) with #{instance_id} for #{route.destination_cidr_block}, I can route to it directly", MU::DEBUG
1032
+ @route_cache[instance_id] = true
1033
+ return true
1034
+ end
1035
+ }
1036
+ }
1037
+
1038
+ return false
1039
+ end
1040
+
1041
+ # Retrieves the route tables of used by subnets
1042
+ # @param subnet_ids [Array]: The cloud identifier of the subnets to retrieve the route tables for.
1043
+ # @param vpc_ids [Array]: The cloud identifier of the VPCs to retrieve route tables for.
1044
+ # @param region [String]: The cloud provider region of the target subnet.
1045
+ # @return [Array<OpenStruct>]: The cloud provider's complete descriptions of the route tables
1046
+ def self.get_route_tables(subnet_ids: [], vpc_ids: [], region: MU.curRegion)
1047
+ resp = []
1048
+ if !subnet_ids.empty?
1049
+ resp = MU::Cloud::AWS.ec2(region).describe_route_tables(
1050
+ filters: [
1051
+ {
1052
+ name: "association.subnet-id",
1053
+ values: subnet_ids
1054
+ }
1055
+ ]
1056
+ ).route_tables
1057
+ elsif !vpc_ids.empty?
1058
+ resp = MU::Cloud::AWS.ec2(region).describe_route_tables(
1059
+ filters: [
1060
+ {
1061
+ name: "vpc-id",
1062
+ values: vpc_ids
1063
+ },
1064
+ {
1065
+ name: "association.main",
1066
+ values: ["true"]
1067
+ },
1068
+ ]
1069
+ ).route_tables
1070
+ else
1071
+ resp = MU::Cloud::AWS.ec2(region).describe_route_tables.route_tables
1072
+ end
1073
+
1074
+ return resp
1075
+ end
1076
+
1077
+ # Remove all VPC resources associated with the currently loaded deployment.
1078
+ # @param noop [Boolean]: If true, will only print what would be done
1079
+ # @param ignoremaster [Boolean]: If true, will remove resources not flagged as originating from this Mu server
1080
+ # @param region [String]: The cloud provider region
1081
+ # @return [void]
1082
+ def self.cleanup(noop: false, ignoremaster: false, region: MU.curRegion, flags: {})
1083
+
1084
+ tagfilters = [
1085
+ {name: "tag:MU-ID", values: [MU.deploy_id]}
1086
+ ]
1087
+ if !ignoremaster
1088
+ tagfilters << {name: "tag:MU-MASTER-IP", values: [MU.mu_public_ip]}
1089
+ end
1090
+
1091
+ vpcs = []
1092
+ retries = 0
1093
+ begin
1094
+ resp = MU::Cloud::AWS.ec2(region).describe_vpcs(filters: tagfilters).vpcs
1095
+ vpcs = resp if !resp.empty?
1096
+ rescue Aws::EC2::Errors::InvalidVpcIDNotFound => e
1097
+ if retries < 5
1098
+ sleep 5
1099
+ retries += 1
1100
+ retry
1101
+ end
1102
+ end
1103
+
1104
+ if !vpcs.empty?
1105
+ gwthreads = []
1106
+ vpcs.each { |vpc|
1107
+ # NAT gateways don't have any tags, and we can't assign them a name. Lets find them based on a VPC ID
1108
+ gwthreads << Thread.new {
1109
+ purge_nat_gateways(noop, vpc_id: vpc.vpc_id, region: region)
1110
+ purge_endpoints(noop, vpc_id: vpc.vpc_id, region: region)
1111
+ }
1112
+ }
1113
+ gwthreads.each { |t|
1114
+ t.join
1115
+ }
1116
+ end
1117
+
1118
+ purge_gateways(noop, tagfilters, region: region)
1119
+ purge_routetables(noop, tagfilters, region: region)
1120
+ purge_interfaces(noop, tagfilters, region: region)
1121
+ purge_subnets(noop, tagfilters, region: region)
1122
+ purge_vpcs(noop, tagfilters, region: region)
1123
+ purge_dhcpopts(noop, tagfilters, region: region)
1124
+
1125
+ unless noop
1126
+ MU::Cloud::AWS.iam.list_roles.roles.each{ |role|
1127
+ match_string = "#{MU.deploy_id}.*TRAFFIC-LOG"
1128
+ }
1129
+ end
1130
+ end
1131
+
1132
+ # Cloud-specific configuration properties.
1133
+ # @param config [MU::Config]: The calling MU::Config object
1134
+ # @return [Array<Array,Hash>]: List of required fields, and json-schema Hash of cloud-specific configuration parameters for this resource
1135
+ def self.schema(config)
1136
+ toplevel_required = []
1137
+ # Flow Logs can be declared at the VPC level or the subnet level
1138
+ flowlogs = {
1139
+ "traffic_type_to_log" => {
1140
+ "type" => "string",
1141
+ "description" => "The class of traffic to log - accepted traffic, rejected traffic or all traffic.",
1142
+ "enum" => ["accept", "reject", "all"],
1143
+ "default" => "all"
1144
+ },
1145
+ "log_group_name" => {
1146
+ "type" => "string",
1147
+ "description" => "An existing CloudWachLogs log group the traffic will be logged to. If not provided, a new one will be created"
1148
+ },
1149
+ "enable_traffic_logging" => {
1150
+ "type" => "boolean",
1151
+ "description" => "If traffic logging is enabled or disabled. Will be enabled on all subnets and network interfaces if set to true on a VPC",
1152
+ "default" => false
1153
+ }
1154
+ }
1155
+
1156
+ schema = {
1157
+ "subnets" => {
1158
+ "items" => {
1159
+ "properties" => flowlogs
1160
+ }
1161
+ }
1162
+ }
1163
+ schema.merge!(flowlogs)
1164
+ [toplevel_required, schema]
1165
+ end
1166
+
1167
+ # Cloud-specific pre-processing of {MU::Config::BasketofKittens::vpcs}, bare and unvalidated.
1168
+ # @param vpc [Hash]: The resource to process and validate
1169
+ # @param configurator [MU::Config]: The overall deployment config of which this resource is a member
1170
+ # @return [Boolean]: True if validation succeeded, False otherwise
1171
+ def self.validateConfig(vpc, configurator)
1172
+ ok = true
1173
+
1174
+ if (!vpc['route_tables'] or vpc['route_tables'].size == 0) and vpc['create_standard_subnets']
1175
+ vpc['route_tables'] = [
1176
+ {
1177
+ "name" => "internet",
1178
+ "routes" => [ { "destination_network" => "0.0.0.0/0", "gateway" => "#INTERNET" } ]
1179
+ },
1180
+ {
1181
+ "name" => "private",
1182
+ "routes" => [ { "destination_network" => "0.0.0.0/0", "gateway" => "#NAT" } ]
1183
+ }
1184
+ ]
1185
+ end
1186
+
1187
+ if vpc["enable_traffic_logging"]
1188
+ logdesc = {
1189
+ "name" => vpc['name']+"loggroup",
1190
+ }
1191
+ logdesc["tags"] = vpc["tags"] if !vpc["tags"].nil?
1192
+ # logdesc["optional_tags"] = vpc["optional_tags"] if !vpc["optional_tags"].nil?
1193
+ configurator.insertKitten(logdesc, "logs")
1194
+ vpc['dependencies'] ||= []
1195
+ vpc['dependencies'] << {
1196
+ "type" => "log",
1197
+ "name" => vpc['name']+"loggroup"
1198
+ }
1199
+
1200
+ roledesc = {
1201
+ "name" => vpc['name']+"logrole",
1202
+ "can_assume" => [
1203
+ {
1204
+ "entity_id" => "vpc-flow-logs.amazonaws.com",
1205
+ "entity_type" => "service"
1206
+ }
1207
+ ],
1208
+ "policies" => [
1209
+ {
1210
+ "name" => "FlowLogPerms",
1211
+ "permissions" => [
1212
+ "logs:CreateLogGroup",
1213
+ "logs:CreateLogStream",
1214
+ "logs:DescribeLogGroups",
1215
+ "logs:DescribeLogStreams",
1216
+ "logs:PutLogEvents"
1217
+ ],
1218
+ "targets" => [
1219
+ {
1220
+ "type" => "log",
1221
+ "identifier" => vpc['name']+"loggroup"
1222
+ }
1223
+ ]
1224
+ }
1225
+ ],
1226
+ "dependencies" => [
1227
+ {
1228
+ "type" => "log",
1229
+ "name" => vpc['name']+"loggroup"
1230
+ }
1231
+ ]
1232
+ }
1233
+ roledesc["tags"] = vpc["tags"] if !vpc["tags"].nil?
1234
+ roledesc["optional_tags"] = vpc["optional_tags"] if !vpc["optional_tags"].nil?
1235
+ configurator.insertKitten(roledesc, "roles")
1236
+ vpc['dependencies'] ||= []
1237
+ vpc['dependencies'] << {
1238
+ "type" => "role",
1239
+ "name" => vpc['name']+"logrole"
1240
+ }
1241
+ end
1242
+
1243
+ subnet_routes = Hash.new
1244
+ public_routes = Array.new
1245
+
1246
+ if vpc['subnets']
1247
+ vpc['subnets'].each { |subnet|
1248
+ subnet_routes[subnet['route_table']] = Array.new if subnet_routes[subnet['route_table']].nil?
1249
+ subnet_routes[subnet['route_table']] << subnet['name']
1250
+ }
1251
+ end
1252
+ if vpc['endpoint_policy'] && !vpc['endpoint_policy'].empty?
1253
+ if !vpc['endpoint']
1254
+ MU.log "'endpoint_policy' is declared however endpoint is not set", MU::ERR
1255
+ ok = false
1256
+ end
1257
+
1258
+ attributes = %w{Effect Action Resource Principal Sid}
1259
+ vpc['endpoint_policy'].each { |rule|
1260
+ rule.keys.each { |key|
1261
+ if !attributes.include?(key)
1262
+ MU.log "'Attribute #{key} can't be used in 'endpoint_policy'", MU::ERR
1263
+ ok = false
1264
+ end
1265
+ }
1266
+ }
1267
+ end
1268
+
1269
+ nat_gateway_route_tables = []
1270
+ nat_gateway_added = false
1271
+ public_rtbs = []
1272
+ private_rtbs = []
1273
+ nat_routes = {}
1274
+ vpc['route_tables'].each { |table|
1275
+ routes = []
1276
+ table['routes'].each { |route|
1277
+ if routes.include?(route['destination_network'])
1278
+ MU.log "Duplicate routes to #{route['destination_network']} in route table #{table['name']}", MU::ERR
1279
+ ok = false
1280
+ else
1281
+ routes << route['destination_network']
1282
+ end
1283
+
1284
+ if (route['nat_host_name'] or route['nat_host_id'])
1285
+ private_rtbs << table['name']
1286
+ route.delete("gateway") if route['gateway'] == '#INTERNET'
1287
+ end
1288
+ if !route['nat_host_name'].nil? and configurator.haveLitterMate?(route['nat_host_name'], "server") and !subnet_routes.nil? and !subnet_routes.empty?
1289
+ subnet_routes[table['name']].each { |subnet|
1290
+ nat_routes[subnet] = route['nat_host_name']
1291
+ }
1292
+ vpc['dependencies'] << {
1293
+ "type" => "server",
1294
+ "name" => route['nat_host_name']
1295
+ }
1296
+ elsif route['gateway'] == '#NAT'
1297
+ private_rtbs << table['name']
1298
+ elsif route['gateway'] == '#INTERNET'
1299
+ public_rtbs << table['name']
1300
+ end
1301
+ next if !vpc['subnets']
1302
+
1303
+ vpc['subnets'].each { |subnet|
1304
+ if route['gateway'] == '#INTERNET'
1305
+ if table['name'] == subnet['route_table']
1306
+ subnet['is_public'] = true
1307
+ if vpc['create_nat_gateway']
1308
+ if vpc['nat_gateway_multi_az']
1309
+ subnet['create_nat_gateway'] = true
1310
+ else
1311
+ if nat_gateway_added
1312
+ subnet['create_nat_gateway'] = false
1313
+ else
1314
+ subnet['create_nat_gateway'] = true
1315
+ nat_gateway_added = true
1316
+ end
1317
+ end
1318
+ end
1319
+ else
1320
+ subnet['is_public'] = false
1321
+ end
1322
+ if !nat_routes[subnet['name']].nil?
1323
+ subnet['nat_host_name'] = nat_routes[subnet['name']]
1324
+ end
1325
+ elsif route['gateway'] == '#NAT'
1326
+ if table['name'] == subnet['route_table']
1327
+ if route['nat_host_name'] or route['nat_host_id']
1328
+ MU.log "You can either use a NAT gateway or a NAT server, not both.", MU::ERR
1329
+ ok = false
1330
+ end
1331
+
1332
+ subnet['is_public'] = false
1333
+ nat_gateway_route_tables << table
1334
+ end
1335
+ end
1336
+ }
1337
+ }
1338
+ }
1339
+
1340
+ if (!vpc['subnets'] or vpc['subnets'].empty?) and vpc['create_standard_subnets']
1341
+ if vpc['availability_zones'].nil? or vpc['availability_zones'].empty?
1342
+ vpc['availability_zones'] = MU::Cloud::AWS.listAZs(vpc['region'])
1343
+ else
1344
+ # turn into a hash so we can use list parameters easily
1345
+ vpc['availability_zones'] = vpc['availability_zones'].map { |val| val['zone'] }
1346
+ end
1347
+ subnets = configurator.divideNetwork(vpc['ip_block'], vpc['availability_zones'].size*vpc['route_tables'].size, 28)
1348
+
1349
+ ok = false if subnets.nil?
1350
+ vpc['subnets'] = []
1351
+ count = 0
1352
+ vpc['availability_zones'].each { |az|
1353
+ addnat = false
1354
+ if vpc['create_nat_gateway'] and (vpc['nat_gateway_multi_az'] or !nat_gateway_added) and public_rtbs.size > 0
1355
+ addnat = true
1356
+ nat_gateway_added = true
1357
+ end
1358
+ vpc['route_tables'].each { |rtb|
1359
+ vpc['subnets'] << {
1360
+ "name" => "Subnet#{count}#{rtb['name'].capitalize}",
1361
+ "availability_zone" => az,
1362
+ "ip_block" => subnets.shift,
1363
+ "route_table" => rtb['name'],
1364
+
1365
+ "map_public_ips" => (public_rtbs and public_rtbs.include?(rtb['name'])),
1366
+ "is_public" => (public_rtbs and public_rtbs.include?(rtb['name'])),
1367
+ "create_nat_gateway" => (addnat and public_rtbs and public_rtbs.include?(rtb['name']))
1368
+ }
1369
+ }
1370
+ count = count + 1
1371
+ }
1372
+ end
1373
+
1374
+ nat_gateway_route_tables.uniq!
1375
+ if nat_gateway_route_tables.size < 2 && vpc['nat_gateway_multi_az']
1376
+ MU.log "'nat_gateway_multi_az' is enabled but only one route table exists. For multi-az support create one private route table per AZ", MU::ERR
1377
+ ok = false
1378
+ end
1379
+
1380
+ if nat_gateway_route_tables.size > 0 && !vpc['create_nat_gateway']
1381
+ MU.log "There are route tables with a NAT gateway route, but create_nat_gateway is set to false. Setting to true", MU::NOTICE
1382
+ vpc['create_nat_gateway'] = true
1383
+ end
1384
+
1385
+ ok
1386
+ end
1387
+
1388
+
1389
+ private
1390
+
1391
+
1392
+ # List the route tables for each subnet in the given VPC
1393
+ def self.listAllSubnetRouteTables(vpc_id, region: MU.curRegion)
1394
+ resp = MU::Cloud::AWS.ec2(region).describe_subnets(
1395
+ filters: [
1396
+ {
1397
+ name: "vpc-id",
1398
+ values: [vpc_id]
1399
+ }
1400
+ ]
1401
+ )
1402
+
1403
+ subnets = resp.subnets.map { |subnet| subnet.subnet_id }
1404
+
1405
+ tables = MU::Cloud::AWS.ec2(region).describe_route_tables(
1406
+ filters: [
1407
+ {
1408
+ name: "vpc-id",
1409
+ values: [vpc_id]
1410
+ },
1411
+ {
1412
+ name: "association.subnet-id",
1413
+ values: subnets
1414
+ }
1415
+ ]
1416
+ )
1417
+
1418
+ if tables.nil? or tables.route_tables.size == 0
1419
+ MU.log "No route table associations found for #{subnets}, falling back to the default table for #{vpc_id}", MU::NOTICE
1420
+ tables = MU::Cloud::AWS.ec2(MU.myRegion).describe_route_tables(
1421
+ filters: [
1422
+ {name: "vpc-id", values: [vpc_id]},
1423
+ {name: "association.main", values: ["true"]},
1424
+ ]
1425
+ )
1426
+ end
1427
+
1428
+ table_ids = []
1429
+ tables.route_tables.each { |rtb|
1430
+ table_ids << rtb.route_table_id
1431
+ }
1432
+ return table_ids.uniq
1433
+ end
1434
+
1435
+ # Helper method for manufacturing route tables. Expect to be called from
1436
+ # {MU::Cloud::AWS::VPC#create} or {MU::Cloud::AWS::VPC#groom}.
1437
+ # @param rtb [Hash]: A route table description parsed through {MU::Config::BasketofKittens::vpcs::route_tables}.
1438
+ # @return [Hash]: The modified configuration that was originally passed in.
1439
+ def createRouteTable(rtb)
1440
+ vpc_id = @cloud_id
1441
+ vpc_name = @config['name']
1442
+ MU.setVar("curRegion", @config['region']) if !@config['region'].nil?
1443
+ resp = MU::Cloud::AWS.ec2.create_route_table(vpc_id: vpc_id).route_table
1444
+ route_table_id = rtb['route_table_id'] = resp.route_table_id
1445
+ sleep 5
1446
+ MU::MommaCat.createTag(route_table_id, "Name", vpc_name+"-"+rtb['name'].upcase)
1447
+
1448
+ if @config['tags']
1449
+ @config['tags'].each { |tag|
1450
+ MU::MommaCat.createTag(route_table_id, tag['key'], tag['value'])
1451
+ }
1452
+ end
1453
+
1454
+ if @config['optional_tags']
1455
+ MU::MommaCat.listOptionalTags.each { |key, value|
1456
+ MU::MommaCat.createTag(route_table_id, key, value, region: @config['region'])
1457
+ }
1458
+ end
1459
+
1460
+ MU::MommaCat.createStandardTags(route_table_id)
1461
+ rtb['routes'].each { |route|
1462
+ if route['nat_host_id'].nil? and route['nat_host_name'].nil?
1463
+ route_config = {
1464
+ :route_table_id => route_table_id,
1465
+ :destination_cidr_block => route['destination_network']
1466
+ }
1467
+ if !route['peer_id'].nil?
1468
+ route_config[:vpc_peering_connection_id] = route['peer_id']
1469
+ else
1470
+ route_config[:gateway_id] = @config['internet_gateway_id']
1471
+ end
1472
+ # XXX how do the network interfaces work with this?
1473
+ unless route['gateway'] == '#NAT'
1474
+ # Need to change the order of how things are created to create the route here
1475
+ MU.log "Creating route for #{route['destination_network']}", details: route_config
1476
+ resp = MU::Cloud::AWS.ec2.create_route(route_config)
1477
+ end
1478
+ end
1479
+ }
1480
+ return rtb
1481
+ end
1482
+
1483
+
1484
+ # Remove all network gateways associated with the currently loaded deployment.
1485
+ # @param noop [Boolean]: If true, will only print what would be done
1486
+ # @param region [String]: The cloud provider region
1487
+ # @return [void]
1488
+ def self.purge_gateways(noop = false, tagfilters = [{name: "tag:MU-ID", values: [MU.deploy_id]}], region: MU.curRegion)
1489
+ resp = MU::Cloud::AWS.ec2(region).describe_internet_gateways(
1490
+ filters: tagfilters
1491
+ )
1492
+ gateways = resp.data.internet_gateways
1493
+
1494
+ gateways.each { |gateway|
1495
+ gateway.attachments.each { |attachment|
1496
+ MU.log "Detaching Internet Gateway #{gateway.internet_gateway_id} from #{attachment.vpc_id}"
1497
+ begin
1498
+ MU::Cloud::AWS.ec2(region).detach_internet_gateway(
1499
+ internet_gateway_id: gateway.internet_gateway_id,
1500
+ vpc_id: attachment.vpc_id
1501
+ ) if !noop
1502
+ rescue Aws::EC2::Errors::GatewayNotAttached => e
1503
+ MU.log "Gateway #{gateway.internet_gateway_id} was already detached", MU::WARN
1504
+ end
1505
+ }
1506
+ MU.log "Deleting Internet Gateway #{gateway.internet_gateway_id}"
1507
+ begin
1508
+ MU::Cloud::AWS.ec2(region).delete_internet_gateway(internet_gateway_id: gateway.internet_gateway_id) if !noop
1509
+ rescue Aws::EC2::Errors::InvalidInternetGatewayIDNotFound
1510
+ MU.log "Gateway #{gateway.internet_gateway_id} was already destroyed by the time I got to it", MU::WARN
1511
+ end
1512
+ }
1513
+ return nil
1514
+ end
1515
+
1516
+ # Remove all NAT gateways associated with the VPC of the currently loaded deployment.
1517
+ # @param noop [Boolean]: If true, will only print what would be done
1518
+ # @param vpc_id [String]: The cloud provider's unique VPC identifier
1519
+ # @param region [String]: The cloud provider region
1520
+ # @return [void]
1521
+ def self.purge_nat_gateways(noop = false, vpc_id: nil, region: MU.curRegion)
1522
+ gateways = MU::Cloud::AWS.ec2(region).describe_nat_gateways(
1523
+ filter: [
1524
+ {
1525
+ name: "vpc-id",
1526
+ values: [vpc_id],
1527
+ }
1528
+ ]
1529
+ ).nat_gateways
1530
+
1531
+ threads = []
1532
+ parent_thread_id = Thread.current.object_id
1533
+ if !gateways.empty?
1534
+ gateways.each { |gateway|
1535
+ threads << Thread.new {
1536
+ MU.dupGlobals(parent_thread_id)
1537
+ MU.log "Deleting NAT Gateway #{gateway.nat_gateway_id}"
1538
+ if !noop
1539
+ begin
1540
+ MU::Cloud::AWS.ec2(region).delete_nat_gateway(nat_gateway_id: gateway.nat_gateway_id)
1541
+ resp = MU::Cloud::AWS.ec2(region).describe_nat_gateways(nat_gateway_ids: [gateway.nat_gateway_id]).nat_gateways.first
1542
+
1543
+ attempts = 0
1544
+ while resp.state != "deleted" and resp.state != "failed"
1545
+ MU.log "Waiting for nat gateway #{gateway.nat_gateway_id} to delete" if attempts % 2 == 0
1546
+ sleep 30
1547
+ begin
1548
+ resp = MU::Cloud::AWS.ec2(region).describe_nat_gateways(nat_gateway_ids: [gateway.nat_gateway_id]).nat_gateways.first
1549
+ rescue Aws::EmptyStructure, NoMethodError
1550
+ sleep 5
1551
+ retry
1552
+ rescue Aws::EC2::Errors::NatGatewayNotFound
1553
+ MU.log "NAT gateway #{gateway.nat_gateway_id} already deleted", MU::NOTICE
1554
+ end
1555
+ MU.log "Timed out while waiting for NAT Gateway to delete #{gateway.nat_gateway_id}: #{resp}", MU::WARN if attempts > 50
1556
+ attempts += 1
1557
+ end
1558
+ rescue Aws::EC2::Errors::NatGatewayMalformed
1559
+ MU.log "NAT Gateway #{gateway.nat_gateway_id} was already deleted", MU::NOTICE
1560
+ end
1561
+ end
1562
+ }
1563
+ }
1564
+ end
1565
+
1566
+ threads.each { |t|
1567
+ t.join
1568
+ }
1569
+
1570
+ return nil
1571
+ end
1572
+
1573
+ # Remove all VPC endpoints associated with the VPC of the currently loaded deployment.
1574
+ # @param noop [Boolean]: If true, will only print what would be done
1575
+ # @param vpc_id [String]: The cloud provider's unique VPC identifier
1576
+ # @param region [String]: The cloud provider region
1577
+ # @return [void]
1578
+ def self.purge_endpoints(noop = false, vpc_id: nil, region: MU.curRegion)
1579
+ vpc_endpoints = MU::Cloud::AWS.ec2(region).describe_vpc_endpoints(
1580
+ filters: [
1581
+ {
1582
+ name:"vpc-id",
1583
+ values: [vpc_id],
1584
+ }
1585
+ ]
1586
+ ).vpc_endpoints
1587
+
1588
+ threads = []
1589
+ parent_thread_id = Thread.current.object_id
1590
+ if !vpc_endpoints.empty?
1591
+ vpc_endpoints.each { |endpoint|
1592
+ threads << Thread.new {
1593
+ MU.dupGlobals(parent_thread_id)
1594
+ MU.log "Deleting VPC endpoint #{endpoint.vpc_endpoint_id}"
1595
+ if !noop
1596
+ begin
1597
+ MU::Cloud::AWS.ec2(region).delete_vpc_endpoints(vpc_endpoint_ids: [endpoint.vpc_endpoint_id])
1598
+ resp = MU::Cloud::AWS.ec2(region).describe_vpc_endpoints(vpc_endpoint_ids: [endpoint.vpc_endpoint_id]).vpc_endpoints.first
1599
+
1600
+ attempts = 0
1601
+ while resp.state != "deleted"
1602
+ MU.log "Waiting for VPC endpoint #{endpoint.vpc_endpoint_id} to delete" if attempts % 5 == 0
1603
+ sleep 30
1604
+ begin
1605
+ resp = MU::Cloud::AWS.ec2(region).describe_vpc_endpoints(vpc_endpoint_ids: [endpoint.vpc_endpoint_id]).vpc_endpoints.first
1606
+ rescue Aws::EmptyStructure, NoMethodError
1607
+ sleep 5
1608
+ retry
1609
+ rescue Aws::EC2::Errors::InvalidVpcEndpointIdNotFound
1610
+ MU.log "VPC endpoint #{endpoint.vpc_endpoint_id} already deleted", MU::NOTICE
1611
+ end
1612
+ MU.log "Timed out while waiting for VPC endpoint to delete #{endpoint.vpc_endpoint_id}: #{resp}", MU::WARN if attempts > 50
1613
+ attempts += 1
1614
+ end
1615
+ rescue Aws::EC2::Errors::VpcEndpointIdMalformed
1616
+ MU.log "VPC endpoint #{endpoint.vpc_endpoint_id} was already deleted", MU::NOTICE
1617
+ rescue Aws::EC2::Errors::InvalidVpcEndpointIdNotFound
1618
+ MU.log "VPC endpoint #{endpoint.vpc_endpoint_id} already deleted", MU::NOTICE
1619
+ end
1620
+ end
1621
+ }
1622
+ }
1623
+ end
1624
+
1625
+ threads.each { |t|
1626
+ t.join
1627
+ }
1628
+
1629
+ return nil
1630
+ end
1631
+
1632
+ # Remove all route tables associated with the currently loaded deployment.
1633
+ # @param noop [Boolean]: If true, will only print what would be done
1634
+ # @param tagfilters [Array<Hash>]: EC2 tags to filter against when search for resources to purge
1635
+ # @param region [String]: The cloud provider region
1636
+ # @return [void]
1637
+ def self.purge_routetables(noop = false, tagfilters = [{name: "tag:MU-ID", values: [MU.deploy_id]}], region: MU.curRegion)
1638
+ resp = MU::Cloud::AWS.ec2(region).describe_route_tables(
1639
+ filters: tagfilters
1640
+ )
1641
+ route_tables = resp.data.route_tables
1642
+
1643
+ return if route_tables.nil? or route_tables.size == 0
1644
+
1645
+ route_tables.each { |table|
1646
+ table.routes.each { |route|
1647
+ if !route.network_interface_id.nil?
1648
+ MU.log "Deleting Network Interface #{route.network_interface_id}"
1649
+ begin
1650
+ MU::Cloud::AWS.ec2(region).delete_network_interface(network_interface_id: route.network_interface_id) if !noop
1651
+ rescue Aws::EC2::Errors::InvalidNetworkInterfaceIDNotFound => e
1652
+ MU.log "Network Interface #{route.network_interface_id} has already been deleted", MU::WARN
1653
+ end
1654
+ end
1655
+ if route.gateway_id != "local"
1656
+ MU.log "Deleting #{table.route_table_id}'s route for #{route.destination_cidr_block}"
1657
+ begin
1658
+ MU::Cloud::AWS.ec2(region).delete_route(
1659
+ route_table_id: table.route_table_id,
1660
+ destination_cidr_block: route.destination_cidr_block
1661
+ ) if !noop
1662
+ rescue Aws::EC2::Errors::InvalidRouteNotFound
1663
+ MU.log "Route #{table.route_table_id} has already been deleted", MU::WARN
1664
+ end
1665
+ end
1666
+ }
1667
+ can_delete = true
1668
+ table.associations.each { |assoc|
1669
+ begin
1670
+ MU::Cloud::AWS.ec2(region).disassociate_route_table(association_id: assoc.route_table_association_id) if !noop
1671
+ rescue Aws::EC2::Errors::InvalidAssociationIDNotFound => e
1672
+ MU.log "Route table association #{assoc.route_table_association_id} already removed", MU::WARN
1673
+ rescue Aws::EC2::Errors::InvalidParameterValue => e
1674
+ # normal and ignorable with the default route table
1675
+ can_delete = false
1676
+ next
1677
+ end
1678
+ }
1679
+ next if !can_delete
1680
+ MU.log "Deleting Route Table #{table.route_table_id}"
1681
+ begin
1682
+ MU::Cloud::AWS.ec2(region).delete_route_table(route_table_id: table.route_table_id) if !noop
1683
+ rescue Aws::EC2::Errors::InvalidRouteTableIDNotFound
1684
+ MU.log "Route table #{table.route_table_id} already removed", MU::WARN
1685
+ end
1686
+ }
1687
+ return nil
1688
+ end
1689
+
1690
+
1691
+ # Remove all network interfaces associated with the currently loaded deployment.
1692
+ # @param noop [Boolean]: If true, will only print what would be done
1693
+ # @param tagfilters [Array<Hash>]: EC2 tags to filter against when search for resources to purge
1694
+ # @param region [String]: The cloud provider region
1695
+ # @return [void]
1696
+ def self.purge_interfaces(noop = false, tagfilters = [{name: "tag:MU-ID", values: [MU.deploy_id]}], region: MU.curRegion)
1697
+ resp = MU::Cloud::AWS.ec2(region).describe_network_interfaces(
1698
+ filters: tagfilters
1699
+ )
1700
+ ifaces = resp.data.network_interfaces
1701
+
1702
+ return if ifaces.nil? or ifaces.size == 0
1703
+
1704
+ ifaces.each { |iface|
1705
+ MU.log "Deleting Network Interface #{iface.network_interface_id}"
1706
+ MU::Cloud::AWS.ec2(region).delete_network_interface(network_interface_id: iface.network_interface_id)
1707
+ }
1708
+ end
1709
+
1710
+ # Remove all subnets associated with the currently loaded deployment.
1711
+ # @param noop [Boolean]: If true, will only print what would be done
1712
+ # @param tagfilters [Array<Hash>]: EC2 tags to filter against when search for resources to purge
1713
+ # @param region [String]: The cloud provider region
1714
+ # @return [void]
1715
+ def self.purge_subnets(noop = false, tagfilters = [{name: "tag:MU-ID", values: [MU.deploy_id]}], region: MU.curRegion)
1716
+ resp = MU::Cloud::AWS.ec2(region).describe_subnets(
1717
+ filters: tagfilters
1718
+ )
1719
+ subnets = resp.data.subnets
1720
+
1721
+ return if subnets.nil? or subnets.size == 0
1722
+
1723
+ retries = 0
1724
+ subnets.each { |subnet|
1725
+ begin
1726
+ if subnet.state != "available"
1727
+ MU.log "Waiting for #{subnet.subnet_id} to be in a removable state...", MU::NOTICE
1728
+ sleep 30
1729
+ else
1730
+ MU.log "Deleting Subnet #{subnet.subnet_id}"
1731
+ MU::Cloud::AWS.ec2(region).delete_subnet(subnet_id: subnet.subnet_id) if !noop
1732
+ end
1733
+ rescue Aws::EC2::Errors::DependencyViolation => e
1734
+ if retries < 7
1735
+ MU.log "#{e.inspect}, retrying in 10s", MU::WARN
1736
+ sleep 10
1737
+ retries = retries + 1
1738
+ retry
1739
+ else
1740
+ raise e
1741
+ end
1742
+ rescue Aws::EC2::Errors::InvalidSubnetIDNotFound
1743
+ MU.log "Subnet #{subnet.subnet_id} disappeared before I could remove it", MU::WARN
1744
+ next
1745
+ end while subnet.state != "available"
1746
+ }
1747
+ end
1748
+
1749
+ # Remove all DHCP options sets associated with the currently loaded
1750
+ # deployment.
1751
+ # @param noop [Boolean]: If true, will only print what would be done
1752
+ # @param tagfilters [Array<Hash>]: EC2 tags to filter against when search for resources to purge
1753
+ # @param region [String]: The cloud provider region
1754
+ # @return [void]
1755
+ def self.purge_dhcpopts(noop = false, tagfilters = [{name: "tag:MU-ID", values: [MU.deploy_id]}], region: MU.curRegion)
1756
+ resp = MU::Cloud::AWS.ec2(region).describe_dhcp_options(
1757
+ filters: tagfilters
1758
+ )
1759
+ sets = resp.data.dhcp_options
1760
+
1761
+ return if sets.nil? or sets.size == 0
1762
+
1763
+ sets.each { |optset|
1764
+ begin
1765
+ MU.log "Deleting DHCP Option Set #{optset.dhcp_options_id}"
1766
+ MU::Cloud::AWS.ec2(region).delete_dhcp_options(dhcp_options_id: optset.dhcp_options_id)
1767
+ rescue Aws::EC2::Errors::DependencyViolation => e
1768
+ MU.log e.inspect, MU::ERR
1769
+ # rescue Aws::EC2::Errors::InvalidSubnetIDNotFound
1770
+ # MU.log "Subnet #{subnet.subnet_id} disappeared before I could remove it", MU::WARN
1771
+ # next
1772
+ end
1773
+ }
1774
+ end
1775
+
1776
+ # Remove all VPCs associated with the currently loaded deployment.
1777
+ # @param noop [Boolean]: If true, will only print what would be done
1778
+ # @param tagfilters [Array<Hash>]: EC2 tags to filter against when search for resources to purge
1779
+ # @param region [String]: The cloud provider region
1780
+ # @return [void]
1781
+ def self.purge_vpcs(noop = false, tagfilters = [{name: "tag:MU-ID", values: [MU.deploy_id]}], region: MU.curRegion)
1782
+ resp = MU::Cloud::AWS.ec2(region).describe_vpcs(
1783
+ filters: tagfilters
1784
+ )
1785
+
1786
+ vpcs = resp.data.vpcs
1787
+ return if vpcs.nil? or vpcs.size == 0
1788
+
1789
+ vpcs.each { |vpc|
1790
+ my_peer_conns = MU::Cloud::AWS.ec2(region).describe_vpc_peering_connections(
1791
+ filters: [
1792
+ {
1793
+ name: "requester-vpc-info.vpc-id",
1794
+ values: [vpc.vpc_id]
1795
+ }
1796
+ ]
1797
+ ).vpc_peering_connections
1798
+ my_peer_conns.concat(MU::Cloud::AWS.ec2(region).describe_vpc_peering_connections(
1799
+ filters: [
1800
+ {
1801
+ name: "accepter-vpc-info.vpc-id",
1802
+ values: [vpc.vpc_id]
1803
+ }
1804
+ ]
1805
+ ).vpc_peering_connections)
1806
+ my_peer_conns.each { |cnxn|
1807
+
1808
+ [cnxn.accepter_vpc_info.vpc_id, cnxn.requester_vpc_info.vpc_id].each { |peer_vpc|
1809
+ MU::Cloud::AWS::VPC.listAllSubnetRouteTables(peer_vpc, region: region).each { |rtb_id|
1810
+ resp = MU::Cloud::AWS.ec2(region).describe_route_tables(
1811
+ route_table_ids: [rtb_id]
1812
+ )
1813
+ resp.route_tables.each { |rtb|
1814
+ rtb.routes.each { |route|
1815
+ if route.vpc_peering_connection_id == cnxn.vpc_peering_connection_id
1816
+ MU.log "Removing route #{route.destination_cidr_block} from route table #{rtb_id} in VPC #{peer_vpc}"
1817
+ MU::Cloud::AWS.ec2(region).delete_route(
1818
+ route_table_id: rtb_id,
1819
+ destination_cidr_block: route.destination_cidr_block
1820
+ ) if !noop
1821
+ end
1822
+ }
1823
+ }
1824
+ }
1825
+ }
1826
+ MU.log "Deleting VPC peering connection #{cnxn.vpc_peering_connection_id}"
1827
+ begin
1828
+ MU::Cloud::AWS.ec2(region).delete_vpc_peering_connection(
1829
+ vpc_peering_connection_id: cnxn.vpc_peering_connection_id
1830
+ ) if !noop
1831
+ rescue Aws::EC2::Errors::InvalidStateTransition => e
1832
+ MU.log "VPC peering connection #{cnxn.vpc_peering_connection_id} not in removable (state #{cnxn.status.code})", MU::WARN
1833
+ end
1834
+ }
1835
+
1836
+ MU.log "Deleting VPC #{vpc.vpc_id}"
1837
+ retries = 0
1838
+ begin
1839
+ MU::Cloud::AWS.ec2(region).delete_vpc(vpc_id: vpc.vpc_id) if !noop
1840
+ rescue Aws::EC2::Errors::InvalidVpcIDNotFound
1841
+ MU.log "VPC #{vpc.vpc_id} has already been deleted", MU::WARN
1842
+ rescue Aws::EC2::Errors::DependencyViolation => e
1843
+ MU.log "Couldn't delete VPC #{vpc.vpc_id} from #{region}: #{e.inspect}", MU::ERR#, details: caller
1844
+ if retries < 5
1845
+ retries += 1
1846
+ sleep 10
1847
+ retry
1848
+ else
1849
+ next
1850
+ end
1851
+ end
1852
+
1853
+ if !MU::Cloud::AWS.isGovCloud?(region)
1854
+ mu_zone = MU::Cloud::DNSZone.find(cloud_id: "platform-mu", region: region).values.first
1855
+ if !mu_zone.nil?
1856
+ MU::Cloud::AWS::DNSZone.toggleVPCAccess(id: mu_zone.id, vpc_id: vpc.vpc_id, remove: true)
1857
+ end
1858
+ end
1859
+ }
1860
+ end
1861
+
1862
+ protected
1863
+
1864
+ # Subnets are almost a first-class resource. So let's kinda sorta treat
1865
+ # them like one. This should only be invoked on objects that already
1866
+ # exists in the cloud layer.
1867
+ class Subnet < MU::Cloud::AWS::VPC
1868
+
1869
+ attr_reader :cloud_id
1870
+ attr_reader :ip_block
1871
+ attr_reader :mu_name
1872
+ attr_reader :name
1873
+ attr_reader :az
1874
+ attr_reader :cloud_desc
1875
+
1876
+ # @param parent [MU::Cloud::AWS::VPC]: The parent VPC of this subnet.
1877
+ # @param config [Hash<String>]:
1878
+ def initialize(parent, config)
1879
+ @parent = parent
1880
+ @config = MU::Config.manxify(config)
1881
+ @cloud_id = config['cloud_id']
1882
+ @mu_name = config['mu_name']
1883
+ @name = config['name']
1884
+ @deploydata = config # This is a dummy for the sake of describe()
1885
+ resp = MU::Cloud::AWS.ec2(@config['region']).describe_subnets(subnet_ids: [@cloud_id]).subnets.first
1886
+ @az = resp.availability_zone
1887
+ @ip_block = resp.cidr_block
1888
+ @cloud_desc = resp
1889
+
1890
+ end
1891
+
1892
+ # Return the cloud identifier for the default route of this subnet.
1893
+ def defaultRoute
1894
+ resp = MU::Cloud::AWS.ec2(@config['region']).describe_route_tables(
1895
+ filters: [{name: "association.subnet-id", values: [@cloud_id]}]
1896
+ )
1897
+ if resp.route_tables.size == 0 # use default route table for the VPC
1898
+ resp = MU::Cloud::AWS.ec2(@config['region']).describe_route_tables(
1899
+ filters: [{name: "vpc-id", values: [@parent.cloud_id]}]
1900
+ )
1901
+ end
1902
+ resp.route_tables.each { |route_table|
1903
+ route_table.routes.each { |route|
1904
+ if route.destination_cidr_block =="0.0.0.0/0" and route.state != "blackhole"
1905
+ return route.instance_id if !route.instance_id.nil?
1906
+ return route.gateway_id if !route.gateway_id.nil?
1907
+ return route.vpc_peering_connection_id if !route.vpc_peering_connection_id.nil?
1908
+ return route.network_interface_id if !route.network_interface_id.nil?
1909
+ end
1910
+ }
1911
+ }
1912
+ return nil
1913
+ end
1914
+
1915
+ # Is this subnet privately-routable only, or public?
1916
+ # @return [Boolean]
1917
+ def private?
1918
+ return false if @cloud_id.nil?
1919
+ resp = MU::Cloud::AWS.ec2(@config['region']).describe_route_tables(
1920
+ filters: [{name: "association.subnet-id", values: [@cloud_id]}]
1921
+ )
1922
+ if resp.route_tables.size == 0 # use default route table for the VPC
1923
+ resp = MU::Cloud::AWS.ec2(@config['region']).describe_route_tables(
1924
+ filters: [{name: "vpc-id", values: [@parent.cloud_id]}]
1925
+ )
1926
+ end
1927
+ resp.route_tables.each { |route_table|
1928
+ route_table.routes.each { |route|
1929
+ return false if !route.gateway_id.nil? and route.gateway_id != "local" # you can have an IgW and route it to a subset of IPs instead of 0.0.0.0/0
1930
+ if route.destination_cidr_block == "0.0.0.0/0"
1931
+ return true if !route.instance_id.nil?
1932
+ return true if route.nat_gateway_id
1933
+ end
1934
+ }
1935
+ }
1936
+ return true
1937
+ end
1938
+ end
1939
+
1940
+ end #class
1941
+ end #class
1942
+ end
1943
+ end #module