Browse Source

Merge pull request #1 from magfest/master

Adds ability to limit or deny services and applications.
tags/v0.2.0
Niels Abspoel 6 years ago
parent
commit
b5ce18c853
No account linked to committer's email address
5 changed files with 195 additions and 42 deletions
  1. +26
    -1
      .kitchen.yml
  2. +74
    -30
      _states/ufw.py
  3. +35
    -2
      pillar.example
  4. +40
    -0
      test/integration/ufw/controls/ufw.rb
  5. +20
    -9
      ufw/init.sls

+ 26
- 1
.kitchen.yml View File

enabled: True enabled: True
settings: settings:
loglevel: 'low' loglevel: 'low'
applications:
MySQL:
comment: Allow MySQL
Postgresql:
limit: True
comment: Limit Postgresql
SSH223:
deny: True
comment: Deny Webscale SSH
'*':
deny: True
from_addr: 10.0.0.0/8
services: services:
'*':
deny: True
from_addr:
- 10.0.0.1
- 10.0.0.2
'22': '22':
protocol: tcp protocol: tcp
comment: Allow SSH
limit: True
comment: Limit SSH
'80':
protocol: tcp
deny: True
comment: Deny HTTP
'443':
protocol: tcp
comment: Allow HTTPS


platforms: platforms:
- name: <%= distrib %>-<%= codename %> - name: <%= distrib %>-<%= codename %>

+ 74
- 30
_states/ufw.py View File

return real_cmd return real_cmd




def _add_rule(method, name, app=None, interface=None, protocol=None,
from_addr=None, from_port=None, to_addr=None, to_port=None, comment=None):

if app and app.strip('"\' ') == '*':
app = None
if to_port and to_port.strip('"\' ') == '*':
to_port = None

rule = _as_rule(method, app=app, interface=interface, protocol=protocol,
from_addr=from_addr, from_port=from_port, to_addr=to_addr, to_port=to_port, comment=comment)

try:
out = __salt__['ufw.add_rule'](rule)
except (CommandExecutionError, CommandNotFoundError) as e:
if method.startswith('insert 1 deny') and "Invalid position '1'" in e.message:
# This is probably the first rule to be added, so try again without "insert 1"
return _add_rule('deny', name, app, interface, protocol, from_addr, from_port, to_addr, to_port, comment)
return _error(name, e.message)

adds = False
inserts = False
updates = False
for line in out.split('\n'):
if re.match('^Skipping', line):
return _unchanged(name, "{0} is already configured".format(name))
break
if re.match('^Rule(s)? added', line):
adds = True
break
if re.match('^Rule(s)? inserted', line):
inserts = True
break
if re.match('^Rule(s)? updated', line):
updates = True
break
if __opts__['test']:
return _test(name, "{0} would have been configured".format(name))
break

if method.startswith('insert 1 deny') and "Invalid position '1'" in line:
# This is probably the first rule to be added, so try again without "insert 1"
return _add_rule('deny', name, app, interface, protocol, from_addr, from_port, to_addr, to_port, comment)
return _error(name, line)

if adds:
return _changed(name, "{0} added".format(name), rule=rule)
elif inserts:
return _changed(name, "{0} inserted".format(name), rule=rule)
elif updates:
return _changed(name, "{0} updated".format(name), rule=rule)
else:
return _unchanged(name, "{0} was already configured".format(name))


def enabled(name, **kwargs): def enabled(name, **kwargs):
if __salt__['ufw.is_enabled'](): if __salt__['ufw.is_enabled']():
return _unchanged(name, "UFW is already enabled") return _unchanged(name, "UFW is already enabled")
return _unchanged(name, "{0} was already set to {1}".format(name, default)) return _unchanged(name, "{0} was already set to {1}".format(name, default))




def allowed(name, app=None, interface=None, protocol=None,
from_addr=None, from_port=None, to_addr=None, to_port=None, comment=None):
def deny(name, app=None, interface=None, protocol=None,
from_addr=None, from_port=None, to_addr=None, to_port=None, comment=None):


rule = _as_rule("allow", app=app, interface=interface, protocol=protocol,
from_addr=from_addr, from_port=from_port, to_addr=to_addr, to_port=to_port, comment=comment)
return _add_rule('insert 1 deny', name, app, interface, protocol, from_addr, from_port, to_addr, to_port, comment)


try:
out = __salt__['ufw.add_rule'](rule)
except (CommandExecutionError, CommandNotFoundError) as e:
return _error(name, e.message)


adds = False
updates = False
for line in out.split('\n'):
if re.match('^Skipping', line):
return _unchanged(name, "{0} is already configured".format(name))
break
if re.match('^Rule(s)? added', line):
adds = True
break
if re.match('^Rule(s)? updated', line):
updates = True
break
if __opts__['test']:
return _test(name, "{0} would have been configured".format(name))
break
return _error(name, line)
def limit(name, app=None, interface=None, protocol=None,
from_addr=None, from_port=None, to_addr=None, to_port=None, comment=None):


if adds:
return _changed(name, "{0} added".format(name), rule=rule)
elif updates:
return _changed(name, "{0} updated".format(name), rule=rule)
else:
return _unchanged(name, "{0} was already configured".format(name))
return _add_rule('limit', name, app, interface, protocol, from_addr, from_port, to_addr, to_port, comment)


def allow(name, app=None, interface=None, protocol=None,
from_addr=None, from_port=None, to_addr=None, to_port=None, comment=None):

return _add_rule('allow', name, app, interface, protocol, from_addr, from_port, to_addr, to_port, comment)


def allowed(name, app=None, interface=None, protocol=None,
from_addr=None, from_port=None, to_addr=None, to_port=None, comment=None):


"""
allow() is aliased to allowed() to maintain backwards compatibility.
"""
return allow(name, app, interface, protocol, from_addr, from_port, to_addr, to_port, comment)

+ 35
- 2
pillar.example View File

protocol: tcp protocol: tcp
comment: Mail relay comment: Mail relay


# Allow from an specific port, by number.
# Allow from a specific port, by number.
139: 139:
protocol: tcp protocol: tcp
comment: Netbios comment: Netbios


# Deny from a specific port, by number.
140:
protocol: tcp
deny: True

# Deny everything from a specific ip address
'*':
protocol: tcp
deny: True
from_addr: 10.0.0.1

# Deny everything from a multiple ip addresses
'*':
protocol: tcp
deny: True
from_addr:
- 10.0.0.2
- 10.0.0.3

# Limit a specific port, by number.
170:
limit: True
protocol: tcp
comment: Print service

# Allow from a range of ports, udp. # Allow from a range of ports, udp.
"10000:20000": "10000:20000":
protocol: udp protocol: udp
protocol: udp protocol: udp
comment: Game server and admin comment: Game server and admin


# Allow an application defined at /etc/ufw/applications.d/
# Allow applications defined at /etc/ufw/applications.d/
applications: applications:
OpenSSH: OpenSSH:
enabled: True enabled: True
comment: We are using fail2ban anyway comment: We are using fail2ban anyway


# Limit access to salt master
Saltmaster:
limit: True

# Deny access to Postgresql
Postgresql:
deny: True

# Allow all traffic in on the specified interface # Allow all traffic in on the specified interface
interfaces: interfaces:
eth1: eth1:

+ 40
- 0
test/integration/ufw/controls/ufw.rb View File

its('stdout') { should match /low/ } its('stdout') { should match /low/ }
end end


describe command('ufw status | grep MySQL') do
its('exit_status') { should eq 0 }
its('stdout') { should match /ALLOW/ }
end

describe command('ufw status | grep Postgresql') do
its('exit_status') { should eq 0 }
its('stdout') { should match /LIMIT/ }
end

describe command('ufw status | grep SSH223') do
its('exit_status') { should eq 0 }
its('stdout') { should match /DENY/ }
end

describe command('ufw status | grep 10.0.0.0') do
its('exit_status') { should eq 0 }
its('stdout') { should match /DENY/ }
end

describe command('ufw status | grep 22/tcp') do describe command('ufw status | grep 22/tcp') do
its('exit_status') { should eq 0 }
its('stdout') { should match /LIMIT/ }
end

describe command('ufw status | grep 80/tcp') do
its('exit_status') { should eq 0 }
its('stdout') { should match /DENY/ }
end

describe command('ufw status | grep 443/tcp') do
its('exit_status') { should eq 0 } its('exit_status') { should eq 0 }
its('stdout') { should match /ALLOW/ } its('stdout') { should match /ALLOW/ }
end end

describe command('ufw status | grep 10.0.0.1') do
its('exit_status') { should eq 0 }
its('stdout') { should match /DENY/ }
end

describe command('ufw status | grep 10.0.0.2') do
its('exit_status') { should eq 0 }
its('stdout') { should match /DENY/ }
end

+ 20
- 9
ufw/init.sls View File

# services # services
{%- for service_name, service_details in ufw.get('services', {}).items() %} {%- for service_name, service_details in ufw.get('services', {}).items() %}


{%- for from_addr in service_details.get('from_addr', [None]) %}
{%- set from_addr_raw = service_details.get('from_addr', [None]) -%}
{%- set from_addrs = [from_addr_raw] if from_addr_raw is string else from_addr_raw -%}


{%- for from_addr in from_addrs %}
{%- set protocol = service_details.get('protocol', None) %} {%- set protocol = service_details.get('protocol', None) %}
{%- set deny = service_details.get('deny', None) %}
{%- set limit = service_details.get('limit', None) %}
{%- set method = 'deny' if deny else ('limit' if limit else 'allow') -%}
{%- set from_port = service_details.get('from_port', None) %} {%- set from_port = service_details.get('from_port', None) %}
{%- set to_addr = service_details.get('to_addr', None) %} {%- set to_addr = service_details.get('to_addr', None) %}
{%- set comment = service_details.get('comment', None) %} {%- set comment = service_details.get('comment', None) %}


ufw-svc-{{service_name}}-{{from_addr}}:
ufw.allowed:
ufw-svc-{{method}}-{{service_name}}-{{from_addr}}:
ufw.{{method}}:
{%- if protocol != None %} {%- if protocol != None %}
- protocol: {{protocol}} - protocol: {{protocol}}
{%- endif %} {%- endif %}


# Applications # Applications
{%- for app_name, app_details in ufw.get('applications', {}).items() %} {%- for app_name, app_details in ufw.get('applications', {}).items() %}
{%- for from_addr in app_details.get('from_addr', [None]) %}

{%- set from_addr_raw = app_details.get('from_addr', [None]) -%}
{%- set from_addrs = [from_addr_raw] if from_addr_raw is string else from_addr_raw -%}

{%- for from_addr in from_addrs %}
{%- set deny = app_details.get('deny', None) %}
{%- set limit = app_details.get('limit', None) %}
{%- set method = 'deny' if deny else ('limit' if limit else 'allow') -%}
{%- set to_addr = app_details.get('to_addr', None) %} {%- set to_addr = app_details.get('to_addr', None) %}
{%- set comment = app_details.get('comment', None) %} {%- set comment = app_details.get('comment', None) %}


{%- if from_addr != None%} {%- if from_addr != None%}
ufw-app-{{app_name}}-{{from_addr}}:
ufw-app-{{method}}-{{app_name}}-{{from_addr}}:
{%- else %} {%- else %}
ufw-app-{{app_name}}:
ufw-app-{{method}}-{{app_name}}:
{%- endif %} {%- endif %}
ufw.allowed:
ufw.{{method}}:
- app: '"{{app_name}}"' - app: '"{{app_name}}"'
{%- if from_addr != None %} {%- if from_addr != None %}
- from_addr: {{from_addr}} - from_addr: {{from_addr}}


{%- endfor %} {%- endfor %}
{%- endfor %} {%- endfor %}
# Interfaces # Interfaces
{%- for interface_name, interface_details in ufw.get('interfaces', {}).items() %} {%- for interface_name, interface_details in ufw.get('interfaces', {}).items() %}
{%- set comment = interface_details.get('comment', None) %} {%- set comment = interface_details.get('comment', None) %}

Loading…
Cancel
Save