Explorar el Código

Feature (rhel7/httpd 2.4) : hardening apache and code refactoring (#251)

* Feature (rhel7/httpd 2.4) : hardening apache and code refactoring

* remove hard returns

* Add default Listen 80 in httpd.conf

In case there no vhosts defined in pillar httpd will listen on port 80.
 Without this default it will not start

* empty file autoindex.conf instead of deleting it

* explicit hardening items and references from CIS

* add #3.5 hardening rule

* explain CIS recommendations categories

* add dependencies before start service

* add recommendation #7.1 Install mod_ssl

* link in readme to hardening doc
pull/261/head
karim Hamza hace 5 años
padre
commit
9662e8b4ab
Se han modificado 17 ficheros con 1512 adiciones y 5 borrados
  1. +105
    -0
      Hardening.md
  2. +149
    -0
      README-ng.rst
  3. +5
    -2
      README.rst
  4. +430
    -0
      _modules/apache_directives.py
  5. +118
    -0
      apache/config-ng.sls
  6. +77
    -0
      apache/defaults/RedHat/defaults-apache-2.4.yaml
  7. +40
    -0
      apache/files/RedHat/apache-2.4-ng.config.jinja
  8. +15
    -0
      apache/files/RedHat/welcome.conf
  9. +151
    -0
      apache/hardening-values.yaml
  10. +67
    -0
      apache/hardening.sls
  11. +26
    -0
      apache/lib.sls
  12. +86
    -0
      apache/modules-ng.sls
  13. +2
    -1
      apache/osfamilymap.yaml
  14. +6
    -0
      apache/vhosts/vhost-ng.conf.jinja
  15. +110
    -0
      apache/vhosts/vhost-ng.sls
  16. +122
    -0
      pillar-ng.example.yaml
  17. +3
    -2
      pillar.example

+ 105
- 0
Hardening.md Ver fichero

@@ -0,0 +1,105 @@
# Hardening list
This formula enforce security recommandations from [CIS Benchmarks](https://www.cisecurity.org/cis-benchmarks/) website
From ***CIS_Apache_HTTP_Server_2.4_Benchmark_v1.4.pdf*** document
> A scoring status indicates whether compliance with the given recommendation impacts the assessed target's benchmark score.
> Items in [*level 2*] profile exhibit one or more of the following characteristics:
> - are intended for environments or use cases where security is paramount
> - acts as defense in depth measure
> - may negatively inhibit the utility or performance of the technology
In this formula we focus on (**Scored**) [*level* ***1***] items
## List of all items with their CIS references
## 2. Minimize Apache Modules
- [ ] 2.1 Enable Only Necessary Authentication and Authorization Modules (Not Scored)
- [X] 2.2 Enable the Log Config Module (**Scored**)
- [X] 2.3 Disable WebDAV Modules (**Scored**)
- [X] 2.4 Disable Status Module (**Scored**)
- [X] 2.5 Disable Autoindex Module (**Scored**)
- [ ] 2.6 Disable Proxy Modules (**Scored**)
- [X] 2.7 Disable User Directories Modules (**Scored**)
- [X] 2.8 Disable Info Module (**Scored**)
## 3. Principles, Permissions, and Ownership
- [X] 3.1 Run the Apache Web Server as a non-root user (**Scored**)
- [X] 3.2 Give the Apache User Account an Invalid Shell (**Scored**)
- [ ] 3.3 Lock the Apache User Account (**Scored**)
- [X] 3.4 Set Ownership on Apache Directories and Files (**Scored**)
- [X] 3.5 Set Group Id on Apache Directories and Files (**Scored**)
- [ ] 3.6 Restrict Other Write Access on Apache Directories and Files (**Scored**)
- [X] 3.7 Secure Core Dump Directory (**Scored**)
- [ ] 3.8 Secure the Lock File (**Scored**)
- [X] 3.9 Secure the Pid File (**Scored**)
- [X] 3.10 Secure the ScoreBoard File (**Scored**)
- [X] 3.11 Restrict Group Write Access for the Apache Directories and Files (**Scored**)
- [X] 3.12 Restrict Group Write Access for the Document Root Directories and Files (**Scored**)
## 4. Apache Access Control
- [X] 4.1 Deny Access to OS Root Directory (**Scored**)
- [ ] 4.2 Allow Appropriate Access to Web Content (Not Scored)
- [X] 4.3 Restrict Override for the OS Root Directory (**Scored**)
- [X] 4.4 Restrict Override for All Directories (**Scored**)
## 5. Minimize Features, Content and Options
- [X] 5.1 Restrict Options for the OS Root Directory (**Scored**)
- [X] 5.2 Restrict Options for the Web Root Directory (**Scored**)
- [X] 5.3 Minimize Options for Other Directories (**Scored**)
- [X] 5.4 Remove Default HTML Content (**Scored**)
- [X] 5.5 Remove Default CGI Content printenv (**Scored**)
- [X] 5.6 Remove Default CGI Content test-cgi (**Scored**)
- [X] 5.7 Limit HTTP Request Methods (**Scored**)
- [X] 5.8 Disable HTTP TRACE Method (**Scored**)
- [X] 5.9 Restrict HTTP Protocol Versions (**Scored**)
- [X] 5.10 Restrict Access to .ht* files (**Scored**)
- [ ] 5.11 Restrict File Extensions [*level 2*] (**Scored**)
- [ ] 5.12 Deny IP Address Based Requests [*level 2*] (**Scored**)
- [ ] 5.13 Restrict Listen Directive [*level 2*] (**Scored**)
- [ ] 5.14 Restrict Browser Frame Options [*level 2*] (**Scored**)
## 6. Operations - Logging, Monitoring and Maintenance
- [X] 6.1 Configure the Error Log (**Scored**)
- [ ] 6.2 Configure a Syslog Facility for Error Logging [*level 2*] (**Scored**)
- [X] 6.3 Configure the Access Log (**Scored**)
- [X] 6.4 Log Storage and Rotation (**Scored**)
- [ ] 6.5 Apply Applicable Patches (**Scored**)
- [ ] 6.6 Install and Enable ModSecurity [*level 2*] (**Scored**)
- [ ] 6.7 Install and Enable OWASP ModSecurity Core Rule Set [*level 2*] (**Scored**)
## 7. SSL/TLS Configuration
- [X] 7.1 Install mod_ssl and/or mod_nss (**Scored**)
- [ ] 7.2 Install a Valid Trusted Certificate (**Scored**)
- [ ] 7.3 Protect the Server's Private Key (**Scored**)
- [X] 7.4 Disable the SSL v3.0 Protocol (**Scored**)
- [ ] 7.5 Restrict Weak SSL/TLS Ciphers (**Scored**)
- [X] 7.6 Disable SSL Insecure Renegotiation (**Scored**)
- [X] 7.7 Ensure SSL Compression is not Enabled (**Scored**)
- [ ] 7.8 Restrict Medium Strength SSL/TLS Ciphers (**Scored**)
- [ ] 7.9 Disable the TLS v1.0 Protocol [*level 2*] (**Scored**)
- [ ] 7.10 Enable OCSP Stapling [*level 2*] (**Scored**)
- [ ] 7.11 Enable HTTP Strict Transport Security [*level 2*] (**Scored**)
## 8. Information Leakage
- [X] 8.1 Set ServerToken to 'Prod' (**Scored**)
- [X] 8.2 Set ServerSignature to 'Off' (**Scored**)
- [ ] 8.3 Information Leakage via Default Apache Content [*level 2*] (**Scored**)
- [ ] 8.4 Information Leakage via ETag [*level 2*] (**Scored**)
## 9. Denial of Service Mitigations
- [X] 9.1 Set TimeOut to 10 or less (**Scored**)
- [X] 9.2 Set the KeepAlive directive to On (**Scored**)
- [X] 9.3 Set MaxKeepAliveRequests to 100 or greater (**Scored**)
- [X] 9.4 Set KeepAliveTimeout Low to Mitigate Denial of Service (**Scored**)
- [X] 9.5 Set Timeout Limits for Request Headers (**Scored**)
- [X] 9.6 Set Timeout Limits for the Request Body (**Scored**)
## 10. Request Limits
- [ ] 10.1 Set the LimitRequestLine directive to 512 or less [*level 2*] (**Scored**)
- [ ] 10.2 Set the LimitRequestFields directive to 100 or less [*level 2*] (**Scored**)
- [ ] 10.3 Set the LimitRequestFieldsize directive to 1024 or less [*level 2*] (**Scored**)
- [ ] 10.4 Set the LimitRequestBody directive to 102400 or less [*level 2*] (**Scored**)
## 11. Enable SELinux to Restrict Apache Processes
- [ ] 11.1 Enable SELinux in Enforcing Mode [*level 2*] (**Scored**)
- [ ] 11.2 Run Apache Processes in the httpd_t Confined Context [*level 2*] (**Scored**)
- [ ] 11.3 Ensure the httpd_t Type is Not in Permissive Mode [*level 2*] (**Scored**)
- [ ] 11.4 Ensure Only the Necessary SELinux Booleans are Enabled [*level 2*] (Not Scored)
## 12. Enable AppArmor to Restrict Apache Processes
- [ ] 12.1 Enable the AppArmor Framework [*level 2*] (**Scored**)
- [ ] 12.2 Customize the Apache AppArmor Profile [*level 2*] (Not Scored)
- [ ] 12.3 Ensure Apache AppArmor Profile is in Enforce Mode [*level 2*] (**Scored**)

+ 149
- 0
README-ng.rst Ver fichero

@@ -0,0 +1,149 @@
======
apache
======
Formulas to set up and configure the Apache HTTP server.
This Formula uses the concepts of ``directive`` and ``container`` in pillars
* ``directive`` is an httpd directive https://httpd.apache.org/docs/2.4/en/mod/directives.html
* ``container`` is what described the `configuration sections` https://httpd.apache.org/docs/2.4/en/sections.html
see examples below for more explanation
Also it includes and enforce some hardening rules to prevent security issues
See `<Hardening.md>`_ and `<apache/hardening-values.yaml>`_.
.. note::
See the full `Salt Formulas installation and usage instructions
<http://docs.saltstack.com/en/latest/topics/development/conventions/formulas.html>`_.
Available states
================
.. contents::
:local:
``apache``
----------
Installs the Apache package and starts the service.
``apache.config-ng``
-----------------
Configures apache server.
The configuration is done by merging the pillar content with defaults
present in the state `<apache/defaults/RedHat/defaults-apache-2.4.yaml>`_
.. code:: yaml
apache:
server_apache_config:
directives:
- Timeout: 5
containers:
IfModule:
-
item: 'mime_module'
directives:
- AddType: 'application/x-font-ttf ttc ttf'
- AddType: 'application/x-font-opentype otf'
- AddType: 'application/x-font-woff woff2'
``apache.modules-ng``
------------------
Enables and disables Apache modules.
``apache.vhosts.vhost-ng``
--------------------------
Configures Apache name-based virtual hosts and creates virtual host directories using data from Pillar.
All necessary data must be provided in the pillar
Exceptions are :
* ``CustomLog`` default is ``/path/apache/log/ServerName-access.log combined``
* if ``Logformat`` is defined in pillar, ``CustomLog`` is enforced to ``/path/apache/log/ServerName-access.log Logformat``
* ``ErrorLog`` is enforced to ``/path/apache/log/ServerName-error.log``
Example Pillar:
Create two vhosts ``example.com.conf`` and ``test.example.com.conf``
.. code:: yaml
apache:
VirtualHost:
example.com: # <-- this is an id decalaration used in salt and default ServerName
item: '*:80'
directives:
- RewriteEngine: 'on'
- Header: 'set Access-Control-Allow-Methods GET,PUT,POST,DELETE,OPTIONS'
containers:
Location:
item: '/test.html'
directives:
- Require: 'all granted'
site_id_declaration:
item: '10.10.1.1:8080'
directives:
- ServerName: 'test.example.com'
- LogFormat: '"%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\" %{ms}T"'
Files produced by these pillars :
``example.com.conf``
.. code:: bash
<VirtualHost *:80>
ServerName example.com
CustomLog /var/log/httpd/example.com-access.log combined
ErrorLog /var/log/httpd/example.com-error.log
RewriteEngine on
Header set Access-Control-Allow-Methods GET,PUT,POST,DELETE,OPTIONS
<Location /test.html>
Require all granted
</Location>
</VirtualHost>
``test.example.com.conf``
.. code:: bash
<VirtualHost 10.10.1.1:8080>
ServerName test.example.com
CustomLog /var/log/httpd/test.example.com-access.log "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\" %{ms}T"
ErrorLog /var/log/httpd/test.example.com-error.log
</VirtualHost>
this will delete ``test.example.com.conf``
.. code:: yaml
apache:
VirtualHost:
test.example.com:
item: '10.10.1.1:8080'
absent: True # <-- delete test.example.com.conf
directives:
- ServerName: 'test.example.com'
``apache.uninstall``
----------
Stops the Apache service and uninstalls the package.

+ 5
- 2
README.rst Ver fichero

@@ -2,6 +2,9 @@
apache
======


.. note:: See `<README-ng.rst>`_ for new gen of the state.

Formulas to set up and configure the Apache HTTP server.

.. note::
@@ -185,14 +188,14 @@ Example Pillar:

When using the provided templates, one can use a space separated list
of interfaces to bind to. For example, to bind both IPv4 and IPv6:
.. code:: yaml

apache:
sites:
example.com:
interface: '1.2.3.4 [2001:abc:def:100::3]'
``apache.manage_security``
--------------------------


+ 430
- 0
_modules/apache_directives.py Ver fichero

@@ -0,0 +1,430 @@
# -*- coding: utf-8 -*-
'''
apache directives
:maintainer: "karim Hamza"
'''
from __future__ import absolute_import, unicode_literals
# Import python libs
import re
from copy import deepcopy
from salt.exceptions import CommandExecutionError
from salt.ext.six.moves import range
def _get_directive_values(directive, d_list):
'''
Returns list all values of directive
'''
values = [item.get(directive) for item in d_list if directive in item]
return values
def get_directive_single_value(directive, d_list, default=None):
'''
Returns single value of directive
default is returned if directive is absent from list
'''
values = _get_directive_values(directive, d_list)
try:
return values[0]
except IndexError:
if default is not None:
return default
error_msg = "invalid Pillar content - " \
+ directive + " - is not defined"
raise CommandExecutionError(error_msg)
def append_to_container_directives(directive, value, container):
'''
Append directive to directives list
'''
try:
container['directives'].append({directive: value})
except KeyError:
container['directives'] = []
container['directives'].append({directive: value})
return container
def _manage_directive_into_containers(directive,
value,
container,
container_name_target,
item,
enforce_value=False,
add_directive=True):
'''
Enforce value for directive into specific container
directive
directive label (name)
value
value to enforce
container
container to parse
container_name_target
container name target into directive/value have to be enforced
item
name of the item target
enforce_value : default=False
True: enforce value if directive exists, otherwise add it if add_directive=True
add_directive : default=True
Only if enforce_value=False add directive if it is not present
'''
for n_container, l_containers in container.get('containers', {}).items():
for idx, nested_container in enumerate(l_containers):
if (n_container == container_name_target
and nested_container['item'] == item):
if enforce_value:
container['containers'][n_container][idx] = \
enforce_directive_value(directive,
{'value': value, 'add_if_absent': add_directive},
n_container,
nested_container)
else:
container['containers'][n_container][idx] = \
append_to_container_directives(directive,
value,
nested_container)
container['containers'][n_container][idx] = \
_manage_directive_into_containers(directive,
value,
nested_container,
container_name_target,
item,
enforce_value,
add_directive)
return container
def set_vhost_logging_directives(container, servername, logdir):
'''
set value of CustomLog and LogFormat directives in vhost
'''
logformat = get_directive_single_value('LogFormat',
container.get('directives', []),
default='combined')
enforce_directive_value(
directive='CustomLog',
enforced_directive_data=
{'value': logdir + '/' + servername +'-access.log ' + logformat,
'add_if_absent': True},
container_name='VirtualHost',
container_data=container)
enforce_directive_value(
directive='ErrorLog',
enforced_directive_data=
{'value': logdir + '/' + servername +'-error.log ',
'add_if_absent': True},
container_name='VirtualHost',
container_data=container)
return container
def _container_merge_multiple_directives(container):
'''
append directives_multiple list into directives
'''
try:
container['directives'].extend(container.get('directives_multiple', []))
except KeyError:
container['directives'] = []
container['directives'] = container.get('directives_multiple', [])
container.pop('directives_multiple', None)
for sub_container_name, sub_containers_list in container.get('containers', {}).items():
for sub_idx, sub_container in enumerate(sub_containers_list):
container['containers'][sub_container_name][sub_idx] = \
_container_merge_multiple_directives(sub_container)
return container
def merge_container_with_additional_data(container_to_update,
container_to_import,
add_directive=True,
add_container=True):
'''
Merge containers usually to merge default values with pillar content
container_to_update
the default container into which put or modify values with pillar content
container_to_import
usually pillar content
add_directive : default=True
add directive if it is not present
add_container : default=True
add sub_container if it is absent in container_to_update
'''
merged_container = deepcopy(container_to_update)
multiple_directives_to_append = []
for mult_directive_item in container_to_update.get('directives_multiple', []):
for mult_directive, imp_value in mult_directive_item.items():
append_to_container_directives(mult_directive,
imp_value,
merged_container)
if mult_directive not in multiple_directives_to_append:
multiple_directives_to_append.append(mult_directive)
merged_container.pop('directives_multiple', None)
for p_directive_item in container_to_import.get('directives', []):
for p_directive, p_value in p_directive_item.items():
if p_directive in multiple_directives_to_append:
append_to_container_directives(p_directive,
p_value,
merged_container)
else:
merged_container = enforce_directive_value(
p_directive,
{'value': p_value, 'add_if_absent': add_directive},
'virtual_name_container',
merged_container)
# containers:
sub_containers_to_update = merged_container.get('containers', {})
sub_containers_to_import = container_to_import.get('containers', {})
if sub_containers_to_update and sub_containers_to_import:
# merge directives of sub containers
for container_name, u_container_list in sub_containers_to_update.items():
to_imp_containers = sub_containers_to_import.get(container_name, [])
for container_idx, to_upd_container_data in enumerate(u_container_list):
imp_items_containers = [container for container in to_imp_containers
if container['item'] == to_upd_container_data['item']]
for i_item_container in imp_items_containers:
merged_container['containers'][container_name][container_idx] = \
merge_container_with_additional_data(
merged_container['containers'][container_name][container_idx],
i_item_container,
add_directive)
if add_container:
# merge containers not present in default 'container_name' list
d_container_items = set([container.get('item') for container
in u_container_list])
p_container_items = set([container.get('item') for container
in to_imp_containers])
items_diff = (p_container_items - d_container_items)
for item in items_diff:
merged_container['containers'][container_name].extend(
[container for container in to_imp_containers if
container.get('item') == item])
if add_container:
# merge global containers not present in default
k_containers_diff = (set(sub_containers_to_import.keys())
- set(sub_containers_to_update.keys()))
for k_container in k_containers_diff:
merged_container['containers'][k_container] = {}
merged_container['containers'][k_container] = sub_containers_to_import[k_container]
elif not sub_containers_to_update \
and sub_containers_to_import \
and add_container:
merged_container['containers'] = {}
merged_container['containers'] = sub_containers_to_import
elif not sub_containers_to_import:
pass
# move directives_multiple into directives and delete directives_multiple
for container_name, containers_list in merged_container.get('containers', {}).items():
for container_idx, container_data in enumerate(containers_list):
merged_container['containers'][container_name][container_idx] = \
_container_merge_multiple_directives(container_data)
return merged_container
def enforce_security_directives_into_containers(container_to_secure,
secured_containers,
add_directive=True,
add_container=True):
'''
Merge secured containers into pillar content
container_to_secure
usually pillar content
secured_containers
content of hadened values
add_directive : default=True
add directive if it is not present
add_container : default=True
add sub_container if it is absent in container_to_secure
'''
i_secured_containers = {}
i_secured_containers['containers'] = secured_containers
container_to_secure = merge_container_with_additional_data(
container_to_secure,
i_secured_containers,
add_directive=add_directive,
add_container=add_container)
# search in (sub) nested containers and secure them
for secure_container_name, l_s_containers in secured_containers.items():
for s_container in l_s_containers:
# search into container_to_secure
secured_item = s_container.get('item')
for s_directive in s_container.get('directives', []):
for s_d_label, s_d_value in s_directive.items():
container_to_secure = _manage_directive_into_containers(
s_d_label,
s_d_value,
container_to_secure,
container_name_target=secure_container_name,
item=secured_item,
enforce_value=True,
add_directive=add_directive)
return container_to_secure
def _substitute_value(text, enforced_value):
'''
conditional replace in 'text' with regex and condition
text
string to process
enforced_value
dict :
match: regex to match
value: value to enforce
onlyif_pillar_is: condition on pillar content
regex_group_position: number of group to replace in regex
'''
def my_match_function(m_object):
return_value = ''.join([m_object.group(idx) for idx in range(1, position)
if m_object.group(idx) is not None])
if condition == 'greater':
return_value = return_value \
+ str(min(int(m_object.group(position)), int(enforced_value['value']))) \
+ ''.join([m_object.group(idx) for idx in range(position+1, m_object.lastindex+1) if m_object.group(idx) is not None])
elif condition == 'lower':
return_value = return_value \
+ str(max(int(m_object.group(position)), int(enforced_value['value']))) \
+ ''.join([m_object.group(idx) for idx in range(position+1, m_object.lastindex+1) if m_object.group(idx) is not None])
elif condition == 'different' and m_object.group(position) != str(enforced_value['value']):
return_value = return_value \
+ enforced_value['value'] \
+ ''.join([m_object.group(idx) for idx in range(position+1, m_object.lastindex+1) if m_object.group(idx) is not None])
else:
return_value = m_object.group(0)
return return_value
_pattern = re.compile(enforced_value.get('match', r'(\S+(\s+\S+)*)'), re.IGNORECASE)
condition = enforced_value.get('onlyif_pillar_is', 'different')
position = enforced_value.get('regex_group_position', 1)
value = _pattern.sub(my_match_function, str(text))
return value
def enforce_directive_value(directive,
enforced_directive_data,
container_name,
container_data):
'''
Enforce value of directive under conditions
directive
directive label (name)
enforced_directive_data
dict containning
value to put
condition (greater|lower|different)
regex match : default= r'(\\w+(\\s+\\w+)*)'
regex group position : default=1
container : enforce value only on the specified container
container_name
the name of httpd container
container_data
container to parse
'''
d_is_present = False
add_directive = enforced_directive_data.get('add_if_absent', False)
enforced_data_values = enforced_directive_data.get('values', [enforced_directive_data])
for idx_d, d_item in enumerate(container_data.get('directives', [])):
if directive in d_item:
d_is_present = True
for enforced_data_value in enforced_data_values:
if (not enforced_data_value.get('container', '')) \
or (enforced_data_value.get('container') == container_name):
container_data['directives'][idx_d][directive] = \
_substitute_value(container_data['directives'][idx_d][directive],
enforced_data_value)
if re.match(r'(\s*)?$', container_data['directives'][idx_d][directive]) is not None:
# delete directive from list in case of
# the value is empty after replacement
del container_data['directives'][idx_d]
break
if add_directive and not d_is_present \
and not enforced_directive_data.get('match', '') \
and not enforced_directive_data.get('values', ''):
append_to_container_directives(directive,
enforced_directive_data.get('value'),
container_data)
# directive is not added in subcontainers
enforced_directive_data['add_if_absent'] = False
for sub_container_name, sub_containers in \
container_data.get('containers', {}).items():
container_to_match = enforced_directive_data.get('container', sub_container_name)
if container_to_match == sub_container_name:
for idx, nested_container in enumerate(sub_containers):
container_data['containers'][sub_container_name][idx] = \
enforce_directive_value(directive,
enforced_directive_data,
sub_container_name,
nested_container)
return container_data
def remove_container(container_data,
container_name_to_remove,
item_name_to_remove):
'''
remove container_name/item from container_data
'''
for idx, container in enumerate(container_data.get('containers', {}).get(container_name_to_remove, [])):
if container.get('item') == item_name_to_remove:
del container_data['containers'][container_name_to_remove][idx]
for sub_container_name, sub_containers in \
container_data.get('containers', {}).items():
for sub_idx, sub_container in enumerate(sub_containers):
container_data['containers'][sub_container_name][sub_idx] = \
remove_container(sub_container, container_name_to_remove, item_name_to_remove)
return container_data

+ 118
- 0
apache/config-ng.sls Ver fichero

@@ -0,0 +1,118 @@
{% from "apache/map.jinja" import apache with context %}
{% import_yaml "apache/hardening-values.yaml" as hardening_values %}
{% import_yaml "apache/defaults/" ~ salt['grains.get']('os_family') ~ "/defaults-apache-" ~ apache.version ~ ".yaml" as global_defaults %}
include:
- apache
- apache.mod_ssl
- apache.hardening
{# merge defaults with pillar content #}
{% set pillar_server_config = salt['pillar.get']('apache:server_apache_config', {}) %}
{% set server_config = salt['apache_directives.merge_container_with_additional_data'](
global_defaults.server_apache_config,
pillar_server_config) %}
{# enforce directives values #}
{% for directive, directive_data in hardening_values.enforced_directives.items() %}
{% set server_config = salt['apache_directives.enforce_directive_value'](directive,
directive_data,
container_name='server',
container_data=server_config) %}
{% endfor %}
{# merge server config with hardened sections #}
{% set server_config = salt['apache_directives.enforce_security_directives_into_containers'](
server_config,
hardening_values.enforced_containers ) %}
{# remove containers #}
{% for container_name_to_remove, items_names in hardening_values.containers_to_remove.items() %}
{% for item_name in items_names %}
{% set server_config = salt['apache_directives.remove_container'](
server_config,
container_name_to_remove,
item_name) %}
{% endfor %}
{% endfor %}
{# add supplemental security directives in server configuration #}
{% for d_directive in hardening_values.server_supplemental_directives %}
{% for directive, value in d_directive.items() %}
{% set server_config = salt['apache_directives.append_to_container_directives'](
directive,
value,
server_config) %}
{% endfor %}
{% endfor %}
{% if grains['os_family']=="RedHat" %}
{{ apache.logdir }}:
file.directory:
- makedirs: True
- require:
- pkg: apache
- user: root
- group: {{ apache.group }}
- dir_mode: 750
- watch_in:
- module: apache-restart
- require_in:
- module: apache-restart
- module: apache-reload
- service: apache
{{ apache.configfile }}:
file.managed:
- template: jinja
- source:
- salt://apache/files/{{ salt['grains.get']('os_family') }}/apache-{{ apache.version }}-ng.config.jinja
- user: root
- group: root
- mode: 644
- require:
- pkg: apache
- watch_in:
- module: apache-restart
- require_in:
- module: apache-restart
- module: apache-reload
- service: apache
- context:
apache: {{ apache }}
server_config: {{ server_config | json }}
{{ apache.vhostdir_ng }}:
file.directory:
- makedirs: True
- require:
- pkg: apache
- user: root
- group: root
- dir_mode: 755
- file_mode: 644
- recurse:
- user
- group
- mode
- watch_in:
- module: apache-restart
- require_in:
- module: apache-restart
- module: apache-reload
- service: apache
/etc/httpd/conf.d/welcome.conf:
file.managed:
- source:
- salt://apache/files/{{ salt['grains.get']('os_family') }}/welcome.conf
- user: root
- group: root
- mode: 644
- require:
- pkg: apache
- watch_in:
- service: apache
{% endif %}

+ 77
- 0
apache/defaults/RedHat/defaults-apache-2.4.yaml Ver fichero

@@ -0,0 +1,77 @@
# defaults for httpd.conf
# The data structure is a little bit different with pillar structure
# ``directives_multiple`` list are directives that can be present multiple time in conf file
# if the same directive is present in pillar, it will be appended to the defaults ones
# there will be no replacement of values
server_apache_config:
directives:
- ServerRoot: '"/etc/httpd"'
- AllowEncodedSlashes: 'On'
- DocumentRoot: '"/var/www"'
- ServerAdmin: 'root@localhost'
- EnableSendfile: 'on'
- ErrorLog: '"/var/log/httpd/error.log"'
- LogLevel: 'warn core:info'
- AddDefaultCharset: 'UTF-8'
- ServerTokens: 'Prod'
containers:
Directory:
-
item: '/'
directives:
- AllowOverride: 'None'
- Require: 'all denied'
-
item: '/var/www'
directives:
- AllowOverride: 'None'
- Require: 'all granted'
- Options: 'Indexes FollowSymLinks'
-
item: '/var/www/cgi-bin'
directives:
- AllowOverride: 'None'
- Options: 'None'
- Require: 'all granted'
IfModule:
-
item: 'dir_module'
directives:
- DirectoryIndex: index.html
-
item: 'log_config_module'
directives:
- CustomLog: '"/var/log/httpd/access.log" combined'
directives_multiple: # <-- Theses directives are appended as it to pillar content
- LogFormat: '"%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined'
- LogFormat: '"%h %l %u %t \"%r\" %>s %b" common'
containers:
IfModule:
-
item: 'logio_module'
directives:
- LogFormat: '"%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio'
-
item: 'alias_module'
directives:
- ScriptAlias: '/cgi-bin/ "/var/www/cgi-bin/"'
-
item: 'mime_module'
directives:
- TypesConfig: '/etc/mime.types'
- AddOutputFilter: 'INCLUDES .shtml'
directives_multiple:
- AddType: 'application/x-compress .Z'
- AddType: 'application/x-gzip .gz .tgz'
- AddType: 'text/html .shtml'
-
item: 'mime_magic_module'
directives:
- MIMEMagicFile: 'conf/magic'
FilesMatch:
-
item: '"^\.ht"'
directives:
- Require: 'all denied'

+ 40
- 0
apache/files/RedHat/apache-2.4-ng.config.jinja Ver fichero

@@ -0,0 +1,40 @@
#
# This file is managed by Salt! Do not edit by hand!
#
{%- from "apache/map.jinja" import apache with context %}
{%- import_yaml "apache/hardening-values.yaml" as hardening_values %}
{%- from "apache/lib.sls" import directives_output, container_output with context %}
{%- set list_interfaces_ports = [] %}
{%- for name, vhost in salt['pillar.get']('apache:VirtualHost', {}).items() %}
{%- set items = vhost.item.split() %}
{%- for item in items if item not in list_interfaces_ports %}
{%- do list_interfaces_ports.append(item) %}
{%- endfor %}
{%- endfor %}
{%- for item in list_interfaces_ports %}
Listen {{ item }}
{% else %}
Listen *:80
{%- endfor %}
{{ directives_output(server_config, 0) }}
Include conf.modules.d/*.conf
User {{ apache.user }}
Group {{ apache.group }}
{%- for container_name, container_data_list in server_config.get('containers', {}).items() %}
{%- for container_data in container_data_list %}
{{ container_output(container_name, container_data) }}
{%- endfor %}
{%- endfor %}
IncludeOptional {{ apache.confdir }}/*.conf
{%- if apache.vhostdir_ng != apache.confdir %}
IncludeOptional {{ apache.vhostdir_ng }}/*.conf
{%- endif %}

+ 15
- 0
apache/files/RedHat/welcome.conf Ver fichero

@@ -0,0 +1,15 @@
#
# This file is managed by Salt! Do not edit by hand!
#
# httpd welcome file commented for security reasons
# This configuration file enables the default ""Welcome""
# page if there is no default index page present for
# the root URL. To disable the Welcome page, comment
# out all the lines below.
#
##<LocationMatch ""^/+$"">
## Options -Indexes
## ErrorDocument 403 /error/noindex.html
##</LocationMatch>

+ 151
- 0
apache/hardening-values.yaml Ver fichero

@@ -0,0 +1,151 @@
enforced_directives:
# httpd directives enforced in all configuration files and sections
# data structure :
# directive:
# value: numeric or string - value to enforce
# add_if_absent: False (default) - True -> add it to server configuration if it is absent from pillar
# onlyif_pillar_is: different (default) |greater|lower -> compare numeric values
# - greater : enforce value if the pillar content is > value
# - lower : enforce value if the pillar content is < value
# match : regex
# container : enforce only on the specified container
# regex_group_position : the position of the group to substitute in regex
# values : list of dict - for multiple replacements in the same directive
# Set TimeOut to 10 or less
Timeout:
value: 10
onlyif_pillar_is: 'greater'
add_if_absent: True
# Set Timeout Limits for Request Headers
RequestReadTimeout:
values:
-
match: '(?<=header=)(\d+-)?(\d+)'
value: 40
onlyif_pillar_is: 'greater'
regex_group_position: 2
-
match: '(?<=body=)(\d+-)?(\d+)'
value: 20
onlyif_pillar_is: 'greater'
regex_group_position: 2
# Disable the SSL v3.0 Protocol
SSLProtocol:
value: ''
match: '(?<!-)((\+)?SSLv3)'
regex_group_position: 1
# Minimize Options for Directories to NOT have a value of Includes
Options:
match: '(?<!-)((\+)?Includes)'
value: ''
regex_group_position: 1
container: 'Directory'
# Set the KeepAlive directive to On
KeepAlive:
value: 'On'
add_if_absent: True
# Set MaxKeepAliveRequests to 100 or greater
MaxKeepAliveRequests:
value: 100
onlyif_pillar_is: 'lower'
add_if_absent: True
# Set KeepAliveTimeout to 15 or less
KeepAliveTimeout:
value: 15
onlyif_pillar_is: 'greater'
add_if_absent: True
# Disable HTTP TRACE Method
TraceEnable:
value: 'off'
add_if_absent: True
# Set ServerSignature to 'Off'
ServerSignature:
value: 'off'
add_if_absent: True
# Set ServerToken to 'Prod'
ServerTokens:
value: 'Prod'
# Secure Core Dump Directory
CoreDumpDirectory:
value: '/var/log/httpd'
# Disable SSL Insecure Renegotiation
SSLInsecureRenegotiation:
value: 'off'
# Ensure SSL Compression is not Enabled
SSLCompression:
value: 'off'
# Restrict Override
AllowOverride:
value: 'None'
AllowOverrideList:
value: 'None'
PidFile:
value: '/etc/httpd/run/httpd.pid'
ScoreBoardFile:
value: '/var/run/apache_runtime_status'
SSLHonorCipherOrder:
value: 'On'
enforced_containers:
# httpd sections (containers) enforced in all configuration files and sections
Directory:
# Restrict Override for the OS Root Directory
-
item: '/'
directives:
- AllowOverride: 'None'
- Require: 'all denied'
- Options: 'None'
# Limit HTTP Request Methods
-
item: '/var/www'
directives:
- Options: 'None'
containers:
LimitExcept:
-
item: 'GET POST OPTIONS'
directives:
- Require: 'all denied'
FilesMatch:
# Restrict Access to .ht* files
-
item: '"^\.ht"'
directives:
- Require: 'all denied'
containers_to_remove:
# Remove Default HTML Content
Location:
- '/server-info'
- '/server-status'
- '/perl-status'
server_supplemental_directives:
# httpd directives added as it in httpd.conf
# Restrict HTTP protocol versions
- RewriteEngine: 'On'
- RewriteCond: '%{THE_REQUEST} !HTTP/1\.1$'
- RewriteRule: '.* - [F]'
vhost_supplemental_directives:
# httpd directives added as it in vhost config file
# Inherit server options
- RewriteEngine: 'On'
- RewriteOptions: 'Inherit'
modules:
# httpd modules: enforce enabled and disabled
enforce_disabled:
- "dav"
- "dav_fs"
- "status"
- "autoindex"
- "userdir"
- "info"
enforce_enabled:
- "log_config"
- "reqtimeout"
- "rewrite"

+ 67
- 0
apache/hardening.sls Ver fichero

@@ -0,0 +1,67 @@
{% from "apache/map.jinja" import apache with context %}
include:
- apache
nologin_shell_for_apache_user:
user.present:
- name: {{ apache.user }}
- shell: /sbin/nologin
- require:
- pkg: apache
- watch_in:
- module: apache-restart
- require_in:
- module: apache-restart
- module: apache-reload
- service: apache
remove_httpd_manual:
pkg.removed:
- name: httpd-manual
- require:
- pkg: apache
- watch_in:
- module: apache-restart
- require_in:
- module: apache-restart
- module: apache-reload
- service: apache
/etc/httpd/conf.d/autoindex.conf:
file.managed:
- contents: |
# File commented with Salt, Do NOT Edit
# Do NOT delete because it is contained in the rpm, so it wil re-created on the next upgrade
# It is emptied for hardening purpose
- require:
- pkg: apache
- watch_in:
- module: apache-restart
- require_in:
- module: apache-restart
- module: apache-reload
- service: apache
/etc/httpd/cgi-bin/printenv:
file.absent:
- require:
- pkg: apache
- watch_in:
- module: apache-restart
- require_in:
- module: apache-restart
- module: apache-reload
- service: apache
/etc/httpd/cgi-bin/test-cgi:
file.absent:
- require:
- pkg: apache
- watch_in:
- module: apache-restart
- require_in:
- module: apache-restart
- module: apache-reload
- service: apache

+ 26
- 0
apache/lib.sls Ver fichero

@@ -0,0 +1,26 @@
# macros for conf Files
{%- macro output_indented(mytext, indent_value) %}
{{ mytext | indent(indent_value, true) }}
{%- endmacro %}
{%- macro directives_output(container, col, default_keys = []) -%}
{%- for ordered_directive in container.get('directives', []) -%}
{%- for directive, value in ordered_directive.items() if directive not in default_keys| difference(['LogFormat']) -%}
{{ output_indented(directive + ' ' + value|string, col) }}
{%- endfor %}
{%- endfor %}
{%- endmacro %}
{%- macro container_output(container_name, container_data, col=0, default_directives = []) -%}
{%- set header_text = '<' ~ container_name ~ ' ' ~ container_data.item ~ '>' -%}
{{ output_indented(header_text, col) }}
{{ directives_output(container_data, col+4, default_directives ) }}
{%- for nested_container_name, nested_containers in container_data.get('containers', {}).items() %}
{%- for nested_container in nested_containers %}
{{ container_output(nested_container_name, nested_container, col+4) }}
{%- endfor %}
{%- endfor %}
{%- set footer_text = '</' ~ container_name ~ '>' -%}
{{ output_indented(footer_text, col) }}
{%- endmacro %}

+ 86
- 0
apache/modules-ng.sls Ver fichero

@@ -0,0 +1,86 @@
{%- import_yaml "apache/hardening-values.yaml" as hardening_values %}

{% if grains['os_family']=="Debian" %}

include:
- apache

{% for module in salt['pillar.get']('apache:modules:enabled', []) %}
a2enmod {{ module }}:
cmd.run:
- unless: ls /etc/apache2/mods-enabled/{{ module }}.load
- order: 225
- require:
- pkg: apache
- watch_in:
- module: apache-restart
{% endfor %}

{% for module in salt['pillar.get']('apache:modules:disabled', []) %}
a2dismod -f {{ module }}:
cmd.run:
- onlyif: ls /etc/apache2/mods-enabled/{{ module }}.load
- order: 225
- require:
- pkg: apache
- watch_in:
- module: apache-restart
{% endfor %}

{% elif grains['os_family']=="RedHat" %}

include:
- apache

{% for module in salt['pillar.get']('apache:modules:enabled', default=hardening_values.modules.enforce_enabled, merge=True) if module not in hardening_values.modules.enforce_disabled %}
find /etc/httpd/ -name '*.conf' -type f -exec sed -i -e 's/\(^#\)\(\s*LoadModule.{{ module }}_module\)/\2/g' {} \;:
cmd.run:
- unless: httpd -M 2> /dev/null | grep "[[:space:]]{{ module }}_module"
- order: 225
- require:
- pkg: apache
- watch_in:
- module: apache-restart
{% endfor %}

{% for module in salt['pillar.get']('apache:modules:disabled', default=hardening_values.modules.enforce_disabled, merge=True) if module not in hardening_values.modules.enforce_enabled %}
find /etc/httpd/ -name '*.conf' -type f -exec sed -i -e 's/\(^\s*LoadModule.{{ module }}_module\)/#\1/g' {} \;:
cmd.run:
- onlyif: httpd -M 2> /dev/null | grep "[[:space:]]{{ module }}_module"
- order: 225
- require:
- pkg: apache
- watch_in:
- module: apache-restart
{% endfor %}



{% elif salt['grains.get']('os_family') == 'Suse' or salt['grains.get']('os') == 'SUSE' %}

include:
- apache

{% for module in salt['pillar.get']('apache:modules:enabled', []) %}
a2enmod {{ module }}:
cmd.run:
- unless: egrep "^APACHE_MODULES=" /etc/sysconfig/apache2 | grep {{ module }}
- order: 225
- require:
- pkg: apache
- watch_in:
- module: apache-restart
{% endfor %}

{% for module in salt['pillar.get']('apache:modules:disabled', []) %}
a2dismod -f {{ module }}:
cmd.run:
- onlyif: egrep "^APACHE_MODULES=" /etc/sysconfig/apache2 | grep {{ module }}
- order: 225
- require:
- pkg: apache
- watch_in:
- module: apache-restart
{% endfor %}

{% endif %}

+ 2
- 1
apache/osfamilymap.yaml Ver fichero

@@ -45,6 +45,7 @@ RedHat:
mod_geoip_database: GeoIP

vhostdir: /etc/httpd/vhosts.d
vhostdir_ng: /etc/httpd/conf.d
confdir: /etc/httpd/conf.d
confext: .conf
default_site: default
@@ -111,7 +112,7 @@ FreeBSD:
modulesdir: /usr/local/etc/apache24/modules.d
global_document_root: /usr/local/www/apache24/data

confext:
confext:
default_site: default
default_site_ssl: default-ssl
logdir: /var/log/

+ 6
- 0
apache/vhosts/vhost-ng.conf.jinja Ver fichero

@@ -0,0 +1,6 @@
{% from "apache/lib.sls" import container_output with context %}
#
# This file is managed by Salt! Do not edit by hand!
#
{{ container_output('VirtualHost', vhost_data, col=0, default_directives = []) }}

+ 110
- 0
apache/vhosts/vhost-ng.sls Ver fichero

@@ -0,0 +1,110 @@
{% from "apache/map.jinja" import apache with context %}
{% import_yaml "apache/hardening-values.yaml" as hardening_values %}
include:
- apache
{% set vhosts = salt['pillar.get']('apache:VirtualHost', {}) %}
{% for virtual_name, vhost in vhosts.items() %}
{% set vhost_server_name = salt['apache_directives.get_directive_single_value'](
'ServerName',
vhost.get('directives'),
default=virtual_name) %}
{% set vhost = salt['apache_directives.enforce_directive_value'](directive='ServerName',
enforced_directive_data={'value': vhost_server_name,
'add_if_absent': True},
container_name='VirtualHost',
container_data=vhost) %}
{% set default_documentroot = '{0}/{1}'.format(apache.wwwdir, vhost_server_name) %}
{% set documentroot = salt['apache_directives.get_directive_single_value'](
'DocumentRoot',
vhost.get('directives'),
default=default_documentroot) %}
{% set vhost = salt['apache_directives.set_vhost_logging_directives'](vhost,
vhost_server_name,
apache.logdir) %}
# enforce directives values #
{% for directive, directive_data in hardening_values.enforced_directives.items() %}
{% if 'add_if_absent' in directive_data %}
{% do directive_data.update({'add_if_absent': False}) %}
{% endif %}
{% set vhost = salt['apache_directives.enforce_directive_value'](directive,
directive_data,
container_name='VirtualHost',
container_data=vhost) %}
{% endfor %}
# merge vhost config with hardened sections #
{% set vhost = salt['apache_directives.enforce_security_directives_into_containers'](
vhost,
hardening_values.enforced_containers,
add_container=False ) %}
# remove containers #
{% for container_name_to_remove, items_names in hardening_values.containers_to_remove.items() %}
{% for item_name in items_names %}
{% set vhost = salt['apache_directives.remove_container'](
vhost,
container_name_to_remove,
item_name) %}
{% endfor %}
{% endfor %}
# add supplemental security directives in vhost configuration #
{% for d_directive in hardening_values.vhost_supplemental_directives %}
{% for directive, value in d_directive.items() %}
{% set vhost = salt['apache_directives.append_to_container_directives'](
directive,
value,
vhost) %}
{% endfor %}
{% endfor %}
{% if vhost.get('absent', False) %}
{{ vhost_server_name }}:
file.absent:
- name: {{ apache.vhostdir_ng }}/{{ vhost_server_name }}{{ apache.confext }}
- require:
- pkg: apache
- watch_in:
- module: apache-reload
- require_in:
- module: apache-restart
- module: apache-reload
- service: apache
{% else %}
{{ vhost_server_name }}:
file.managed:
- name: {{ apache.vhostdir_ng }}/{{ vhost_server_name }}{{ apache.confext }}
- source: 'salt://apache/vhosts/vhost-ng.conf.jinja'
- template: 'jinja'
- user: root
- group: root
- mode: 644
- context:
vhost_data: {{ vhost|json }}
- require:
- pkg: apache
- watch_in:
- module: apache-reload
- require_in:
- module: apache-restart
- module: apache-reload
- service: apache
{{ documentroot }}-documentroot:
file.directory:
- name: {{ documentroot }}
- makedirs: True
- allow_symlink: True
{% endif %}
{% endfor %}

+ 122
- 0
pillar-ng.example.yaml Ver fichero

@@ -0,0 +1,122 @@
# server configuration and any vhost configuration have the same data structure
# This data structure is similar to below :
#
# directives: # list of top level directives/values
# - directive_1: value_1
# - directive_2: value_2
# - directive_3: value_3
# containers: # any type of httpd container
# container_name_1: # Files|Directory|DirectoryMatch|Proxy|location|locationMatch ...
# -
# item: 'path/to/1' # label, path or whatever that container applies to
# directives: # list of directives into this container
# - directive_1: value_1
# ...
# -
# item: '/path/to/2'
# direcives:
# - ...
# containers: # nested containers in /path/to/2
# nested_c_1:
# - item: '...'
# directives:
# - ...
# container_name_2:
# -
# item: '...'
# ...
# ``apache`` formula configuration:
apache:
# By default apache restart/reload states run (false skips)
manage_service_states: True
# lookup section overrides ``map.jinja`` values
lookup:
server: apache2
service: apache2
user: some_system_user
group: some_system_group
vhostdir: /etc/apache2/sites-available
confdir: /etc/apache2/conf.d
confext: .conf
logdir: /var/log/apache2
wwwdir: /srv/apache2
# apache version (generally '2.2' or '2.4')
version: '2.2'
# ``apache.mod_wsgi`` formula additional configuration:
mod_wsgi: mod_wsgi
# global (server) apache directives
server_apache_config: # this content will populate httpd.conf
directives:
- AllowEncodedSlashes: 'On'
- Timeout: 5
containers:
IfModule:
-
item: 'mime_module'
directives:
- AddType: 'application/x-font-ttf ttc ttf'
- AddType: 'application/x-font-opentype otf'
- AddType: 'application/x-font-woff woff2'
# ``apache.vhosts.vhost-ng`` formula additional configuration:
VirtualHost:
example.com: # <-- site_name : can be the real ServerName or a virtual name
item: '*:8080' # simple example
directives:
- ServerName: 'example.com' # if not defined default is site_name
- ServerAdmin: 'webmaster@example.com'
- DocumentRoot: '/path/to/www/dir/example.com'
- LogLevel: 'warn'
containers:
Location:
-
item: '/test.html'
directives:
- Require: 'all granted'
my_reverse_proxy: # example with a virtual site_name
item: '*:80' # vhost with proxypass
directives:
- ServerName: 'rp-example.com'
- ServerAdmin: 'webmaster@example.com'
- DocumentRoot: '/path/to/www/dir/rp-example.com'
- LogLevel: 'warn'
- ProxyPass: '/ balancer://cluster_1'
- ProxyPassReverse: '/ balancer://cluster_1'
- ProxyPreserveHost: 'On'
containers:
Proxy:
-
item: 'balancer://cluster_1'
directives:
- BalancerMember: 'http://my_backend_1:8081 route=backend-1-8081 timeout=240 retry=120'
- BalancerMember: 'http://my_backend_2:8081 route=backend-2-8081 timeout=240 retry=120'
- ProxySet: 'stickysession=JSESSIONID|jsessionid nofailover=off maxattempts=1'
unused_vhost:
item: '*:80'
absent: True # Delete this vhost
directives:
- ServerName: 'to-delete-example.com'
- ServerAdmin: 'webmaster@example.com'
- DocumentRoot: '/path/to/www/dir/to-delete-example.com'
- LogLevel: 'warn'
containers:
Location:
-
item: '/test.html'
directives:
- Require: 'all granted'
modules:
enabled: # List modules to enable
- ldap
- ssl
disabled: # List modules to disable
- rewrite

+ 3
- 2
pillar.example Ver fichero

@@ -1,3 +1,5 @@
# see ``pillar-ng.example.yaml`` for new gen pillar

# ``apache`` formula configuration:
apache:

@@ -98,7 +100,7 @@ apache:

redirectmatch.com:
# Use RedirectMatch Directive https://httpd.apache.org/docs/2.4/fr/mod/mod_alias.html#redirectmatch
# Require module mod_alias
# Require module mod_alias
enabled: True
template_file: salt://apache/vhosts/redirect.tmpl
ServerName: www.redirectmatch.com
@@ -368,4 +370,3 @@ apache:
SSLProtocol: all -SSLv2 -SSLv3 -TLSv1
SSLHonorCipherOrder: On
SSLOptions: "+StrictRequire"


Cargando…
Cancelar
Guardar