├── .foodcritic ├── .gitignore ├── .kitchen.yml ├── .rspec ├── Berksfile ├── Gemfile ├── LICENSE ├── README.md ├── attributes └── default.rb ├── chefignore ├── libraries ├── ec2.rb └── matchers.rb ├── metadata.rb ├── providers ├── group.rb └── group_rule.rb ├── recipes └── default.rb ├── resources ├── group.rb └── group_rule.rb ├── spec ├── recipes │ └── default_spec.rb └── spec_helper.rb └── test ├── fixtures └── cookbooks │ └── fake │ ├── files │ ├── centos │ │ └── runmoto │ └── ubuntu │ │ └── runmoto │ ├── metadata.rb │ ├── recipes │ ├── add_test.rb │ ├── aws_mock.rb │ └── remove_test.rb │ └── templates │ └── default │ └── aws_config.erb └── integration ├── add_test └── serverspec │ ├── Gemfile │ ├── security_group_spec.rb │ └── spec_helper.rb └── remove_test └── serverspec ├── Gemfile ├── security_group_spec.rb └── spec_helper.rb /.foodcritic: -------------------------------------------------------------------------------- 1 | ~FC017 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.rbc 2 | .config 3 | coverage 4 | InstalledFiles 5 | lib/bundler/man 6 | pkg 7 | rdoc 8 | spec/reports 9 | test/tmp 10 | test/version_tmp 11 | tmp 12 | _Store 13 | *~ 14 | *# 15 | .#* 16 | \#*# 17 | .*.sw[a-z] 18 | *.un~ 19 | *.tmp 20 | *.bk 21 | *.bkup 22 | 23 | # ruby/bundler files 24 | .ruby-version 25 | .ruby-gemset 26 | .rvmrc 27 | Gemfile.lock 28 | .bundle 29 | *.gem 30 | 31 | # YARD artifacts 32 | .yardoc 33 | _yardoc 34 | doc/ 35 | .idea 36 | 37 | # chef stuff 38 | Berksfile.lock 39 | .kitchen 40 | .kitchen.local.yml 41 | vendor/ 42 | .coverage/ 43 | .zero-knife.rb 44 | Policyfile.lock.json 45 | 46 | # vagrant stuff 47 | .vagrant/ 48 | .vagrant.d/ 49 | .kitchen/ 50 | 51 | # secrets for testing this Cookbook 52 | test/fixtures/encrypted_data_bag_secret 53 | test/fixtures/data_bags/ 54 | -------------------------------------------------------------------------------- /.kitchen.yml: -------------------------------------------------------------------------------- 1 | driver: 2 | name: vagrant 3 | 4 | provisioner: 5 | name: chef_zero 6 | 7 | platforms: 8 | - name: centos-6.8 9 | - name: centos-7.2 10 | - name: ubuntu-14.04 11 | - name: ubuntu-16.04 12 | 13 | suites: 14 | - name: add_test 15 | data_bags_path: "test/fixtures/data_bags" 16 | encrypted_data_bag_secret_key_path: "test/fixtures/encrypted_data_bag_secret" 17 | run_list: 18 | - recipe[fake::aws_mock] 19 | - recipe[fake::add_test] 20 | attributes: 21 | aws_security: 22 | mocking: true 23 | encrypted_data_bag: aws_security 24 | 25 | - name: remove_test 26 | data_bags_path: "test/fixtures/data_bags" 27 | encrypted_data_bag_secret_key_path: "test/fixtures/encrypted_data_bag_secret" 28 | run_list: 29 | - recipe[fake::aws_mock] 30 | - recipe[fake::add_test] 31 | - recipe[fake::remove_test] 32 | attributes: 33 | aws_security: 34 | mocking: true 35 | encrypted_data_bag: aws_security 36 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | -f documentation -------------------------------------------------------------------------------- /Berksfile: -------------------------------------------------------------------------------- 1 | source 'https://supermarket.chef.io' 2 | 3 | metadata 4 | 5 | cookbook 'build-essential', '~> 2.0.6' 6 | cookbook 'nokogiri', '~> 0.1.1' 7 | cookbook 'libxml2', '~> 0.1.1' 8 | 9 | group :integration do 10 | cookbook 'fake', path: 'test/fixtures/cookbooks/fake' 11 | cookbook 'python' 12 | end 13 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'fog-aws', '~> 0.11' 4 | 5 | group :lint do 6 | gem 'foodcritic', '~> 7.0' 7 | gem 'cookstyle' 8 | end 9 | 10 | group :unit do 11 | gem 'berkshelf', '~> 4.3' 12 | gem 'chefspec', '~> 4.7' 13 | gem 'rspec_junit_formatter', '~> 0.1' 14 | end 15 | 16 | group :kitchen_common do 17 | gem 'test-kitchen', '~> 1.11' 18 | end 19 | 20 | group :kitchen_vagrant do 21 | gem 'kitchen-vagrant', '~> 0.20' 22 | end 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # aws_security 2 | 3 | # Description 4 | 5 | This cookbook provides libraries, resource and providers to config and manage Amazon EC2 Security Groups 6 | 7 | # Requirements 8 | 9 | ## Testing Gems 10 | 11 | - berkshelf 12 | - test-kitchen 13 | - kitchen-vagrant 14 | - chefspec 15 | - rspec_junit_formatter 16 | 17 | # Recipes 18 | 19 | ## default 20 | 21 | This recipe contains no resources as the fog-aws gem is now automatically installed by the resources as necessary. 22 | 23 | # Attributes 24 | 25 | `default['build-essential']['compile_time'] = true` This must be set to true to ensure development tools are installed before the chef_gem 'fog-aws' is installed 26 | 27 | `default['aws_security']['encrypted_data_bag'] = nil` Name of the data bag to search for your AWS credentials 28 | 29 | `default['aws_security']['aws_access_key_id'] = nil` `default['aws_security']['aws_secret_access_key'] = nil` If these are defined, they will be used by default for the LWRPs 30 | 31 | # LWRPs 32 | 33 | ## `aws_security_group` 34 | 35 | Description: Creates and destroys security groups 36 | 37 | Actions: 38 | 39 | - `create_if_missing` - Creates a new security group if it doesn't already exist (default action) 40 | - `create_and_attach` - Creates a new security group if it doesn't already exist and adds the instance to it 41 | - `remove` - Removes an existing security group 42 | - `attach` - Attaches the instance where Chef is running to an existing security group 43 | - `detach` - Detaches the instance where Chef is running from an existing security group 44 | 45 | Attribute Parameters: 46 | 47 | - `groupname` - Name attribute 48 | - `aws_access_key_id` - optional (falls back to IAM roles if not provided) 49 | - `aws_secret_access_key` - required if aws_access_key_id is specified 50 | - `description` - required 51 | - `vpcid` - optional 52 | - `region` - optional (defaults to 'us-east-1') 53 | 54 | ## Usage 55 | 56 | ``` 57 | aws_security_group 'Example' do 58 | description "Example Security Group" 59 | aws_access_key_id node['aws_security']['aws_access_key_id'] 60 | aws_secret_access_key node['aws_security']['aws_secret_access_key'] 61 | region 'us-west-2' 62 | end 63 | ``` 64 | 65 | ## `aws_security_group_rule` 66 | 67 | Description: Creates and destroys rules in an existing security group 68 | 69 | Actions: 70 | 71 | - `add` - Adds new rule to existing security group (default action) 72 | - `remove` - Removes an existing rule from a security group 73 | 74 | Attribute Parameters: 75 | 76 | - `name` - Name attribute 77 | - `aws_access_key_id` - required 78 | - `aws_secret_access_key` - required 79 | - `groupname` - optional 80 | - `description` - optional 81 | - `vpcid` - optional 82 | - `region` - optional (defaults to 'us-east-1') 83 | - `groupid` - optional 84 | - `groupname` - optional 85 | - `cidr_ip` - optional 86 | - `group` - optional 87 | - `owner` - optional 88 | - `ip_protocol` - optional, (must be one of the following [-1,udp,tcp,icmp]) 89 | - `port_range` - optional (port..port) 90 | - `from_port` - optional 91 | - `to_port` - optional 92 | 93 | ## Usage 94 | 95 | The following will create a rule in security group `Example` in region `us-west-2` to allow 192.168.1.1 access to port 80 96 | 97 | ```ruby 98 | aws_security_group_rule 'example1' do 99 | description "Example Rule 1" 100 | aws_access_key_id node['aws_security']['aws_access_key_id'] 101 | aws_secret_access_key node['aws_security']['aws_secret_access_key'] 102 | cidr_ip "192.168.1.1/32" 103 | groupname "Example" 104 | region 'us-west-2' 105 | port_range "80..80" 106 | ip_protocol 'tcp' 107 | end 108 | ``` 109 | 110 | The following will create a rule in security group `Example` in region `us-west-2` to allow a security group with the id of `sg-3b5a6ffe` to allow access to port 80 111 | 112 | ```ruby 113 | aws_security_group_rule 'exmaple2' do 114 | description "Example Rule 2" 115 | aws_access_key_id node['aws_security']['aws_access_key_id'] 116 | aws_secret_access_key node['aws_security']['aws_secret_access_key'] 117 | group "sg-3b5a6ffe" 118 | groupname "Example" 119 | region 'us-west-2' 120 | port_range "80..80" 121 | ip_protocol 'tcp' 122 | end 123 | ``` 124 | 125 | The following will create a rule in security group `Example` in region `us-east-1` to allow 10.0.0.0/24 all access 126 | 127 | ```ruby 128 | aws_security_group_rule 'example3' do 129 | description "Example Rule 3" 130 | aws_access_key_id node['aws_security']['aws_access_key_id'] 131 | aws_secret_access_key node['aws_security']['aws_secret_access_key'] 132 | cidr_ip "10.0.0.0/24" 133 | groupname "Example" 134 | ip_protocol '-1' 135 | end 136 | ``` 137 | 138 | # TODO 139 | 140 | - Egress rules 141 | - Apply security groups to instances, elbs, vpcs, etc 142 | 143 | # AWS Credentials for testing 144 | 145 | The default testing recipe will look for a encrypted data bag item aws_keys in the data bag defined by node['aws_security']['encrypted_data_bag'] with the following keys: 146 | 147 | - aws_access_key_id 148 | - aws_secret_access_key 149 | 150 | E.G. 151 | 152 | ```json 153 | { 154 | "id": "aws_keys", 155 | "aws_access_key_id": "YOUR_ACCESS_KEY", 156 | "aws_secret_access_key": "YOUR_SECRET_ACCESS_KEY" 157 | } 158 | ``` 159 | 160 | # License and Author 161 | 162 | - Author:: Greg Hellings ([greg@thesub.net](mailto:greg@thesub.net)) 163 | 164 | Copyright 2014, B7 Interactive, LLC. 165 | 166 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at 167 | 168 | ``` 169 | http://www.apache.org/licenses/LICENSE-2.0 170 | ``` 171 | 172 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 173 | -------------------------------------------------------------------------------- /attributes/default.rb: -------------------------------------------------------------------------------- 1 | default['aws_security']['aws_access_key_id'] = nil 2 | default['aws_security']['aws_secret_access_key'] = nil 3 | default['aws_security']['mocking'] = false 4 | -------------------------------------------------------------------------------- /chefignore: -------------------------------------------------------------------------------- 1 | # Put files/directories that should be ignored in this file when uploading 2 | # to a chef-server or supermarket. 3 | # Lines that start with '# ' are comments. 4 | 5 | # OS generated files # 6 | ###################### 7 | .DS_Store 8 | Icon? 9 | nohup.out 10 | ehthumbs.db 11 | Thumbs.db 12 | 13 | # SASS # 14 | ######## 15 | .sass-cache 16 | 17 | # EDITORS # 18 | ########### 19 | \#* 20 | .#* 21 | *~ 22 | *.sw[a-z] 23 | *.bak 24 | REVISION 25 | TAGS* 26 | tmtags 27 | *_flymake.* 28 | *_flymake 29 | *.tmproj 30 | .project 31 | .settings 32 | mkmf.log 33 | 34 | ## COMPILED ## 35 | ############## 36 | a.out 37 | *.o 38 | *.pyc 39 | *.so 40 | *.com 41 | *.class 42 | *.dll 43 | *.exe 44 | */rdoc/ 45 | 46 | # Testing # 47 | ########### 48 | .watchr 49 | .rspec 50 | spec/* 51 | spec/fixtures/* 52 | test/* 53 | features/* 54 | examples/* 55 | Guardfile 56 | Procfile 57 | .kitchen* 58 | .rubocop.yml 59 | spec/* 60 | Rakefile 61 | .travis.yml 62 | .foodcritic 63 | .codeclimate.yml 64 | 65 | # SCM # 66 | ####### 67 | .git 68 | */.git 69 | .gitignore 70 | .gitmodules 71 | .gitconfig 72 | .gitattributes 73 | .svn 74 | */.bzr/* 75 | */.hg/* 76 | */.svn/* 77 | 78 | # Berkshelf # 79 | ############# 80 | Berksfile 81 | Berksfile.lock 82 | cookbooks/* 83 | tmp 84 | 85 | # Cookbooks # 86 | ############# 87 | CONTRIBUTING* 88 | CHANGELOG* 89 | TESTING* 90 | MAINTAINERS.toml 91 | 92 | # Strainer # 93 | ############ 94 | Colanderfile 95 | Strainerfile 96 | .colander 97 | .strainer 98 | 99 | # Vagrant # 100 | ########### 101 | .vagrant 102 | Vagrantfile 103 | 104 | # secrets for testing this Cookbook 105 | test/fixtures/encrypted_data_bag_secret 106 | test/fixtures/data_bags/* 107 | -------------------------------------------------------------------------------- /libraries/ec2.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Author:: Greg Hellings () 3 | # 4 | # 5 | # Copyright 2014, B7 Interactive, LLC 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | 19 | module Aws 20 | module Ec2 21 | def ec2 22 | # rubocop: disable Style/ClassVars 23 | @@ec2 ||= create_aws_interface 24 | end 25 | 26 | def create_aws_interface 27 | begin 28 | require 'fog/aws' 29 | rescue LoadError 30 | chef_gem 'fog-aws' do 31 | compile_time true if Chef::Resource::ChefGem.method_defined?(:compile_time) 32 | action :install 33 | end 34 | 35 | require 'fog/aws' 36 | end 37 | options = { provider: 'AWS', region: @current_resource.region } 38 | 39 | if @current_resource.aws_access_key_id 40 | # Only pass credentials if we have them. This allows Fog to fall back 41 | # to IAM roles. 42 | options[:aws_access_key_id] = @current_resource.aws_access_key_id 43 | options[:aws_secret_access_key] = 44 | @current_resource.aws_secret_access_key 45 | end 46 | 47 | if @current_resource.mocking 48 | options[:host] = 'localhost' 49 | options[:port] = 5000 50 | options[:scheme] = 'http' 51 | end 52 | Fog::Compute.new(options) 53 | end 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /libraries/matchers.rb: -------------------------------------------------------------------------------- 1 | if defined?(ChefSpec) 2 | def create_if_missing_aws_security_group(resource_name) 3 | ChefSpec::Matchers::ResourceMatcher.new(:aws_security_group, :create_if_missing, resource_name) 4 | end 5 | 6 | def remove_aws_security_group(resource_name) 7 | ChefSpec::Matchers::ResourceMatcher.new(:aws_security_group, :remove, resource_name) 8 | end 9 | 10 | def add_aws_security_group_rule(resource_name) 11 | ChefSpec::Matchers::ResourceMatcher.new(:aws_security_group_rule, :add, resource_name) 12 | end 13 | 14 | def remove_aws_security_group_rule(resource_name) 15 | ChefSpec::Matchers::ResourceMatcher.new(:aws_security_group_rule, :remove, resource_name) 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /metadata.rb: -------------------------------------------------------------------------------- 1 | name 'aws_security' 2 | maintainer 'Greg Hellings' 3 | maintainer_email 'greg@thesub.net' 4 | license 'Apache V2' 5 | description 'Installs/Configures aws_security_group' 6 | long_description 'Installs/Configures aws_security_group' 7 | version '0.1.9' 8 | source_url 'https://github.com/SearchSpring/aws_security' 9 | issues_url 'https://github.com/SearchSpring/aws_security/issues' 10 | chef_version '>= 11' 11 | -------------------------------------------------------------------------------- /providers/group.rb: -------------------------------------------------------------------------------- 1 | include Aws::Ec2 2 | 3 | def whyrun_supported? 4 | true 5 | end 6 | 7 | use_inline_resources 8 | 9 | action :create_if_missing do 10 | if @current_resource.exists 11 | Chef::Log.info "#{@new_resource} already exists - nothing to do." 12 | else 13 | converge_by("Creating #{@new_resource} security group") do 14 | create_security_group 15 | end 16 | end 17 | end 18 | 19 | action :create_and_attach do 20 | if @current_resource.exists 21 | Chef::Log.info "#{@new_resource} already exists." 22 | else 23 | converge_by("Creating #{@new_resource} security group") do 24 | create_security_group 25 | end 26 | end 27 | add_instance_to_security_group(instance) 28 | end 29 | 30 | action :remove do 31 | if @current_resource.exists 32 | raise "#{@new_resource} cannot be removed - configuration " \ 33 | 'mismatch' unless security_group 34 | converge_by("Remvoing #{@new_resource} security group") do 35 | security_group.destroy 36 | end 37 | else 38 | Chef::Log.info "#{@new_resource} does not exist - nothing to do." 39 | end 40 | end 41 | 42 | action :attach do 43 | if @current_resource.exists 44 | add_instance_to_security_group(instance) 45 | else 46 | raise "#{@new_resource} does not exist - unable to attach." 47 | end 48 | end 49 | 50 | action :detach do 51 | if @current_resource.exists 52 | remove_instance_from_security_group(instance) 53 | else 54 | Chef::Log.info "#{@new_resource} does not exist - nothing to do." 55 | end 56 | end 57 | 58 | def load_current_resource 59 | @current_resource = 60 | Chef::Resource::AwsSecurityGroup.new(@new_resource.groupname) 61 | 62 | %w(aws_access_key_id 63 | aws_secret_access_key 64 | groupname 65 | description 66 | vpcid 67 | region).each do |attrib| 68 | @current_resource.send(attrib, @new_resource.send(attrib)) 69 | end 70 | 71 | if @new_resource.aws_access_key_id || node['aws_security']['aws_access_key_id'] 72 | @current_resource.mocking(@new_resource.mocking || 73 | node['aws_security']['mocking']) 74 | end 75 | @current_resource.exists = true if security_group 76 | end 77 | 78 | def security_group 79 | @sg ||= ec2.security_groups.all( 80 | 'group-name' => [@current_resource.groupname] 81 | ).find { |g| g.vpc_id == @current_resource.vpcid } 82 | end 83 | 84 | def create_security_group 85 | ec2.security_groups.new(attributes).save 86 | end 87 | 88 | def add_instance_to_security_group(instance) 89 | existing_groups = instance.network_interfaces.first['groupIds'] 90 | 91 | unless existing_groups.include?(security_group.group_id) 92 | ec2.modify_instance_attribute(instance.id, 'GroupId' => (existing_groups + [security_group.group_id])) 93 | end 94 | end 95 | 96 | def remove_instance_from_security_group(instance) 97 | existing_groups = instance.network_interfaces.first['groupIds'] 98 | 99 | if existing_groups.include?(security_group.group_id) 100 | ec2.modify_instance_attribute(instance.id, 'GroupId' => (existing_groups - [security_group.group_id])) 101 | end 102 | end 103 | 104 | def attributes 105 | attributes = { 106 | name: @current_resource.groupname, 107 | description: @current_resource.description, 108 | region: @current_resource.region 109 | } 110 | attributes[:vpc_id] = @current_resource.vpcid if @current_resource.vpcid 111 | attributes 112 | end 113 | 114 | def instance 115 | instance_host = '169.254.169.254' 116 | instance_id_url = '/latest/meta-data/instance-id' 117 | 118 | httpcall = Net::HTTP.new(instance_host) 119 | resp = httpcall.get2(instance_id_url) 120 | 121 | @instance ||= ec2.servers.get(resp.body) 122 | end 123 | -------------------------------------------------------------------------------- /providers/group_rule.rb: -------------------------------------------------------------------------------- 1 | include Aws::Ec2 2 | 3 | def whyrun_supported? 4 | true 5 | end 6 | 7 | use_inline_resources 8 | 9 | action :add do 10 | validate! 11 | if @current_resource.exists 12 | Chef::Log.info("#{@new_resource} already exists -- nothing to do") 13 | else 14 | raise "#{new_resource} can not be created -- security group '#{security_group}' does not " \ 15 | 'exist' unless security_group 16 | require 'ipaddress' 17 | converge_by("Adding rule #{@new_resource} to security group") do 18 | from_port = @current_resource.from_port 19 | to_port = @current_resource.to_port 20 | security_group.authorize_port_range(from_port..to_port, 21 | construct_security_group_options) 22 | end 23 | end 24 | end 25 | 26 | action :remove do 27 | if @current_resource.exists && security_group_rule_exact_match? 28 | converge_by("Removing rule #{@new_resource} from security group") do 29 | from_port = @current_resource.from_port 30 | to_port = @current_resource.to_port 31 | security_group.revoke_port_range(from_port..to_port, 32 | construct_security_group_options) 33 | end 34 | else 35 | Chef::Log.info("#{@new_resource} does not exists -- nothing to do") 36 | end 37 | end 38 | 39 | def validate! 40 | if @new_resource.group && 41 | (@new_resource.source_group_name || 42 | @new_resource.source_group_id) 43 | raise 'Cannot specify group and a source_group_* parameter at the same time' 44 | end 45 | return true unless @new_resource.source_group_name && 46 | @new_resource.source_group_id 47 | raise 'source_group_name and source_group_id cannot be specified at the ' \ 48 | 'same time.' 49 | end 50 | # rubocop:enable Metrics/CyclomaticComplexity 51 | 52 | def load_current_resource 53 | @current_resource = 54 | Chef::Resource::AwsSecurityGroupRule.new(@new_resource.name) 55 | 56 | @current_resource.mocking(@new_resource.mocking || 57 | node['aws_security']['mocking']) 58 | 59 | %w(aws_access_key_id 60 | aws_secret_access_key 61 | groupname 62 | name 63 | cidr_ip 64 | group 65 | ip_protocol 66 | port_range 67 | owner 68 | region 69 | ).each do |attrib| 70 | @current_resource.send(attrib, @new_resource.send(attrib)) 71 | end 72 | 73 | if @new_resource.group 74 | if group_is_id?(@new_resource.group) 75 | @current_resource.source_group_id(@new_resource.group) 76 | else 77 | @current_resource.source_group_name(@new_resource.group) 78 | end 79 | end 80 | 81 | if @current_resource.port_range 82 | (from_port, to_port) = @current_resource.port_range.split(/\.\./) 83 | @current_resource.from_port(from_port.to_i) 84 | @current_resource.to_port(to_port.to_i) 85 | else 86 | @current_resource.from_port(@new_resource.from_port) 87 | @current_resource.to_port(@new_resource.to_port) 88 | end 89 | if new_resource.groupid 90 | @current_resource.groupid(@new_resource.groupid) 91 | elsif security_groupname 92 | @current_resource.groupid(security_groupname.group_id) 93 | else 94 | return false 95 | end 96 | @current_resource.exists = security_group_rule_exists? 97 | end 98 | 99 | def group_is_id?(group) 100 | return true if group =~ /^sg-[a-zA-Z0-9]{8}$/ 101 | false 102 | end 103 | 104 | def security_group_rule_exact_match? 105 | security_group.ip_permissions.each do |ip_permission| 106 | return true if permission_exact_match? ip_permission 107 | end 108 | end 109 | 110 | def permission_exact_match?(existing_rule) 111 | return false unless current_resource_ip_permissions['groups'].eql?( 112 | existing_rule['groups'] 113 | ) 114 | return false unless current_resource_ip_permissions['ipProtocol'] == 115 | existing_rule['ipProtocol'] 116 | return false unless 117 | current_resource_ip_permissions['ipRanges'].sort_by { |r| r['cidrIp'] } == 118 | existing_rule['ipRanges'].sort_by { |r| r['cidrIp'] } 119 | unless current_resource_ip_permissions['ipProtocol'] == '-1' 120 | return false unless existing_rule['fromPort'] == 121 | current_resource_ip_permissions['fromPort'] && 122 | existing_rule['toPort'] == 123 | current_resource_ip_permissions['toPort'] 124 | end 125 | true 126 | end 127 | # rubocop:enable Metrics/MethodLength 128 | # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity 129 | 130 | def security_group_rule_exists? 131 | security_group.ip_permissions.each do |ip_permission| 132 | return true if permissions_overlap? ip_permission 133 | end 134 | false 135 | end 136 | 137 | def permissions_overlap?(existing_rule) 138 | if permission_exact_match?(existing_rule) 139 | Chef::Log.debug('Permissions match exactly') 140 | return true 141 | end 142 | if current_resource_ip_permissions['groups'].any? && existing_rule['groups'].any? 143 | if existing_rule['groups'].any? { |r| r['groupId'] == current_resource_ip_permissions['groups'].first['groupId'] } 144 | return true if port_range_overlap?(existing_rule['fromPort'], 145 | existing_rule['toPort']) 146 | end 147 | end 148 | return false unless !current_resource_ip_permissions['ipRanges'].empty? && 149 | ip_range_covered?(existing_rule['ipRanges']) 150 | return false unless current_resource_ip_permissions['ipProtocol'] != '-1' && 151 | port_range_overlap?(existing_rule['fromPort'], 152 | existing_rule['toPort']) 153 | true 154 | end 155 | 156 | def ip_range_covered?(existing_ranges) 157 | return true if (current_resource_ip_permissions['ipRanges'] - 158 | existing_ranges).empty? 159 | current_resource_ip_permissions['ipRanges'].each do |new_range| 160 | existing_ranges.each do |existing_range| 161 | return true if IPAddr.new(existing_range['cidrIp']).include?( 162 | IPAddr.new(new_range['cidrIp']) 163 | ) 164 | end 165 | end 166 | false 167 | end 168 | 169 | def port_range_overlap?(existing_from_port, existing_to_port) 170 | existing_from_port <= current_resource_ip_permissions['fromPort'] && 171 | existing_to_port >= current_resource_ip_permissions['toPort'] 172 | end 173 | 174 | def source_group 175 | return [] unless @current_resource.source_group_id || 176 | @current_resource.source_group_name 177 | o = { 'userId' => @current_resource.owner || security_group.owner_id } 178 | if @current_resource.source_group_id 179 | o['groupName'] = nil 180 | o['groupId'] = @current_resource.group 181 | else 182 | o['groupName'] = @current_resource.source_group_name 183 | o['groupId'] = nil 184 | end 185 | [o] 186 | end 187 | # rubocop:enable Metrics/MethodLength 188 | 189 | def source_ip_ranges 190 | return [] unless @current_resource.cidr_ip 191 | [{ 'cidrIp' => @current_resource.cidr_ip }] 192 | end 193 | 194 | def current_resource_ip_permissions 195 | @current_resource_ip_permissions ||= begin 196 | rule = { 'groups' => source_group, 197 | 'ipRanges' => source_ip_ranges, 198 | 'ipProtocol' => @current_resource.ip_protocol } 199 | unless @current_resource.ip_protocol == '-1' 200 | rule['fromPort'] = @current_resource.from_port 201 | rule['toPort'] = @current_resource.to_port 202 | end 203 | rule 204 | end 205 | end 206 | 207 | def construct_security_group_options 208 | options = { ip_protocol: @current_resource.ip_protocol } 209 | if @current_resource.cidr_ip 210 | options[:cidr_ip] = @current_resource.cidr_ip 211 | else 212 | options[:group] = { @current_resource.owner => @current_resource.group } 213 | end 214 | options 215 | end 216 | 217 | def security_group 218 | @security_group ||= ec2.security_groups.get_by_id(@current_resource.groupid) 219 | end 220 | 221 | def security_groupname 222 | @security_groupname ||= begin 223 | ec2.security_groups.all( 224 | 'group-name' => [@current_resource.groupname] 225 | ).first 226 | end 227 | end 228 | -------------------------------------------------------------------------------- /recipes/default.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook Name:: aws_security 3 | # Recipe:: default 4 | # 5 | # Author:: Greg Hellings () 6 | # 7 | # 8 | # Copyright 2014, B7 Interactive, LLC 9 | # 10 | # Licensed under the Apache License, Version 2.0 (the "License"); 11 | # you may not use this file except in compliance with the License. 12 | # You may obtain a copy of the License at 13 | # 14 | # http://www.apache.org/licenses/LICENSE-2.0 15 | # 16 | # Unless required by applicable law or agreed to in writing, software 17 | # distributed under the License is distributed on an "AS IS" BASIS, 18 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | # See the License for the specific language governing permissions and 20 | # limitations under the License. 21 | # 22 | 23 | Chef::Log.warn('The default aws_security recipe does nothing. See the readme for information on using the aws_security resources') 24 | -------------------------------------------------------------------------------- /resources/group.rb: -------------------------------------------------------------------------------- 1 | actions :create_if_missing, :remove, :attach, :create_and_attach, :detach 2 | 3 | default_action :create_if_missing 4 | 5 | attribute :groupname, kind_of: String, name_attribute: true, required: true 6 | attribute :description, kind_of: String, required: false 7 | attribute :vpcid, kind_of: String, required: false 8 | attribute :aws_access_key_id, kind_of: String, required: false 9 | attribute :aws_secret_access_key, kind_of: String, required: false 10 | attribute :region, kind_of: String, required: true 11 | attribute :mocking, kind_of: [TrueClass, FalseClass], 12 | default: false 13 | 14 | attr_accessor :exists 15 | -------------------------------------------------------------------------------- /resources/group_rule.rb: -------------------------------------------------------------------------------- 1 | actions :add, :remove 2 | 3 | default_action :add 4 | 5 | attribute :name, kind_of: String, 6 | name_attribute: true, 7 | required: false 8 | attribute :groupid, kind_of: String, 9 | callbacks: { 'is not a valid group ID' => 10 | ->(n) { valid_group_id?(n) } } 11 | attribute :groupname, kind_of: String, 12 | callbacks: { 'is not a valid group name' => 13 | ->(n) { valid_group?(n) } } 14 | attribute :description, kind_of: String 15 | attribute :cidr_ip, kind_of: String, 16 | callbacks: { 'is not a valid IP' => 17 | ->(ip) { valid_ip?(ip) } } 18 | attribute :group, kind_of: String, 19 | callbacks: { 'is not a valid group name/ID' => 20 | ->(n) { valid_group?(n) } } 21 | attribute :source_group_id, kind_of: String, 22 | regex: [/^sg-[a-zA-Z0-9]{8}$/] 23 | attribute :source_group_name, kind_of: String 24 | attribute :owner, kind_of: String 25 | attribute :ip_protocol, kind_of: String, 26 | default: 'tcp', 27 | equal_to: %w(-1 tcp udp icmp) 28 | attribute :port_range, kind_of: String 29 | attribute :aws_access_key_id, kind_of: String, required: false 30 | attribute :aws_secret_access_key, kind_of: String, required: false 31 | attribute :region, kind_of: String, default: 'us-east-1' 32 | attribute :from_port, kind_of: Integer, default: 0 33 | attribute :to_port, kind_of: Integer, default: 65_535 34 | attribute :mocking, kind_of: [TrueClass, FalseClass], 35 | default: false 36 | 37 | def self.valid_ip?(ip) 38 | require 'ipaddress' 39 | true if IPAddress.parse ip 40 | rescue ArgumentError => e 41 | raise e unless e.message =~ /Invalid IP/ 42 | false 43 | end 44 | 45 | def self.valid_group?(group) 46 | return true if group =~ /^[ \w-]+$/ 47 | false 48 | end 49 | 50 | def self.valid_group_id?(group_id) 51 | return true if group_id =~ /^sg-[a-zA-Z0-9]{8}$/ 52 | end 53 | 54 | attr_accessor :exists 55 | -------------------------------------------------------------------------------- /spec/recipes/default_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative '../spec_helper' 2 | 3 | describe 'aws_security::default' do 4 | let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) } 5 | 6 | it 'converges successfully' do 7 | expect { :chef_run }.to_not raise_error 8 | end 9 | 10 | end 11 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'chefspec' 2 | require 'chefspec/berkshelf' 3 | 4 | RSpec.configure do |config| 5 | # Specify the Chef log_level (default: :warn) 6 | config.log_level = :fatal 7 | 8 | # Specify the operating platform to mock Ohai data from 9 | config.platform = 'amazon' 10 | 11 | # Specify the operating version to mock Ohai data from 12 | config.version = '2016.03' 13 | end 14 | -------------------------------------------------------------------------------- /test/fixtures/cookbooks/fake/files/centos/runmoto: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | /usr/bin/moto_server -H 127.0.0.1 ec2 & 4 | -------------------------------------------------------------------------------- /test/fixtures/cookbooks/fake/files/ubuntu/runmoto: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | start-stop-daemon --start --background --make-pidfile \ 4 | --pidfile /var/run/moto_server --exec /usr/local/bin/moto_server -- \ 5 | -H 127.0.0.1 ec2 6 | -------------------------------------------------------------------------------- /test/fixtures/cookbooks/fake/metadata.rb: -------------------------------------------------------------------------------- 1 | name 'fake' 2 | version '1.0.1' 3 | 4 | depends 'aws_security' 5 | depends 'python' 6 | -------------------------------------------------------------------------------- /test/fixtures/cookbooks/fake/recipes/add_test.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook Name:: fake 3 | # Recipe:: test1 4 | # 5 | # Author:: Greg Hellings () 6 | # 7 | # 8 | # Copyright 2014, B7 Interactive, LLC 9 | # 10 | # Licensed under the Apache License, Version 2.0 (the 'License'); 11 | # you may not use this file except in compliance with the License. 12 | # You may obtain a copy of the License at 13 | # 14 | # http://www.apache.org/licenses/LICENSE-2.0 15 | # 16 | # Unless required by applicable law or agreed to in writing, software 17 | # distributed under the License is distributed on an 'AS IS' BASIS, 18 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | # See the License for the specific language governing permissions and 20 | # limitations under the License. 21 | # 22 | 23 | credentials = data_bag_item( 24 | node['aws_security']['encrypted_data_bag'], 25 | 'aws_keys' 26 | ) 27 | 28 | aws_security_group 'test' do 29 | description 'test security group' 30 | region 'us-west-2' 31 | aws_access_key_id credentials['aws_access_key_id'] 32 | aws_secret_access_key credentials['aws_secret_access_key'] 33 | end 34 | 35 | aws_security_group 'test_source_group' do 36 | description 'test source security group' 37 | region 'us-west-2' 38 | aws_access_key_id credentials['aws_access_key_id'] 39 | aws_secret_access_key credentials['aws_secret_access_key'] 40 | end 41 | 42 | aws_security_group_rule 'test rule 1' do 43 | description 'test rule 1' 44 | cidr_ip '192.168.1.1/32' 45 | groupname 'test' 46 | region 'us-west-2' 47 | port_range '80..80' 48 | ip_protocol 'tcp' 49 | aws_access_key_id credentials['aws_access_key_id'] 50 | aws_secret_access_key credentials['aws_secret_access_key'] 51 | end 52 | 53 | aws_security_group_rule 'test rule 2' do 54 | cidr_ip '192.168.1.2/32' 55 | groupname 'test' 56 | region 'us-west-2' 57 | port_range '80..80' 58 | ip_protocol 'udp' 59 | aws_access_key_id credentials['aws_access_key_id'] 60 | aws_secret_access_key credentials['aws_secret_access_key'] 61 | end 62 | 63 | aws_security_group_rule 'test rule 3' do 64 | cidr_ip '192.168.1.3/32' 65 | groupname 'test' 66 | region 'us-west-2' 67 | port_range '80..80' 68 | ip_protocol 'tcp' 69 | aws_access_key_id credentials['aws_access_key_id'] 70 | aws_secret_access_key credentials['aws_secret_access_key'] 71 | end 72 | 73 | aws_security_group_rule 'test rule 3 (duplicate)' do 74 | cidr_ip '192.168.1.3/32' 75 | groupname 'test' 76 | region 'us-west-2' 77 | port_range '80..80' 78 | ip_protocol 'tcp' 79 | aws_access_key_id credentials['aws_access_key_id'] 80 | aws_secret_access_key credentials['aws_secret_access_key'] 81 | end 82 | 83 | aws_security_group_rule 'test rule 4' do 84 | group 'test_source_group' 85 | groupname 'test' 86 | region 'us-west-2' 87 | port_range '80..80' 88 | ip_protocol 'tcp' 89 | aws_access_key_id credentials['aws_access_key_id'] 90 | aws_secret_access_key credentials['aws_secret_access_key'] 91 | end 92 | 93 | aws_security_group_rule 'test rule 5' do 94 | group 'test_source_group' 95 | groupname 'test' 96 | region 'us-west-2' 97 | ip_protocol 'tcp' 98 | aws_access_key_id credentials['aws_access_key_id'] 99 | aws_secret_access_key credentials['aws_secret_access_key'] 100 | end 101 | -------------------------------------------------------------------------------- /test/fixtures/cookbooks/fake/recipes/aws_mock.rb: -------------------------------------------------------------------------------- 1 | include_recipe 'python' 2 | 3 | python_pip 'moto' 4 | python_pip 'argparse' 5 | 6 | cookbook_file '/usr/local/bin/runmoto' do 7 | source 'runmoto' 8 | owner 'root' 9 | group 'root' 10 | mode 0755 11 | end 12 | 13 | execute 'runmoto' do 14 | command '/usr/local/bin/runmoto' 15 | creates '/var/run/moto_server' 16 | action :run 17 | end 18 | 19 | # packages to get nokogiri to install with serverspec 20 | package %w(zlib1g-dev liblzma-dev) if platform_family?('debian') 21 | 22 | package %w(gcc ruby-devel zlib-devel) if platform_family?('rhel') 23 | -------------------------------------------------------------------------------- /test/fixtures/cookbooks/fake/recipes/remove_test.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook Name:: fake 3 | # Recipe:: test2 4 | # 5 | # Author:: Greg Hellings () 6 | # 7 | # 8 | # Copyright 2014, B7 Interactive, LLC 9 | # 10 | # Licensed under the Apache License, Version 2.0 (the 'License'); 11 | # you may not use this file except in compliance with the License. 12 | # You may obtain a copy of the License at 13 | # 14 | # http://www.apache.org/licenses/LICENSE-2.0 15 | # 16 | # Unless required by applicable law or agreed to in writing, software 17 | # distributed under the License is distributed on an 'AS IS' BASIS, 18 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | # See the License for the specific language governing permissions and 20 | # limitations under the License. 21 | # 22 | 23 | credentials = Chef::EncryptedDataBagItem.load( 24 | node['aws_security']['encrypted_data_bag'], 25 | 'aws_keys' 26 | ) 27 | 28 | aws_security_group_rule 'test rule 1' do 29 | description 'test rule 1' 30 | cidr_ip '192.168.1.1/32' 31 | groupname 'test' 32 | region 'us-west-2' 33 | port_range '80..80' 34 | ip_protocol 'tcp' 35 | aws_access_key_id credentials['aws_access_key_id'] 36 | aws_secret_access_key credentials['aws_secret_access_key'] 37 | action :remove 38 | end 39 | 40 | aws_security_group_rule 'test rule 2' do 41 | cidr_ip '192.168.1.2/32' 42 | groupname 'test' 43 | region 'us-west-2' 44 | port_range '80..80' 45 | ip_protocol 'udp' 46 | aws_access_key_id credentials['aws_access_key_id'] 47 | aws_secret_access_key credentials['aws_secret_access_key'] 48 | action :remove 49 | end 50 | 51 | aws_security_group_rule 'test rule 3' do 52 | cidr_ip '192.168.1.3/32' 53 | groupname 'test' 54 | region 'us-west-2' 55 | port_range '80..80' 56 | ip_protocol 'tcp' 57 | aws_access_key_id credentials['aws_access_key_id'] 58 | aws_secret_access_key credentials['aws_secret_access_key'] 59 | action :remove 60 | end 61 | 62 | aws_security_group_rule 'test rule 3 (duplicate)' do 63 | cidr_ip '192.168.1.3/32' 64 | groupname 'test' 65 | region 'us-west-2' 66 | port_range '80..80' 67 | ip_protocol 'tcp' 68 | aws_access_key_id credentials['aws_access_key_id'] 69 | aws_secret_access_key credentials['aws_secret_access_key'] 70 | action :remove 71 | end 72 | 73 | aws_security_group_rule 'test rule 4' do 74 | group 'test_source_group' 75 | groupname 'test' 76 | region 'us-west-2' 77 | port_range '80..80' 78 | ip_protocol 'tcp' 79 | aws_access_key_id credentials['aws_access_key_id'] 80 | aws_secret_access_key credentials['aws_secret_access_key'] 81 | action :remove 82 | end 83 | 84 | aws_security_group_rule 'test rule 5' do 85 | group 'test_source_group' 86 | groupname 'test' 87 | region 'us-west-2' 88 | ip_protocol 'tcp' 89 | aws_access_key_id credentials['aws_access_key_id'] 90 | aws_secret_access_key credentials['aws_secret_access_key'] 91 | action :remove 92 | end 93 | 94 | aws_security_group 'test' do 95 | description 'test security group' 96 | region 'us-west-2' 97 | aws_access_key_id credentials['aws_access_key_id'] 98 | aws_secret_access_key credentials['aws_secret_access_key'] 99 | action :remove 100 | end 101 | -------------------------------------------------------------------------------- /test/fixtures/cookbooks/fake/templates/default/aws_config.erb: -------------------------------------------------------------------------------- 1 | [default] 2 | aws_access_key_id = <%= @aws_access_key_id %> 3 | aws_secret_access_key = <%= @aws_secret_access_key %> 4 | -------------------------------------------------------------------------------- /test/integration/add_test/serverspec/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'fog-aws', '~> 0.11' 4 | gem 'rspec-collection_matchers' 5 | -------------------------------------------------------------------------------- /test/integration/add_test/serverspec/security_group_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe "it should create a security group named 'test" do 4 | sg = ec2.security_groups.all('group-name' => 'test').first 5 | source_group_id = ec2.security_groups.all( 6 | 'group-name' => 'test_source_group' 7 | ).first.group_id 8 | user_id = '111122223333' 9 | 10 | describe sg.attributes do 11 | it { should include(name: 'test') } 12 | it { should include(description: 'test security group') } 13 | end 14 | describe sg.ip_permissions do 15 | it { should have_exactly(5).entries } 16 | it do 17 | should include('fromPort' => 80, 18 | 'toPort' => 80, 19 | 'ipProtocol' => 'tcp', 20 | 'ipRanges' => [{ 'cidrIp' => '192.168.1.1/32' }], 21 | 'groups' => []) 22 | end 23 | it do 24 | should include('fromPort' => 80, 25 | 'toPort' => 80, 26 | 'ipProtocol' => 'udp', 27 | 'ipRanges' => [{ 'cidrIp' => '192.168.1.2/32' }], 28 | 'groups' => []) 29 | end 30 | it do 31 | should include('fromPort' => 80, 32 | 'toPort' => 80, 33 | 'ipProtocol' => 'tcp', 34 | 'ipRanges' => [{ 'cidrIp' => '192.168.1.3/32' }], 35 | 'groups' => []) 36 | end 37 | it do 38 | should include('fromPort' => 80, 39 | 'toPort' => 80, 40 | 'ipProtocol' => 'tcp', 41 | 'ipRanges' => [], 42 | 'groups' => [{ 'userId' => user_id, 43 | 'groupName' => 'test_source_group', 44 | 'groupId' => source_group_id }]) 45 | end 46 | it do 47 | should include('fromPort' => 0, 48 | 'toPort' => 65_535, 49 | 'ipProtocol' => 'tcp', 50 | 'ipRanges' => [], 51 | 'groups' => [{ 'userId' => user_id, 52 | 'groupName' => 'test_source_group', 53 | 'groupId' => source_group_id }]) 54 | end 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /test/integration/add_test/serverspec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'serverspec' 2 | require 'rspec/collection_matchers' 3 | require 'fog/aws' 4 | 5 | set :backend, :exec 6 | 7 | def ec2 8 | @ec2 ||= Fog::Compute::AWS.new(host: 'localhost', 9 | port: 5000, 10 | scheme: 'http', 11 | aws_access_key_id: 'MOCKING', 12 | aws_secret_access_key: 'MOCKING') 13 | end 14 | -------------------------------------------------------------------------------- /test/integration/remove_test/serverspec/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'fog-aws', '~> 0.11' 4 | -------------------------------------------------------------------------------- /test/integration/remove_test/serverspec/security_group_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe "it should create a security group named 'test" do 4 | describe ec2.security_groups.all('group-name' => 'test') do 5 | it { should eq [] } 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /test/integration/remove_test/serverspec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'serverspec' 2 | require 'fog/aws' 3 | 4 | set :backend, :exec 5 | 6 | def ec2 7 | @ec2 ||= Fog::Compute::AWS.new(host: 'localhost', 8 | port: 5000, 9 | scheme: 'http', 10 | aws_access_key_id: 'MOCKING', 11 | aws_secret_access_key: 'MOCKING') 12 | end 13 | --------------------------------------------------------------------------------