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:
Ilya Shakhat
2019-07-05 12:22:48 +02:00
parent bce5702bec
commit 82cb432398
6 changed files with 35 additions and 74 deletions

View File

@@ -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()

View File

@@ -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():

View File

@@ -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',
}

View File

@@ -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,

View File

@@ -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.)'

View File

@@ -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 = {