Add rules to database, cast refresh message and trickle down to firewall driver.
This commit is contained in:
@@ -21,7 +21,10 @@ Admin API controller, exposed through http via the api worker.
|
||||
"""
|
||||
|
||||
import base64
|
||||
import IPy
|
||||
import urllib
|
||||
|
||||
from nova import compute
|
||||
from nova import db
|
||||
from nova import exception
|
||||
from nova import log as logging
|
||||
@@ -70,6 +73,9 @@ class AdminController(object):
|
||||
def __str__(self):
|
||||
return 'AdminController'
|
||||
|
||||
def __init__(self):
|
||||
self.compute_api = compute.API()
|
||||
|
||||
def describe_user(self, _context, name, **_kwargs):
|
||||
"""Returns user data, including access and secret keys."""
|
||||
return user_dict(manager.AuthManager().get_user(name))
|
||||
@@ -210,10 +216,39 @@ class AdminController(object):
|
||||
"""Returns status info for single node."""
|
||||
return host_dict(db.host_get(name))
|
||||
|
||||
def _provider_fw_rule_exists(context, rule):
|
||||
for old_rule in db.provider_fw_rule_get_all(context):
|
||||
for key in ('cidr', 'from_port', 'to_port', 'protocol'):
|
||||
dupe = True
|
||||
if rule[key] != old_rule[key]:
|
||||
dupe = False
|
||||
if dupe:
|
||||
return dupe
|
||||
return False
|
||||
|
||||
|
||||
def block_external_addresses(self, context, cidr):
|
||||
"""Add provider-level firewall rules to block incoming traffic."""
|
||||
LOG.audit(_("Blocking access to all projects incoming from %s"),
|
||||
LOG.audit(_("Blocking traffic to all projects incoming from %s"),
|
||||
cidr, context=context)
|
||||
raise NotImplementedError(_("Awaiting implementation."))
|
||||
# TODO(todd): implement
|
||||
# return {'status': 'OK', 'message': 'Disabled (number) IPs'}
|
||||
rule = {'cidr': IPy.IP(urllib.unquote(cidr).decode())}
|
||||
tcp_rule = rule.copy()
|
||||
tcp_rule.update({"protocol": "TCP", "from_port": 1, "to_port": 65535})
|
||||
udp_rule = rule.copy()
|
||||
udp_rule.update({"protocol": "UDP", "from_port": 1, "to_port": 65535})
|
||||
icmp_rule = rule.copy()
|
||||
icmp_rule.update({"protocol": "ICMP", "from_port": -1, "to_port": -1})
|
||||
rules_added = 0
|
||||
if not self._provider_fw_rule_exists(context, tcp_rule):
|
||||
db.provider_fw_rule_create(context, tcp_rule)
|
||||
rules_added += 1
|
||||
if not self._provider_fw_rule_exists(context, udp_rule):
|
||||
db.provider_fw_rule_create(context, udp_rule)
|
||||
rules_added += 1
|
||||
if not self._provider_fw_rule_exists(context, icmp_rule):
|
||||
db.provider_fw_rule_create(context, icmp_rule)
|
||||
rules_added += 1
|
||||
if rules_added == 0:
|
||||
raise exception.ApiError(_('Duplicate rule'))
|
||||
self.compute_api.trigger_provider_fw_rules_refresh(context)
|
||||
return {'status': 'OK', 'message': 'Disabled (number) IPs'}
|
||||
|
||||
@@ -268,6 +268,15 @@ class API(base.Base):
|
||||
{"method": "refresh_security_group_members",
|
||||
"args": {"security_group_id": group_id}})
|
||||
|
||||
def trigger_provider_fw_rules_refresh(self, context):
|
||||
"""Called when a rule is added to or removed from a security_group"""
|
||||
|
||||
hosts = [x['host'] for x in db.service_get_all_compute_sorted(context)]
|
||||
for host in hosts:
|
||||
rpc.cast(context,
|
||||
self.db.queue_get_for(context, FLAGS.compute_topic, host),
|
||||
{"method": "refresh_provider_fw_rules", "args": {}})
|
||||
|
||||
def update(self, context, instance_id, **kwargs):
|
||||
"""Updates the instance in the datastore.
|
||||
|
||||
|
||||
@@ -169,6 +169,11 @@ class ComputeManager(manager.Manager):
|
||||
"""This call passes straight through to the virtualization driver."""
|
||||
return self.driver.refresh_security_group_members(security_group_id)
|
||||
|
||||
@exception.wrap_exception
|
||||
def refresh_provider_fw_rules(self, context, **_kwargs):
|
||||
"""This call passes straight through to the virtualization driver."""
|
||||
return self.driver.refresh_security_group_rules()
|
||||
|
||||
@exception.wrap_exception
|
||||
def run_instance(self, context, instance_id, **_kwargs):
|
||||
"""Launch a new instance with specified options."""
|
||||
|
||||
@@ -813,6 +813,14 @@ def security_group_rule_destroy(context, security_group_rule_id):
|
||||
###################
|
||||
|
||||
|
||||
def provider_fw_rule_create(context, rule):
|
||||
"""Add a firewall rule at the provider level (all hosts & instances)."""
|
||||
return IMPL.provider_fw_rule_create(context, rule)
|
||||
|
||||
|
||||
###################
|
||||
|
||||
|
||||
def user_get(context, id):
|
||||
"""Get user by id."""
|
||||
return IMPL.user_get(context, id)
|
||||
|
||||
@@ -1687,6 +1687,18 @@ def security_group_rule_destroy(context, security_group_rule_id):
|
||||
|
||||
###################
|
||||
|
||||
|
||||
@require_admin_context
|
||||
def provider_fw_rule_create(context, rule):
|
||||
fw_rule_ref = models.ProviderFirewallRule()
|
||||
fw_rule_ref.update(rule)
|
||||
fw_rule_ref.save()
|
||||
return fw_rule_ref
|
||||
|
||||
|
||||
###################
|
||||
|
||||
|
||||
@require_admin_context
|
||||
def user_get(context, id, session=None):
|
||||
if not session:
|
||||
|
||||
@@ -394,6 +394,17 @@ class SecurityGroupIngressRule(BASE, NovaBase):
|
||||
group_id = Column(Integer, ForeignKey('security_groups.id'))
|
||||
|
||||
|
||||
class ProviderFirewallRule(BASE, NovaBase):
|
||||
"""Represents a rule in a security group."""
|
||||
__tablename__ = 'provider_fw_rules'
|
||||
id = Column(Integer, primary_key=True)
|
||||
|
||||
protocol = Column(String(5)) # "tcp", "udp", or "icmp"
|
||||
from_port = Column(Integer)
|
||||
to_port = Column(Integer)
|
||||
cidr = Column(String(255))
|
||||
|
||||
|
||||
class KeyPair(BASE, NovaBase):
|
||||
"""Represents a public key pair for ssh."""
|
||||
__tablename__ = 'key_pairs'
|
||||
|
||||
@@ -54,6 +54,7 @@ def get_connection(read_only=False):
|
||||
* fake
|
||||
* libvirt
|
||||
* xenapi
|
||||
* hyperv
|
||||
"""
|
||||
# TODO(termie): maybe lazy load after initial check for permissions
|
||||
# TODO(termie): check whether we can be disconnected
|
||||
|
||||
@@ -848,6 +848,9 @@ class LibvirtConnection(object):
|
||||
def refresh_security_group_members(self, security_group_id):
|
||||
self.firewall_driver.refresh_security_group_members(security_group_id)
|
||||
|
||||
def refresh_provier_fw_rules(self):
|
||||
self.firewall_driver.refresh_provider_fw_rules()
|
||||
|
||||
|
||||
class FirewallDriver(object):
|
||||
def prepare_instance_filter(self, instance):
|
||||
@@ -884,6 +887,13 @@ class FirewallDriver(object):
|
||||
the security group."""
|
||||
raise NotImplementedError()
|
||||
|
||||
def refresh_provider_fw_rules(self):
|
||||
"""Refresh common rules for all hosts/instances from data store.
|
||||
|
||||
Gets called when a rule has been added to or removed from
|
||||
the list of rules (via admin api)."""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class NWFilterFirewall(FirewallDriver):
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user