Re-implement network faults using standard iptables module
1. Switch from home-made iptables module to the one from Ansible. 2. Allow to choose the direction: ingress (default) or egress. 3. Refactor to use shared schema for ports. Change-Id: I4904c4abd0dd23ceb48ba4f3c403fa8dc498926b
This commit is contained in:
@@ -1,48 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
# 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
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# 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.
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule # noqa
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
service=dict(required=True, type='str'),
|
||||
action=dict(required=True, choices=['block', 'unblock']),
|
||||
port=dict(required=True, type='int'),
|
||||
protocol=dict(required=True, choices=['tcp', 'udp']),
|
||||
))
|
||||
|
||||
service = module.params['service']
|
||||
action = module.params['action']
|
||||
port = module.params['port']
|
||||
protocol = module.params['protocol']
|
||||
comment = '{}_temporary_DROP'.format(service)
|
||||
|
||||
if action == 'block':
|
||||
cmd = ('bash -c "iptables -I INPUT 1 -p {protocol} --dport {port} '
|
||||
'-j DROP -m comment --comment "{comment}""'.format(
|
||||
comment=comment, port=port, protocol=protocol))
|
||||
else:
|
||||
cmd = ('bash -c "rule=`iptables -L INPUT -n --line-numbers | '
|
||||
'grep "{comment}" | cut -d \' \' -f1`; for arg in $rule;'
|
||||
' do iptables -D INPUT -p {protocol} --dport {port} '
|
||||
'-j DROP -m comment --comment "{comment}"; done"'.format(
|
||||
comment=comment, port=port, protocol=protocol))
|
||||
rc, stdout, stderr = module.run_command(cmd, check_rc=True)
|
||||
module.exit_json(cmd=cmd, rc=rc, stderr=stderr, stdout=stdout)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@@ -53,9 +53,9 @@ class CloudManagement(base_driver.BaseDriver):
|
||||
self.containers.update(containers)
|
||||
|
||||
def validate_services(self):
|
||||
for service_name, serive_conf in self.services.items():
|
||||
serive_cls = registry.get_driver(serive_conf["driver"])
|
||||
jsonschema.validate(serive_conf['args'], serive_cls.CONFIG_SCHEMA)
|
||||
for service_name, service_conf in self.services.items():
|
||||
cls = registry.get_driver(service_conf["driver"])
|
||||
jsonschema.validate(service_conf['args'], cls.CONFIG_SCHEMA)
|
||||
|
||||
def validate_containers(self):
|
||||
for container_name, container_conf in self.containers.items():
|
||||
|
@@ -17,19 +17,11 @@ import signal
|
||||
from os_faults.ansible import executor
|
||||
from os_faults.api import error
|
||||
from os_faults.api import service
|
||||
from os_faults.drivers import shared_schemas
|
||||
from os_faults import utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
PORT_SCHEMA = {
|
||||
'type': 'array',
|
||||
'items': [
|
||||
{'enum': ['tcp', 'udp']},
|
||||
{'type': 'integer', 'minimum': 0, 'maximum': 65535},
|
||||
],
|
||||
'minItems': 2,
|
||||
'maxItems': 2,
|
||||
}
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ServiceAsProcess(service.Service):
|
||||
@@ -52,7 +44,7 @@ class ServiceAsProcess(service.Service):
|
||||
restart_cmd: /bin/my_app --restart
|
||||
terminate_cmd: /bin/stop_my_app
|
||||
start_cmd: /bin/my_app
|
||||
port: ['tcp', 4242]
|
||||
port: ['tcp', 4242, 'ingress']
|
||||
|
||||
parameters:
|
||||
|
||||
@@ -60,8 +52,11 @@ class ServiceAsProcess(service.Service):
|
||||
- **restart_cmd** - command to restart service (optional)
|
||||
- **terminate_cmd** - command to terminate service (optional)
|
||||
- **start_cmd** - command to start service (optional)
|
||||
- **port** - tuple with two values - protocol, port number (optional)
|
||||
- **port** - tuple with two or three values - protocol, port number,
|
||||
direction (optional)
|
||||
|
||||
Note that network operations are based on iptables. They are applied
|
||||
to the whole host and not restricted to a single process.
|
||||
"""
|
||||
|
||||
NAME = 'process'
|
||||
@@ -73,7 +68,7 @@ class ServiceAsProcess(service.Service):
|
||||
'start_cmd': {'type': 'string'},
|
||||
'terminate_cmd': {'type': 'string'},
|
||||
'restart_cmd': {'type': 'string'},
|
||||
'port': PORT_SCHEMA,
|
||||
'port': shared_schemas.PORT_SCHEMA,
|
||||
},
|
||||
'required': ['grep'],
|
||||
'additionalProperties': False,
|
||||
@@ -163,10 +158,14 @@ class ServiceAsProcess(service.Service):
|
||||
def plug(self, nodes=None):
|
||||
nodes = nodes if nodes is not None else self.get_nodes()
|
||||
message = "Open port %d for" % self.port[1]
|
||||
direction = self.port[2] if len(self.port) > 2 else 'ingress'
|
||||
task = {
|
||||
'iptables': {
|
||||
'protocol': self.port[0], 'port': self.port[1],
|
||||
'action': 'unblock', 'service': self.service_name
|
||||
'chain': 'INPUT' if direction == 'ingress' else 'OUTPUT',
|
||||
'protocol': self.port[0],
|
||||
'jump': 'DROP',
|
||||
'destination_port': self.port[1],
|
||||
'state': 'absent',
|
||||
},
|
||||
'become': 'yes',
|
||||
}
|
||||
@@ -176,10 +175,15 @@ class ServiceAsProcess(service.Service):
|
||||
def unplug(self, nodes=None):
|
||||
nodes = nodes if nodes is not None else self.get_nodes()
|
||||
message = "Close port %d for" % self.port[1]
|
||||
direction = self.port[2] if len(self.port) > 2 else 'ingress'
|
||||
task = {
|
||||
'iptables': {
|
||||
'protocol': self.port[0], 'port': self.port[1],
|
||||
'action': 'block', 'service': self.service_name
|
||||
'chain': 'INPUT' if direction == 'ingress' else 'OUTPUT',
|
||||
'protocol': self.port[0],
|
||||
'jump': 'DROP',
|
||||
'destination_port': self.port[1],
|
||||
'action': 'insert',
|
||||
'state': 'present',
|
||||
},
|
||||
'become': 'yes',
|
||||
}
|
||||
|
@@ -12,6 +12,8 @@
|
||||
# limitations under the License.
|
||||
|
||||
from os_faults.drivers.services import process
|
||||
from os_faults.drivers import shared_schemas
|
||||
|
||||
|
||||
SALT_CALL = 'salt-call --local --retcode-passthrough '
|
||||
SALT_RESTART = SALT_CALL + 'service.restart {service}'
|
||||
@@ -34,13 +36,14 @@ class SaltService(process.ServiceAsProcess):
|
||||
args:
|
||||
salt_service: app
|
||||
grep: my_app
|
||||
port: ['tcp', 4242]
|
||||
port: ['tcp', 4242, 'egress']
|
||||
|
||||
parameters:
|
||||
|
||||
- **salt_service** - name of a service
|
||||
- **grep** - regexp for grep to find process PID
|
||||
- **port** - tuple with two values - protocol, port number (optional)
|
||||
- **port** - tuple with two or three values - protocol, port number,
|
||||
direction (optional)
|
||||
|
||||
"""
|
||||
|
||||
@@ -51,7 +54,7 @@ class SaltService(process.ServiceAsProcess):
|
||||
'properties': {
|
||||
'salt_service': {'type': 'string'},
|
||||
'grep': {'type': 'string'},
|
||||
'port': process.PORT_SCHEMA,
|
||||
'port': shared_schemas.PORT_SCHEMA,
|
||||
},
|
||||
'required': ['grep', 'salt_service'],
|
||||
'additionalProperties': False,
|
||||
|
@@ -33,13 +33,14 @@ class SystemService(process.ServiceAsProcess):
|
||||
args:
|
||||
service_name: app
|
||||
grep: my_app
|
||||
port: ['tcp', 4242]
|
||||
port: ['tcp', 4242, 'ingress']
|
||||
|
||||
parameters:
|
||||
|
||||
- **service_name** - name of a service
|
||||
- **grep** - regexp for grep to find process PID
|
||||
- **port** - tuple with two values - protocol, port number (optional)
|
||||
- **port** - tuple with two or three values - protocol, port number,
|
||||
direction (optional)
|
||||
"""
|
||||
NAME = 'system_service'
|
||||
DESCRIPTION = 'System Service (systemd, upstart, SysV, etc.)'
|
||||
|
@@ -17,9 +17,10 @@ PORT_SCHEMA = {
|
||||
'items': [
|
||||
{'enum': ['tcp', 'udp']},
|
||||
{'type': 'integer', 'minimum': 0, 'maximum': 65535},
|
||||
{'enum': ['egress', 'ingress']}
|
||||
],
|
||||
'minItems': 2,
|
||||
'maxItems': 2,
|
||||
'maxItems': 3,
|
||||
}
|
||||
|
||||
AUTH_SCHEMA = {
|
||||
|
Reference in New Issue
Block a user