Adds ability to limit or deny services and applications.tags/v0.2.0
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 %> |
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) |
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: |
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 |
# 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) %} |