summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJason DeTiberus <jdetiber@redhat.com>2015-03-03 13:06:49 -0500
committerJason DeTiberus <jdetiber@redhat.com>2015-03-05 17:37:19 -0500
commit7c90cacef0f5cf61fb8ac3adb905507dd4247d84 (patch)
tree708aacc9b414cc8392927ecd1ab515a0faaa7f49
parent151f10b010651a49dfb4b46ca74e966be36b1279 (diff)
downloadopenshift-7c90cacef0f5cf61fb8ac3adb905507dd4247d84.tar.gz
openshift-7c90cacef0f5cf61fb8ac3adb905507dd4247d84.tar.bz2
openshift-7c90cacef0f5cf61fb8ac3adb905507dd4247d84.tar.xz
openshift-7c90cacef0f5cf61fb8ac3adb905507dd4247d84.zip
refactor firewall management into new role
- Add os_firewall role - Remove firewall settings from base_os, add wait task to os_firewall - Added a iptables firewall module for maintaining the following (in a mostly naive manner): - ensure the OPENSHIFT_ALLOW chain is defined - ensure that there is a jump rule in the INPUT chain for OPENSHIFT_ALLOW - adds or removes entries from the OPENSHIFT_ALLOW chain - issues '/usr/libexec/iptables/iptables.init save' when rules are changed - Limitations of iptables firewall module - only allows setting of ports/protocols to open - no testing on ipv6 support - made os_firewall a dependency of openshift_common - Hardcoded openshift_common to use iptables (through the vars directory) until upstream support is in place for firewalld
-rw-r--r--roles/base_os/tasks/main.yaml16
-rw-r--r--roles/openshift_common/meta/main.yml4
-rw-r--r--roles/openshift_common/tasks/firewall.yml34
-rw-r--r--roles/openshift_common/tasks/main.yml16
-rw-r--r--roles/openshift_common/vars/main.yml4
-rw-r--r--roles/os_firewall/README.md66
-rw-r--r--roles/os_firewall/defaults/main.yml2
-rw-r--r--roles/os_firewall/library/os_firewall_manage_iptables.py254
-rw-r--r--roles/os_firewall/meta/main.yml13
-rw-r--r--roles/os_firewall/tasks/firewall/firewalld.yml68
-rw-r--r--roles/os_firewall/tasks/firewall/iptables.yml53
-rw-r--r--roles/os_firewall/tasks/main.yml6
12 files changed, 477 insertions, 59 deletions
diff --git a/roles/base_os/tasks/main.yaml b/roles/base_os/tasks/main.yaml
index 51fe1e5b6..aad611f70 100644
--- a/roles/base_os/tasks/main.yaml
+++ b/roles/base_os/tasks/main.yaml
@@ -15,19 +15,3 @@
yum:
pkg: bash-completion
state: installed
-
-- name: Install firewalld
- yum:
- pkg: firewalld
- state: installed
-
-- name: start and enable firewalld service
- service:
- name: firewalld
- state: started
- enabled: yes
- register: result
-
-- name: need to pause here, otherwise the firewalld service starting can sometimes cause ssh to fail
- pause: seconds=10
- when: result | changed
diff --git a/roles/openshift_common/meta/main.yml b/roles/openshift_common/meta/main.yml
index 128da25b4..7dc4603d0 100644
--- a/roles/openshift_common/meta/main.yml
+++ b/roles/openshift_common/meta/main.yml
@@ -1,3 +1,4 @@
+---
galaxy_info:
author: Jason DeTiberus
description: OpenShift Common
@@ -10,4 +11,5 @@ galaxy_info:
- 7
categories:
- cloud
-dependencies: []
+dependencies:
+- { role: os_firewall }
diff --git a/roles/openshift_common/tasks/firewall.yml b/roles/openshift_common/tasks/firewall.yml
deleted file mode 100644
index 514466769..000000000
--- a/roles/openshift_common/tasks/firewall.yml
+++ /dev/null
@@ -1,34 +0,0 @@
----
-# TODO: Ansible 1.9 will eliminate the need for separate firewalld tasks for
-# enabling rules and making them permanent with the immediate flag
-- name: "Add firewalld allow rules"
- firewalld:
- port: "{{ item.port }}"
- permanent: false
- state: enabled
- with_items: allow
- when: allow is defined
-
-- name: "Persist firewalld allow rules"
- firewalld:
- port: "{{ item.port }}"
- permanent: true
- state: enabled
- with_items: allow
- when: allow is defined
-
-- name: "Remove firewalld allow rules"
- firewalld:
- port: "{{ item.port }}"
- permanent: false
- state: disabled
- with_items: deny
- when: deny is defined
-
-- name: "Persist removal of firewalld allow rules"
- firewalld:
- port: "{{ item.port }}"
- permanent: true
- state: disabled
- with_items: deny
- when: deny is defined
diff --git a/roles/openshift_common/tasks/main.yml b/roles/openshift_common/tasks/main.yml
index b94fca690..723bdd9fa 100644
--- a/roles/openshift_common/tasks/main.yml
+++ b/roles/openshift_common/tasks/main.yml
@@ -7,6 +7,14 @@
- name: Configure local facts file
file: path=/etc/ansible/facts.d/ state=directory mode=0750
+- name: Add KUBECONFIG to .bash_profile for user root
+ lineinfile:
+ dest: /root/.bash_profile
+ regexp: "KUBECONFIG="
+ line: "export KUBECONFIG=/var/lib/openshift/openshift.local.certificates/admin/.kubeconfig"
+ state: present
+ insertafter: EOF
+
- name: Set common OpenShift facts
include: set_facts.yml
facts:
@@ -19,11 +27,3 @@
- section: common
option: debug_level
value: "{{ openshift_debug_level }}"
-
-- name: Add KUBECONFIG to .bash_profile for user root
- lineinfile:
- dest: /root/.bash_profile
- regexp: "KUBECONFIG="
- line: "export KUBECONFIG=/var/lib/openshift/openshift.local.certificates/admin/.kubeconfig"
- state: present
- insertafter: EOF
diff --git a/roles/openshift_common/vars/main.yml b/roles/openshift_common/vars/main.yml
index c93898665..0855c0cc5 100644
--- a/roles/openshift_common/vars/main.yml
+++ b/roles/openshift_common/vars/main.yml
@@ -1,2 +1,6 @@
---
openshift_master_credentials_dir: /var/lib/openshift/openshift.local.certificates/admin/
+
+# TODO: Upstream kubernetes only supports iptables currently, if this changes,
+# then these variable should be moved to defaults
+openshift_use_firewalld: False
diff --git a/roles/os_firewall/README.md b/roles/os_firewall/README.md
new file mode 100644
index 000000000..fe6318184
--- /dev/null
+++ b/roles/os_firewall/README.md
@@ -0,0 +1,66 @@
+OS Firewall
+===========
+
+OS Firewall manages firewalld and iptables firewall settings for a minimal use
+case (Adding/Removing rules based on protocol and port number).
+
+Requirements
+------------
+
+None.
+
+Role Variables
+--------------
+
+| Name | Default | |
+|---------------------------|---------|----------------------------------------|
+| os_firewall_use_firewalld | True | If false, use iptables |
+| os_firewall_allow | [] | List of service,port mappings to allow |
+| os_firewall_deny | [] | List of service, port mappings to deny |
+
+Dependencies
+------------
+
+None.
+
+Example Playbook
+----------------
+
+Use iptables and open tcp ports 80 and 443:
+```
+---
+- hosts: servers
+ vars:
+ os_firewall_use_firewalld: false
+ os_firewall_allow:
+ - service: httpd
+ port: 80/tcp
+ - service: https
+ port: 443/tcp
+ roles:
+ - os_firewall
+```
+
+Use firewalld and open tcp port 443 and close previously open tcp port 80:
+```
+---
+- hosts: servers
+ vars:
+ os_firewall_allow:
+ - service: https
+ port: 443/tcp
+ os_firewall_deny:
+ - service: httpd
+ port: 80/tcp
+ roles:
+ - os_firewall
+```
+
+License
+-------
+
+ASL 2.0
+
+Author Information
+------------------
+Jason DeTiberus - jdetiber@redhat.com
diff --git a/roles/os_firewall/defaults/main.yml b/roles/os_firewall/defaults/main.yml
new file mode 100644
index 000000000..bcf1d9a34
--- /dev/null
+++ b/roles/os_firewall/defaults/main.yml
@@ -0,0 +1,2 @@
+---
+os_firewall_use_firewalld: True
diff --git a/roles/os_firewall/library/os_firewall_manage_iptables.py b/roles/os_firewall/library/os_firewall_manage_iptables.py
new file mode 100644
index 000000000..fef710055
--- /dev/null
+++ b/roles/os_firewall/library/os_firewall_manage_iptables.py
@@ -0,0 +1,254 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+from subprocess import call, check_output
+
+DOCUMENTATION = '''
+---
+module: os_firewall_manage_iptables
+short_description: This module manages iptables rules for a given chain
+author: Jason DeTiberus
+requirements: [ ]
+'''
+EXAMPLES = '''
+'''
+
+
+class IpTablesError(Exception):
+ def __init__(self, msg, cmd, exit_code, output):
+ self.msg = msg
+ self.cmd = cmd
+ self.exit_code = exit_code
+ self.output = output
+
+
+class IpTablesAddRuleError(IpTablesError):
+ pass
+
+
+class IpTablesRemoveRuleError(IpTablesError):
+ pass
+
+
+class IpTablesSaveError(IpTablesError):
+ pass
+
+
+class IpTablesCreateChainError(IpTablesError):
+ def __init__(self, chain, msg, cmd, exit_code, output):
+ super(IpTablesCreateChainError, self).__init__(msg, cmd, exit_code, output)
+ self.chain = chain
+
+
+class IpTablesCreateJumpRuleError(IpTablesError):
+ def __init__(self, chain, msg, cmd, exit_code, output):
+ super(IpTablesCreateJumpRuleError, self).__init__(msg, cmd, exit_code,
+ output)
+ self.chain = chain
+
+
+# TODO: impliment rollbacks for any events that where successful and an
+# exception was thrown later. for example, when the chain is created
+# successfully, but the add/remove rule fails.
+class IpTablesManager:
+ def __init__(self, module, ip_version, check_mode, chain):
+ self.module = module
+ self.ip_version = ip_version
+ self.check_mode = check_mode
+ self.chain = chain
+ self.cmd = self.gen_cmd()
+ self.save_cmd = self.gen_save_cmd()
+ self.output = []
+ self.changed = False
+
+ def save(self):
+ try:
+ self.output.append(check_output(self.save_cmd,
+ stderr=subprocess.STDOUT))
+ except subprocess.CalledProcessError as e:
+ raise IpTablesSaveError(
+ msg="Failed to save iptables rules",
+ cmd=e.cmd, exit_code=e.returncode, output=e.output)
+
+ def add_rule(self, port, proto):
+ rule = self.gen_rule(port, proto)
+ if not self.rule_exists(rule):
+ if not self.chain_exists():
+ self.create_chain()
+ if not self.jump_rule_exists():
+ self.create_jump_rule()
+
+ if self.check_mode:
+ self.changed = True
+ self.output.append("Create rule for %s %s" % (proto, port))
+ else:
+ cmd = self.cmd + ['-A'] + rule
+ try:
+ self.output.append(check_output(cmd))
+ self.changed = True
+ self.save()
+ except subprocess.CalledProcessError as e:
+ raise IpTablesCreateChainError(
+ chain=self.chain,
+ msg="Failed to create rule for "
+ "%s %s" % (self.proto, self.port),
+ cmd=e.cmd, exit_code=e.returncode,
+ output=e.output)
+
+ def remove_rule(self, port, proto):
+ rule = self.gen_rule(port, proto)
+ if self.rule_exists(rule):
+ if self.check_mode:
+ self.changed = True
+ self.output.append("Remove rule for %s %s" % (proto, port))
+ else:
+ cmd = self.cmd + ['-D'] + rule
+ try:
+ self.output.append(check_output(cmd))
+ self.changed = True
+ self.save()
+ except subprocess.CalledProcessError as e:
+ raise IpTablesRemoveChainError(
+ chain=self.chain,
+ msg="Failed to remove rule for %s %s" % (proto, port),
+ cmd=e.cmd, exit_code=e.returncode, output=e.output)
+
+ def rule_exists(self, rule):
+ check_cmd = self.cmd + ['-C'] + rule
+ return True if subprocess.call(check_cmd) == 0 else False
+
+ def gen_rule(self, port, proto):
+ return [self.chain, '-p', proto, '-m', 'state', '--state', 'NEW',
+ '-m', proto, '--dport', str(port), '-j', 'ACCEPT']
+
+ def create_jump_rule(self):
+ if self.check_mode:
+ self.changed = True
+ self.output.append("Create jump rule for chain %s" % self.chain)
+ else:
+ try:
+ cmd = self.cmd + ['-L', 'INPUT', '--line-numbers']
+ output = check_output(cmd, stderr=subprocess.STDOUT)
+
+ # break the input rules into rows and columns
+ input_rules = map(lambda s: s.split(), output.split('\n'))
+
+ # Find the last numbered rule
+ last_rule_num = None
+ last_rule_target = None
+ for rule in input_rules[:-1]:
+ if rule:
+ try:
+ last_rule_num = int(rule[0])
+ except ValueError:
+ continue
+ last_rule_target = rule[1]
+
+ # Raise an exception if we do not find a valid INPUT rule
+ if not last_rule_num or not last_rule_target:
+ raise IpTablesCreateJumpRuleError(
+ chain=self.chain,
+ msg="Failed to find existing INPUT rules",
+ cmd=None, exit_code=None, output=None)
+
+ # Naively assume that if the last row is a REJECT rule, then
+ # we can add insert our rule right before it, otherwise we
+ # assume that we can just append the rule.
+ if last_rule_target == 'REJECT':
+ # insert rule
+ cmd = self.cmd + ['-I', 'INPUT', str(last_rule_num)]
+ else:
+ # append rule
+ cmd = self.cmd + ['-A', 'INPUT']
+ cmd += ['-j', self.chain]
+ output = check_output(cmd, stderr=subprocess.STDOUT)
+ changed = True
+ self.output.append(output)
+ except subprocess.CalledProcessError as e:
+ if '--line-numbers' in e.cmd:
+ raise IpTablesCreateJumpRuleError(
+ chain=self.chain,
+ msg="Failed to query existing INPUT rules to "
+ "determine jump rule location",
+ cmd=e.cmd, exit_code=e.returncode,
+ output=e.output)
+ else:
+ raise IpTablesCreateJumpRuleError(
+ chain=self.chain,
+ msg="Failed to create jump rule for chain %s" %
+ self.chain,
+ cmd=e.cmd, exit_code=e.returncode,
+ output=e.output)
+
+ def create_chain(self):
+ if self.check_mode:
+ self.changed = True
+ self.output.append("Create chain %s" % self.chain)
+ else:
+ try:
+ cmd = self.cmd + ['-N', self.chain]
+ self.output.append(check_output(cmd,
+ stderr=subprocess.STDOUT))
+ self.changed = True
+ self.output.append("Successfully created chain %s" %
+ self.chain)
+ except subprocess.CalledProcessError as e:
+ raise IpTablesCreateChainError(
+ chain=self.chain,
+ msg="Failed to create chain: %s" % self.chain,
+ cmd=e.cmd, exit_code=e.returncode, output=e.output
+ )
+
+ def jump_rule_exists(self):
+ cmd = self.cmd + ['-C', 'INPUT', '-j', self.chain]
+ return True if subprocess.call(cmd) == 0 else False
+
+ def chain_exists(self):
+ cmd = self.cmd + ['-L', self.chain]
+ return True if subprocess.call(cmd) == 0 else False
+
+ def gen_cmd(self):
+ cmd = 'iptables' if self.ip_version == 'ipv4' else 'ip6tables'
+ return ["/usr/sbin/%s" % cmd]
+
+ def gen_save_cmd(self):
+ cmd = 'iptables' if self.ip_version == 'ipv4' else 'ip6tables'
+ return ['/usr/libexec/iptables/iptables.init', 'save']
+
+
+def main():
+ module = AnsibleModule(
+ argument_spec=dict(
+ name=dict(required=True),
+ action=dict(required=True, choices=['add', 'remove']),
+ protocol=dict(required=True, choices=['tcp', 'udp']),
+ port=dict(required=True, type='int'),
+ ip_version=dict(required=False, default='ipv4',
+ choices=['ipv4', 'ipv6']),
+ ),
+ supports_check_mode=True
+ )
+
+ action = module.params['action']
+ protocol = module.params['protocol']
+ port = module.params['port']
+ ip_version = module.params['ip_version']
+ chain = 'OS_FIREWALL_ALLOW'
+
+ iptables_manager = IpTablesManager(module, ip_version, module.check_mode, chain)
+
+ try:
+ if action == 'add':
+ iptables_manager.add_rule(port, protocol)
+ elif action == 'remove':
+ iptables_manager.remove_rule(port, protocol)
+ except IpTablesError as e:
+ module.fail_json(msg=e.msg)
+
+ return module.exit_json(changed=iptables_manager.changed,
+ output=iptables_manager.output)
+
+
+# import module snippets
+from ansible.module_utils.basic import *
+main()
diff --git a/roles/os_firewall/meta/main.yml b/roles/os_firewall/meta/main.yml
new file mode 100644
index 000000000..e431f531c
--- /dev/null
+++ b/roles/os_firewall/meta/main.yml
@@ -0,0 +1,13 @@
+galaxy_info:
+ author: Jason DeTiberus
+ description: os_firewall
+ company: Red Hat, Inc.
+ license: ASL 2.0
+ min_ansible_version: 1.7
+ platforms:
+ - name: EL
+ versions:
+ - 7
+ categories:
+ - system
+dependencies: []
diff --git a/roles/os_firewall/tasks/firewall/firewalld.yml b/roles/os_firewall/tasks/firewall/firewalld.yml
new file mode 100644
index 000000000..f6d5fe2eb
--- /dev/null
+++ b/roles/os_firewall/tasks/firewall/firewalld.yml
@@ -0,0 +1,68 @@
+---
+- name: Install firewalld packages
+ yum:
+ name: firewalld
+ state: present
+
+- name: Start and enable firewalld service
+ service:
+ name: firewalld
+ state: started
+ enabled: yes
+ register: result
+
+- name: need to pause here, otherwise the firewalld service starting can sometimes cause ssh to fail
+ pause: seconds=10
+ when: result | changed
+
+- name: Ensure iptables services are not enabled
+ service:
+ name: "{{ item }}"
+ state: stopped
+ enabled: no
+ with_items:
+ - iptables
+ - ip6tables
+
+- name: Mask iptables services
+ command: systemctl mask "{{ item }}"
+ register: result
+ failed_when: result.rc != 0
+ changed_when: False
+ with_items:
+ - iptables
+ - ip6tables
+
+# TODO: Ansible 1.9 will eliminate the need for separate firewalld tasks for
+# enabling rules and making them permanent with the immediate flag
+- name: Add firewalld allow rules
+ firewalld:
+ port: "{{ item.port }}"
+ permanent: false
+ state: enabled
+ with_items: allow
+ when: allow is defined
+
+- name: Persist firewalld allow rules
+ firewalld:
+ port: "{{ item.port }}"
+ permanent: true
+ state: enabled
+ with_items: allow
+ when: allow is defined
+
+- name: Remove firewalld allow rules
+ firewalld:
+ port: "{{ item.port }}"
+ permanent: false
+ state: disabled
+ with_items: deny
+ when: deny is defined
+
+- name: Persist removal of firewalld allow rules
+ firewalld:
+ port: "{{ item.port }}"
+ permanent: true
+ state: disabled
+ with_items: deny
+ when: deny is defined
diff --git a/roles/os_firewall/tasks/firewall/iptables.yml b/roles/os_firewall/tasks/firewall/iptables.yml
new file mode 100644
index 000000000..4f051c2bd
--- /dev/null
+++ b/roles/os_firewall/tasks/firewall/iptables.yml
@@ -0,0 +1,53 @@
+---
+- name: Install iptables packages
+ yum:
+ name: "{{ item }}"
+ state: present
+ with_items:
+ - iptables
+ - iptables-services
+
+- name: Start and enable iptables services
+ service:
+ name: "{{ os_firewall_svc }}"
+ state: started
+ enabled: yes
+ with_items:
+ - iptables
+ - ip6tables
+ register: result
+
+- name: need to pause here, otherwise the iptables service starting can sometimes cause ssh to fail
+ pause: seconds=10
+ when: result | changed
+
+- name: Ensure firewalld service is not enabled
+ service:
+ name: firewalld
+ state: stopped
+ enabled: no
+
+- name: Mask firewalld service
+ command: systemctl mask firewalld
+ register: result
+ failed_when: result.rc != 0
+ changed_when: False
+ ignore_errors: yes
+
+- name: Add iptables allow rules
+ os_firewall_manage_iptables:
+ name: "{{ item.service }}"
+ action: add
+ protocol: "{{ item.port.split('/')[1] }}"
+ port: "{{ item.port.split('/')[0] }}"
+ with_items: allow
+ when: allow is defined
+
+- name: Remove iptables rules
+ os_firewall_manage_iptables:
+ name: "{{ item.service }}"
+ action: remove
+ protocol: "{{ item.port.split('/')[1] }}"
+ port: "{{ item.port.split('/')[0] }}"
+ with_items: deny
+ when: deny is defined
diff --git a/roles/os_firewall/tasks/main.yml b/roles/os_firewall/tasks/main.yml
new file mode 100644
index 000000000..ad89ef97c
--- /dev/null
+++ b/roles/os_firewall/tasks/main.yml
@@ -0,0 +1,6 @@
+---
+- include: firewall/firewalld.yml
+ when: os_firewall_use_firewalld
+
+- include: firewall/iptables.yml
+ when: not os_firewall_use_firewalld