Class: Reyes::GroupManager
- Inherits:
-
Object
- Object
- Reyes::GroupManager
- Includes:
- Chalk::Log
- Defined in:
- lib/reyes/group_manager.rb
Overview
TODO: use a more precise name
Constant Summary collapse
- ReyesInputChain =
'reyes-input'
Instance Attribute Summary collapse
-
#fake_aws ⇒ Object
readonly
Returns the value of attribute fake_aws.
-
#instance_id ⇒ Object
readonly
Returns the value of attribute instance_id.
Class Method Summary collapse
-
.parse_ipset_name(name) ⇒ Hash
The parsed names, or nil in case of error.
Instance Method Summary collapse
-
#addresses_for_group(region, group_id) ⇒ Array<String>
Look up addresses for given group in remote VPCs.
-
#foreign_groups_by_name(name) ⇒ Hash
Look up remote VPC / EC2 classic security groups by name.
- #generate_ipsets(data) ⇒ Object
- #generate_iptables_script(data, options = {}) ⇒ String
-
#generate_rules ⇒ Object
Given our instance ID and security group rules, generate IPTables rules needed to emulate security group behavior for foreign VPCs.
- #generate_rules_empty ⇒ Object
-
#initialize(fake_aws, region, instance_id, generation = nil) ⇒ GroupManager
constructor
A new instance of GroupManager.
-
#input_chain_rules ⇒ Array<String>
Generate a list of IPTables script lines that will inject traffic into the Reyes processing chain.
-
#ipset_name_for_group_hash(group_hash) ⇒ String
A string title, at most 31 characters long.
- #load_from_s3(aws, config) ⇒ Object
-
#local_ipv4_addresses ⇒ Array<IPAddr>
List IPv4 addresses of the current host.
- #our_groups(skip_excluded = true) ⇒ Hash
-
#our_instance ⇒ Hash
Look up data for this instance from FakeAws data.
- #run_generation ⇒ Integer
-
#run_generation_increment! ⇒ Object
Increment the run generation and persist it to disk.
- #run_generation_time ⇒ Time
-
#vpc? ⇒ Boolean
Whether the self EC2 instance is in VPC.
-
#vpc_id ⇒ String?
The VPC ID (or nil) of the self EC2 instance.
Constructor Details
#initialize(fake_aws, region, instance_id, generation = nil) ⇒ GroupManager
Returns a new instance of GroupManager.
18 19 20 21 22 23 24 25 |
# File 'lib/reyes/group_manager.rb', line 18 def initialize(fake_aws, region, instance_id, generation=nil) log.info("Initializing #{self.class.name} for #{region} #{instance_id}") @fake_aws = fake_aws @generation = generation || RunGeneration.new @region = region @instance_id = instance_id end |
Instance Attribute Details
#fake_aws ⇒ Object (readonly)
Returns the value of attribute fake_aws.
12 13 14 |
# File 'lib/reyes/group_manager.rb', line 12 def fake_aws @fake_aws end |
#instance_id ⇒ Object (readonly)
Returns the value of attribute instance_id.
12 13 14 |
# File 'lib/reyes/group_manager.rb', line 12 def instance_id @instance_id end |
Class Method Details
.parse_ipset_name(name) ⇒ Hash
Returns the parsed names, or nil in case of error.
191 192 193 194 195 196 197 198 199 200 201 202 |
# File 'lib/reyes/group_manager.rb', line 191 def self.parse_ipset_name(name) parts = name.split(":") return nil unless parts.length == 4 generation, region, group_id, group_name = parts return { generation: Integer(generation), region: region, group_id: group_id, group_name: group_name } end |
Instance Method Details
#addresses_for_group(region, group_id) ⇒ Array<String>
Look up addresses for given group in remote VPCs.
211 212 213 |
# File 'lib/reyes/group_manager.rb', line 211 def addresses_for_group(region, group_id) fake_aws.addresses_for_security_group(region, group_id).reject(&:nil?) end |
#foreign_groups_by_name(name) ⇒ Hash
Look up remote VPC / EC2 classic security groups by name.
223 224 225 |
# File 'lib/reyes/group_manager.rb', line 223 def foreign_groups_by_name(name) fake_aws.foreign_groups_by_name(name, vpc_id) end |
#generate_ipsets(data) ⇒ Object
151 152 153 154 155 156 157 158 159 160 161 162 |
# File 'lib/reyes/group_manager.rb', line 151 def generate_ipsets(data) data.fetch(:ipsets).map do |name, hosts| log.info "Creating IPSet for #{name.inspect}" builder = IPSetBuilder.new(name) hosts.each do |host| log.info " - #{host.inspect}" builder << host end builder end end |
#generate_iptables_script(data, options = {}) ⇒ String
263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 |
# File 'lib/reyes/group_manager.rb', line 263 def generate_iptables_script(data, ={}) log.info("Generating script for iptables-restore") = { log_drop: true, log_accept: false, }.merge() log_drop = .fetch(:log_drop) log_accept = .fetch(:log_accept) lines = [] lines << "# Generated by Reyes v#{Reyes::VERSION} at #{Time.now.to_s}" # we use the default filter table lines << '*filter' lines << ':INPUT ACCEPT' lines << ':FORWARD ACCEPT' lines << ':OUTPUT ACCEPT' lines << '' lines << ":#{ReyesInputChain} -" lines << ':reyes-accept -' lines << ':reyes-drop -' lines << ':reyes-log-accept -' lines << ':reyes-log-drop -' # add rules to direct appropriate traffic into reyes lines << '' lines << '## input chain rules' lines.concat(input_chain_rules) lines << '' lines << '-A reyes-accept -j reyes-log-accept' if log_accept lines << '-A reyes-accept -j ACCEPT' lines << '-A reyes-drop -j reyes-log-drop' if log_drop lines << '-A reyes-drop -j DROP' lines << '' lines << '## static global rules' # allow normal ICMP traffic IPTables.innocuous_icmp_rules(ReyesInputChain).each do |r| lines << r.join(' ') end # allow established connections without logging lines << "-A #{ReyesInputChain} -m conntrack --ctstate " \ "RELATED,ESTABLISHED -j ACCEPT" # drop invalid connections lines << "-A #{ReyesInputChain} -m conntrack --ctstate " \ "INVALID -j reyes-drop" # add dynamic rules lines << '' lines << '## dynamic rules from security groups' dynamic_rules_from_data(data).each do |rule| lines << rule end # add reyes-drop to very end of reyes-input chain lines << '' lines << '## default drop' lines << "-A #{ReyesInputChain} -j reyes-drop" lines << '' lines << '## log rules' lines << IPTables.log_rule_string('reyes-log-accept', 'REYES ACCEPT') lines << IPTables.log_rule_string('reyes-log-drop', 'REYES BLOCK') # commit the filter table lines << '' lines << 'COMMIT' text = lines.join("\n") text << "\n" text end |
#generate_rules ⇒ Object
Given our instance ID and security group rules, generate IPTables rules needed to emulate security group behavior for foreign VPCs.
Look up the set of instance IP addresses in any remote VPC security groups referenced by our rules, and add them to a list of ipsets to create.
Also create a list of IPTables rules that will reference these ipsets.
This method generates the data and returns it as a hash without making any changes to the system.
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
# File 'lib/reyes/group_manager.rb', line 96 def generate_rules log.info("Generating rules for generation #{run_generation}") data = generate_rules_empty needed_groups = {} our_groups.each_pair do |group_id, group| group_key = "#{group_id}:#{group.fetch('name')}" data[:groups][group_key] = [] # we only work with ingress permissions group.fetch('ingress_ip_permissions').each do |perm| perm_hash = { protocol: perm.fetch('protocol'), port: [perm.fetch('port_start'), perm.fetch('port_end')], remote_addrs: [], remote_sets: [], } case perm.fetch('protocol').to_s when 'icmp' log.info("Skipping ICMP rule") next when 'tcp', 'udp' # append IP ranges perm_hash[:remote_addrs] += perm.fetch('ip_ranges') # list the security groups we'll need to look up perm.fetch('group_names').each do |g_name| foreign_groups_by_name(g_name).each_pair do |fg_id, fg_data| needed_groups[fg_id] = fg_data perm_hash[:remote_sets] << ipset_name_for_group_hash(fg_data) end end else raise NotImplementedError.new( "Unexpected protocol #{perm['protocol'].inspect} in #{group_id}") end data[:groups][group_key] << perm_hash end end data[:ipsets] = {} needed_groups.each_pair do |group_id, group_data| data[:ipsets][ipset_name_for_group_hash(group_data)] = \ addresses_for_group(group_data.fetch('region'), group_id) end data end |
#generate_rules_empty ⇒ Object
72 73 74 75 76 77 |
# File 'lib/reyes/group_manager.rb', line 72 def generate_rules_empty { :groups => {}, :ipsets => {}, } end |
#input_chain_rules ⇒ Array<String>
Generate a list of IPTables script lines that will inject traffic into the Reyes processing chain.
In EC2 classic, traffic relevant to Reyes will be arriving directly through IPsec, so these rules will filter all IPsec traffic.
In VPC, traffic relevant to Reyes may be forwarded by VPN servers and arrive from VPC CIDR blocks or EC2 classic CIDR blocks. All of this CIDR block information will be fetched from FakeAws data.
239 240 241 242 243 244 245 246 247 248 249 250 251 252 |
# File 'lib/reyes/group_manager.rb', line 239 def input_chain_rules if vpc? # filter all remote CIDR blocks through reyes fake_aws.remote_cidr_blocks(vpc_id).map do |cidr| check_cidr_ok_for_reyes(cidr) "-A INPUT -s #{cidr} -j #{ReyesInputChain}" end else # filter all ipsec tunneled traffic through reyes ["-A INPUT -m policy --pol ipsec --dir in -j #{ReyesInputChain}"] end end |
#ipset_name_for_group_hash(group_hash) ⇒ String
Returns A string title, at most 31 characters long.
183 184 185 186 187 188 |
# File 'lib/reyes/group_manager.rb', line 183 def ipset_name_for_group_hash(group_hash) [ run_generation.to_s, group_hash.fetch('ipset_suffix'), ].join(':')[0...31] end |
#load_from_s3(aws, config) ⇒ Object
79 80 81 82 |
# File 'lib/reyes/group_manager.rb', line 79 def load_from_s3(aws, config) s3 = S3Loader.new(aws, config) s3.latest end |
#local_ipv4_addresses ⇒ Array<IPAddr>
List IPv4 addresses of the current host.
55 56 57 58 59 |
# File 'lib/reyes/group_manager.rb', line 55 def local_ipv4_addresses Socket.ip_address_list.find_all(&:ipv4?).map {|a| IPAddr.new(a.ip_address) } end |
#our_groups(skip_excluded = true) ⇒ Hash
62 63 64 65 66 67 68 69 70 |
# File 'lib/reyes/group_manager.rb', line 62 def our_groups(skip_excluded=true) data = fake_aws.security_groups_for_instance(@region, @instance_id) if skip_excluded exclude = fake_aws.excluded_group_names.to_set data.reject {|g_id, g_data| exclude.include?(g_data.fetch('name')) } else data end end |
#our_instance ⇒ Hash
Look up data for this instance from FakeAws data.
47 48 49 |
# File 'lib/reyes/group_manager.rb', line 47 def our_instance fake_aws.instance(@region, @instance_id) end |
#run_generation ⇒ Integer
165 166 167 |
# File 'lib/reyes/group_manager.rb', line 165 def run_generation @generation.value end |
#run_generation_increment! ⇒ Object
Increment the run generation and persist it to disk
175 176 177 |
# File 'lib/reyes/group_manager.rb', line 175 def run_generation_increment! @generation.increment! end |
#run_generation_time ⇒ Time
170 171 172 |
# File 'lib/reyes/group_manager.rb', line 170 def run_generation_time @generation.mtime end |
#vpc? ⇒ Boolean
Whether the self EC2 instance is in VPC.
31 32 33 |
# File 'lib/reyes/group_manager.rb', line 31 def vpc? !!vpc_id end |
#vpc_id ⇒ String?
The VPC ID (or nil) of the self EC2 instance.
39 40 41 |
# File 'lib/reyes/group_manager.rb', line 39 def vpc_id our_instance.fetch('vpc') end |