├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ └── release.yml ├── .github_changelog_generator ├── .gitignore ├── .kitchen.vagrant.yml ├── .kitchen.yml ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Gemfile ├── README.md ├── Rakefile ├── ansible.cfg ├── defaults └── main.yml ├── handlers └── main.yml ├── kitchen_vagrant_block.rb ├── meta └── main.yml ├── requirements.yml ├── tasks └── main.yml ├── templates └── hardening.conf.j2 ├── tests ├── official-nginx-role-debian.yml ├── official-nginx-role-redhat.yml └── test.yml └── vars └── main.yml /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | **Describe the bug** 8 | A clear and concise description of what the bug is. 9 | 10 | **Expected behavior** 11 | A clear and concise description of what you expected to happen. 12 | 13 | **Actual behavior** 14 | 15 | ```paste below 16 | 17 | ``` 18 | **Example Playbook** 19 | 20 | ```paste below 21 | 22 | ``` 23 | 24 | **OS / Environment** 25 | 26 | 27 | **Ansible Version** 28 | 29 | ```paste below 30 | 31 | ``` 32 | 33 | **Role Version** 34 | 35 | ```paste below 36 | 37 | ``` 38 | 39 | **Additional context** 40 | Add any other context about the problem here. 41 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | **Is your feature request related to a problem? Please describe.** 8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 9 | 10 | **Describe the solution you'd like** 11 | A clear and concise description of what you want to happen. 12 | 13 | **Describe alternatives you've considered** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: New release 3 | 4 | on: # yamllint disable-line rule:truthy 5 | push: 6 | branches: 7 | - master 8 | 9 | jobs: 10 | generate_changelog: 11 | runs-on: ubuntu-latest 12 | name: create release draft 13 | steps: 14 | - uses: actions/checkout@v1 15 | 16 | - name: 'Get Previous tag' 17 | id: previoustag 18 | uses: "WyriHaximus/github-action-get-previous-tag@master" 19 | env: 20 | GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" 21 | 22 | - name: calculate next version 23 | id: version 24 | uses: patrickjahns/version-drafter-action@v1 25 | env: 26 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 27 | 28 | - name: Generate changelog 29 | uses: charmixer/auto-changelog-action@8095796 30 | with: 31 | token: ${{ secrets.GITHUB_TOKEN }} 32 | future_release: ${{ steps.version.outputs.next-version }} 33 | 34 | - name: Generate changelog for the release 35 | uses: charmixer/auto-changelog-action@8095796 36 | with: 37 | token: ${{ secrets.GITHUB_TOKEN }} 38 | since_tag: ${{ steps.previoustag.outputs.tag }} 39 | future_release: ${{ steps.version.outputs.next-version }} 40 | output: CHANGELOGRELEASE.md 41 | 42 | - name: push changelog 43 | uses: github-actions-x/commit@v2.6 44 | with: 45 | github-token: ${{ secrets.GITHUB_TOKEN }} 46 | push-branch: 'master' 47 | commit-message: 'update changelog' 48 | force-add: 'true' 49 | files: CHANGELOG.md 50 | name: dev-sec CI 51 | email: hello@dev-sec.io 52 | 53 | - name: Read CHANGELOG.md 54 | id: package 55 | uses: juliangruber/read-file-action@v1 56 | with: 57 | path: ./CHANGELOGRELEASE.md 58 | 59 | - name: Create Release draft 60 | id: create_release 61 | uses: actions/create-release@v1 62 | env: 63 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token 64 | with: 65 | release_name: ${{ steps.version.outputs.next-version }} 66 | tag_name: ${{ steps.version.outputs.next-version }} 67 | body: | 68 | ${{ steps.package.outputs.content }} 69 | draft: true 70 | -------------------------------------------------------------------------------- /.github_changelog_generator: -------------------------------------------------------------------------------- 1 | unreleased=false 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .kitchen 2 | hosts 3 | Gemfile.lock 4 | -------------------------------------------------------------------------------- /.kitchen.vagrant.yml: -------------------------------------------------------------------------------- 1 | --- 2 | driver: 3 | name: vagrant 4 | driver_config: 5 | http_proxy: <%= ENV['http_proxy'] || nil %> 6 | https_proxy: <%= ENV['https_proxy'] || nil %> 7 | vagrantfiles: 8 | - kitchen_vagrant_block.rb 9 | 10 | provisioner: 11 | name: ansible_playbook 12 | hosts: all 13 | require_ansible_repo: false 14 | require_ansible_omnibus: true 15 | require_chef_for_busser: false 16 | require_ruby_for_busser: false 17 | ansible_verbose: true 18 | roles_path: ../ansible-nginx-hardening/ 19 | requirements_path: requirements.yml 20 | playbook: default.yml 21 | http_proxy: <%= ENV['http_proxy'] || nil %> 22 | https_proxy: <%= ENV['https_proxy'] || nil %> 23 | 24 | transport: 25 | max_ssh_sessions: 5 26 | 27 | platforms: 28 | - name: ubuntu-16.04 29 | driver_config: 30 | box: bento/ubuntu-16.04 31 | - name: ubuntu-18.04 32 | driver_config: 33 | box: bento/ubuntu-18.04 34 | - name: centos-6 35 | driver_config: 36 | box: bento/centos-6 37 | - name: centos-7 38 | driver_config: 39 | box: bento/centos-7 40 | - name: oracle-6 41 | driver_config: 42 | box: bento/oracle-6 43 | - name: oracle-7 44 | driver_config: 45 | box: bento/oracle-7 46 | - name: debian-9 47 | driver_config: 48 | box: bento/debian-9 49 | - name: debian-10 50 | driver_config: 51 | box: bento/debian-10 52 | - name: amazon 53 | driver_config: 54 | box: bento/amazonlinux-2 55 | 56 | verifier: 57 | name: inspec 58 | sudo: true 59 | inspec_tests: 60 | - https://github.com/dev-sec/nginx-baseline/ 61 | 62 | suites: 63 | - name: nginx 64 | -------------------------------------------------------------------------------- /.kitchen.yml: -------------------------------------------------------------------------------- 1 | --- 2 | driver: 3 | name: docker 4 | use_sudo: false 5 | privileged: true 6 | http_proxy: <%= ENV['http_proxy'] || nil %> 7 | https_proxy: <%= ENV['https_proxy'] || nil %> 8 | 9 | transport: 10 | max_ssh_sessions: 5 11 | 12 | provisioner: 13 | name: ansible_playbook 14 | hosts: all 15 | require_ansible_repo: false 16 | require_chef_for_busser: false 17 | require_ruby_for_busser: false 18 | ansible_verbose: true 19 | ansible_diff: true 20 | 21 | roles_path: ../ansible-nginx-hardening/ 22 | http_proxy: <%= ENV['http_proxy'] || nil %> 23 | https_proxy: <%= ENV['https_proxy'] || nil %> 24 | playbook: tests/test.yml 25 | requirements_path: requirements.yml 26 | galaxy_ignore_certs: true 27 | 28 | platforms: 29 | - name: centos6-ansible-latest 30 | driver: 31 | image: rndmh3ro/docker-centos6-ansible:latest 32 | platform: centos 33 | - name: centos7-ansible-latest 34 | driver: 35 | image: rndmh3ro/docker-centos7-ansible:latest 36 | platform: centos 37 | run_command: /sbin/init 38 | provision_command: 39 | - sed -i 's/UsePAM yes/UsePAM no/g' /etc/ssh/sshd_config 40 | - systemctl enable sshd.service 41 | - name: oracle6-ansible-latest 42 | driver: 43 | image: rndmh3ro/docker-oracle6-ansible:latest 44 | platform: centos 45 | - name: oracle7-ansible-latest 46 | driver: 47 | image: rndmh3ro/docker-oracle7-ansible:latest 48 | run_command: /sbin/init 49 | platform: centos 50 | provision_command: 51 | - sed -i 's/UsePAM yes/UsePAM no/g' /etc/ssh/sshd_config 52 | - systemctl enable sshd.service 53 | - name: ubuntu1604-ansible-latest 54 | driver: 55 | image: rndmh3ro/docker-ubuntu1604-ansible:latest 56 | platform: ubuntu 57 | run_command: /sbin/init 58 | provision_command: 59 | - systemctl enable ssh.service 60 | - name: ubuntu1804-ansible-latest 61 | driver: 62 | image: rndmh3ro/docker-ubuntu1804-ansible:latest 63 | platform: ubuntu 64 | run_command: /sbin/init 65 | provision_command: 66 | - systemctl enable ssh.service 67 | - name: debian9-ansible-latest 68 | driver: 69 | image: rndmh3ro/docker-debian9-ansible:latest 70 | platform: debian 71 | run_command: /sbin/init 72 | provision_command: 73 | - apt install -y systemd-sysv 74 | - systemctl enable ssh.service 75 | - name: debian10-ansible-latest 76 | driver: 77 | image: rndmh3ro/docker-debian10-ansible:latest 78 | platform: debian 79 | run_command: /sbin/init 80 | provision_command: 81 | - apt install -y systemd-sysv 82 | - systemctl enable ssh.service 83 | - name: amazon-ansible-latest 84 | driver: 85 | image: rndmh3ro/docker-amazon-ansible:latest 86 | platform: centos 87 | run_command: /sbin/init 88 | provision_command: 89 | - sed -i 's/UsePAM yes/UsePAM no/g' /etc/ssh/sshd_config 90 | - systemctl enable sshd.service 91 | 92 | verifier: 93 | name: inspec 94 | sudo: true 95 | inspec_tests: 96 | - https://github.com/dev-sec/nginx-baseline 97 | controls: 98 | - nginx-01 99 | - nginx-02 100 | - nginx-03 101 | - nginx-04 102 | - nginx-05 103 | - nginx-06 104 | - nginx-07 105 | - nginx-08 106 | - nginx-09 107 | - nginx-10 108 | - nginx-12 109 | - nginx-13 110 | - nginx-14 111 | - nginx-15 112 | - nginx-17 113 | 114 | suites: 115 | - name: nginx 116 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | services: docker 3 | 4 | env: 5 | - distro: centos6 6 | version: latest 7 | init: /sbin/init 8 | test_playbook: test.yml 9 | 10 | - distro: centos7 11 | init: /lib/systemd/systemd 12 | run_opts: "--privileged --volume=/sys/fs/cgroup:/sys/fs/cgroup:ro" 13 | version: latest 14 | test_playbook: test.yml 15 | 16 | - distro: oracle6 17 | version: latest 18 | init: /sbin/init 19 | test_playbook: test.yml 20 | 21 | - distro: ubuntu1604 22 | version: latest 23 | init: /lib/systemd/systemd 24 | run_opts: "--privileged --volume=/sys/fs/cgroup:/sys/fs/cgroup:ro" 25 | test_playbook: test.yml 26 | 27 | - distro: ubuntu1804 28 | version: latest 29 | init: /lib/systemd/systemd 30 | run_opts: "--privileged --volume=/sys/fs/cgroup:/sys/fs/cgroup:ro" 31 | test_playbook: test.yml 32 | 33 | - distro: debian9 34 | version: latest 35 | init: /lib/systemd/systemd 36 | run_opts: "--privileged --volume=/sys/fs/cgroup:/sys/fs/cgroup:ro" 37 | test_playbook: test.yml 38 | 39 | - distro: debian10 40 | version: latest 41 | init: /lib/systemd/systemd 42 | run_opts: "--privileged --volume=/sys/fs/cgroup:/sys/fs/cgroup:ro" 43 | test_playbook: test.yml 44 | 45 | # - distro: amazon 46 | # init: /lib/systemd/systemd 47 | # version: latest 48 | # run_opts: "--privileged --volume=/sys/fs/cgroup:/sys/fs/cgroup:ro" 49 | # test_playbook: test.yml 50 | # 51 | # - distro: fedora 52 | # init: /lib/systemd/systemd 53 | # version: latest 54 | # run_opts: "--privileged --volume=/sys/fs/cgroup:/sys/fs/cgroup:ro" 55 | # test_playbook: test.yml 56 | 57 | - distro: centos6 58 | version: latest 59 | init: /sbin/init 60 | test_playbook: official-nginx-role-redhat.yml 61 | 62 | - distro: centos7 63 | init: /lib/systemd/systemd 64 | container_id: $(mktemp) 65 | run_opts: "--privileged --volume=/sys/fs/cgroup:/sys/fs/cgroup:ro" 66 | version: latest 67 | test_playbook: official-nginx-role-redhat.yml 68 | 69 | - distro: oracle6 70 | version: latest 71 | init: /sbin/init 72 | test_playbook: official-nginx-role-redhat.yml 73 | 74 | - distro: ubuntu1604 75 | version: latest 76 | init: /lib/systemd/systemd 77 | run_opts: "--privileged --volume=/sys/fs/cgroup:/sys/fs/cgroup:ro" 78 | test_playbook: official-nginx-role-debian.yml 79 | 80 | - distro: ubuntu1804 81 | version: latest 82 | init: /lib/systemd/systemd 83 | run_opts: "--privileged --volume=/sys/fs/cgroup:/sys/fs/cgroup:ro" 84 | test_playbook: official-nginx-role-debian.yml 85 | 86 | - distro: debian9 87 | version: latest 88 | init: /lib/systemd/systemd 89 | run_opts: "--privileged --volume=/sys/fs/cgroup:/sys/fs/cgroup:ro" 90 | test_playbook: official-nginx-role-debian.yml 91 | 92 | before_install: 93 | # Pull container 94 | - 'docker pull rndmh3ro/docker-${distro}-ansible:${version}' 95 | 96 | script: 97 | - pip install --user ansible-lint 98 | - ansible-lint ./ 99 | 100 | - container_id=$(mktemp) 101 | # Run container in detached state 102 | - 'docker run --detach --volume="${PWD}":/etc/ansible/roles/ansible-nginx-hardening:ro ${run_opts} rndmh3ro/docker-${distro}-ansible:${version} "${init}" > "${container_id}"' 103 | 104 | # Install ansible galaxy requirements 105 | - 'docker exec "$(cat ${container_id})" ansible-galaxy install --ignore-certs -r /etc/ansible/roles/ansible-nginx-hardening/requirements.yml -p /etc/ansible/roles/' 106 | 107 | # Test role 108 | - 'docker exec "$(cat ${container_id})" ansible-playbook /etc/ansible/roles/ansible-nginx-hardening/tests/"${test_playbook}" -vv' 109 | 110 | # Verify role 111 | - 'inspec exec https://github.com/dev-sec/nginx-baseline/ -t docker://$(cat ${container_id}) --controls=nginx-01 nginx-02 nginx-03 nginx-05 nginx-06 nginx-07 nginx-08 nginx-09 nginx-10 nginx-12 nginx-13 nginx-15 nginx-17 --no-distinct-exit' 112 | 113 | notifications: 114 | webhooks: https://galaxy.ansible.com/api/v1/notifications/ 115 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [2.1.0](https://github.com/dev-sec/ansible-nginx-hardening/tree/2.1.0) (2018-11-18) 4 | 5 | [Full Changelog](https://github.com/dev-sec/ansible-nginx-hardening/compare/2.0.0...2.1.0) 6 | 7 | **Merged pull requests:** 8 | 9 | - add ubuntu 18.04 support [\#20](https://github.com/dev-sec/ansible-nginx-hardening/pull/20) ([rndmh3ro](https://github.com/rndmh3ro)) 10 | - updated minimum required ansible version to 2.5 in README, as the used 'loop' keyword was introduced in version 2.5 [\#19](https://github.com/dev-sec/ansible-nginx-hardening/pull/19) ([szEvEz](https://github.com/szEvEz)) 11 | 12 | ## [2.0.0](https://github.com/dev-sec/ansible-nginx-hardening/tree/2.0.0) (2018-09-08) 13 | 14 | [Full Changelog](https://github.com/dev-sec/ansible-nginx-hardening/compare/1.0.2...2.0.0) 15 | 16 | **Implemented enhancements:** 17 | 18 | - Update readme to include baselines [\#10](https://github.com/dev-sec/ansible-nginx-hardening/issues/10) 19 | - Update testing, remove useless params, style update [\#18](https://github.com/dev-sec/ansible-nginx-hardening/pull/18) ([rndmh3ro](https://github.com/rndmh3ro)) 20 | - Update README.md [\#14](https://github.com/dev-sec/ansible-nginx-hardening/pull/14) ([vishesh92](https://github.com/vishesh92)) 21 | - Add comment filter to {{ansible\_managed}} string [\#12](https://github.com/dev-sec/ansible-nginx-hardening/pull/12) ([fazlearefin](https://github.com/fazlearefin)) 22 | - use new Docker images [\#8](https://github.com/dev-sec/ansible-nginx-hardening/pull/8) ([rndmh3ro](https://github.com/rndmh3ro)) 23 | 24 | **Fixed bugs:** 25 | 26 | - Running kitchen verify asks for 'roots' password [\#11](https://github.com/dev-sec/ansible-nginx-hardening/issues/11) 27 | - Fix duplicate ssl\_prefer\_server\_ciphers error [\#16](https://github.com/dev-sec/ansible-nginx-hardening/pull/16) ([oakey-b1](https://github.com/oakey-b1)) 28 | 29 | ## [1.0.2](https://github.com/dev-sec/ansible-nginx-hardening/tree/1.0.2) (2016-10-24) 30 | 31 | [Full Changelog](https://github.com/dev-sec/ansible-nginx-hardening/compare/1.0.1...1.0.2) 32 | 33 | **Fixed bugs:** 34 | 35 | - Syntax Error while loading YAML in defaults/main.yml [\#6](https://github.com/dev-sec/ansible-nginx-hardening/issues/6) 36 | 37 | **Merged pull requests:** 38 | 39 | - remove tabs. fix \#6 [\#7](https://github.com/dev-sec/ansible-nginx-hardening/pull/7) ([rndmh3ro](https://github.com/rndmh3ro)) 40 | 41 | ## [1.0.1](https://github.com/dev-sec/ansible-nginx-hardening/tree/1.0.1) (2016-09-23) 42 | 43 | [Full Changelog](https://github.com/dev-sec/ansible-nginx-hardening/compare/1.0.0...1.0.1) 44 | 45 | **Fixed bugs:** 46 | 47 | - ssl\_dhparam [\#4](https://github.com/dev-sec/ansible-nginx-hardening/issues/4) 48 | 49 | **Closed issues:** 50 | 51 | - Make the owner /etc/nginx configurabe [\#3](https://github.com/dev-sec/ansible-nginx-hardening/issues/3) 52 | - Running worker process as non-privileged user \(1 failed\) [\#2](https://github.com/dev-sec/ansible-nginx-hardening/issues/2) 53 | 54 | **Merged pull requests:** 55 | 56 | - create dhparam file. fix \#4 [\#5](https://github.com/dev-sec/ansible-nginx-hardening/pull/5) ([rndmh3ro](https://github.com/rndmh3ro)) 57 | - improve gemfile, update readme for local tests [\#1](https://github.com/dev-sec/ansible-nginx-hardening/pull/1) ([chris-rock](https://github.com/chris-rock)) 58 | 59 | ## [1.0.0](https://github.com/dev-sec/ansible-nginx-hardening/tree/1.0.0) (2016-08-11) 60 | 61 | [Full Changelog](https://github.com/dev-sec/ansible-nginx-hardening/compare/1b9dcf16cfbf45ff5f50cd83509245d1527f9fd0...1.0.0) 62 | 63 | 64 | 65 | \* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)* 66 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributor Guideline 2 | 3 | This document provides an overview of how you can participate in improving this project or extending it. We are grateful for all your help: bug reports and fixes, code contributions, documentation or ideas. Feel free to join, we appreciate your support!! 4 | 5 | ## Communication 6 | 7 | ### GitHub repositories 8 | 9 | Much of the issues, goals and ideas are tracked in the respective projects in GitHub. Please use this channel to report bugs and post ideas. 10 | 11 | ## git and GitHub 12 | 13 | In order to contribute code please: 14 | 15 | 1. Fork the project on GitHub 16 | 2. Clone the project 17 | 3. Add changes (and tests) 18 | 4. Commit and push 19 | 5. Create a merge-request 20 | 21 | To have your code merged, see the expectations listed below. 22 | 23 | You can find a well-written guide [here](https://help.github.com/articles/fork-a-repo). 24 | 25 | Please follow common commit best-practices. Be explicit, have a short summary, a well-written description and references. This is especially important for the merge-request. 26 | 27 | Some great guidelines can be found [here](https://wiki.openstack.org/wiki/GitCommitMessages) and [here](http://robots.thoughtbot.com/5-useful-tips-for-a-better-commit-message). 28 | 29 | 30 | ## Expectations 31 | 32 | ### Don't reinvent the wheel 33 | 34 | This hardening project doesn't intend to reinvent the configuration stack for services. Aim to use official configuration projects first and provide hardening as a layer on top. The goal is remove the need for a user to configure all aspects of services and maintain security configuration. This way, the user can still configure a service using the interface provided by the official project. 35 | 36 | * For Chef refer to the official [opscode community cookbooks](http://community.opscode.com/cookbooks). 37 | * For Puppet head to the [Puppet Forge](https://forge.puppetlabs.com/) and take a node of the Puppet supported modules. 38 | * For Ansible check the [Ansible Module Index](http://docs.ansible.com/list_of_all_modules.html) 39 | 40 | These projects are generally hosted on GitHub as well. 41 | 42 | In some cases, we in fact create the full rollout stack, but this is generally the exception ([os-hardening](https://github.com/TelekomLabs/chef-os-hardening), [nginx-hardening](https://github.com/TelekomLabs/chef-nginx-hardening)). 43 | 44 | 45 | ### Be explicit 46 | 47 | * Please avoid using nonsensical property and variable names. 48 | * Use self-describing attribute names for user configuration. 49 | * In case of failures, communicate what happened and why a failure occurs to the user. Make it easy to track the code or action that produced the error. Try to catch and handle errors if possible to provide improved failure messages. 50 | 51 | 52 | ### Add tests 53 | 54 | The security review of this project is done using integration tests. 55 | 56 | Whenever you add a new security configuration, please start by writing a test that checks for this configuration. For example: If you want to set a new attribute in a configuration file, write a test that expects the value to be set first. Then implement your change. 57 | 58 | You may add a new feature request by creating a test for whatever value you need. 59 | 60 | All tests will be reviewed internally for their validity and overall project direction. 61 | 62 | 63 | ### Document your code 64 | 65 | As code is more often read than written, please provide documentation in all projects. 66 | 67 | Adhere to the respective guidelines for documentation: 68 | 69 | * Chef generally documents code based explicit readme files. For code documentation please use [yard-chef](https://github.com/rightscale/yard-chef) 70 | * [Puppet module documentation](http://docs.puppetlabs.com/puppet/latest/reference/modules_documentation.html) 71 | 72 | 73 | ### Follow coding styles 74 | 75 | We generally include test for coding guidelines: 76 | 77 | * Chef follows [Foodcritic](http://acrmp.github.io/foodcritic/) 78 | * Puppet is checked with [puppet-lint](http://puppet-lint.com/checks/) 79 | * Ansible is checked by running the playbook with the syntax-check option, e.g. `ansible-playbook foo.yml --syntax-check` 80 | 81 | Remember: Code is generally read much more often than written. 82 | 83 | ### Use Markdown 84 | 85 | Wherever possible, please refrain from any other formats and stick to simple markdown. 86 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | source 'https://rubygems.org' 4 | 5 | group :integration do 6 | gem 'test-kitchen', '~> 1.0' 7 | gem 'kitchen-ansible' 8 | gem 'kitchen-vagrant' 9 | gem 'kitchen-inspec' 10 | gem 'kitchen-sharedtests', '~> 0.2.0' 11 | gem 'kitchen-sync' 12 | gem 'kitchen-transport-rsync' 13 | gem 'kitchen-docker' 14 | gem 'inspec', '~> 3' 15 | end 16 | 17 | group :tools do 18 | gem 'github_changelog_generator', '~> 1' 19 | end 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nginx-hardening (Ansible Role) 2 | 3 | 4 | **Attention:** This role has been migrated to our hardening-collection: 5 | * https://github.com/dev-sec/ansible-os-hardening/ 6 | * https://galaxy.ansible.com/devsec/hardening 7 | 8 | 9 | ## Requirements 10 | 11 | * Ansible >= 2.5 12 | 13 | ## Role Variables 14 | 15 | * [nginx_client_body_buffer_size][] 16 | * Default: `1k` 17 | * Description: Sets buffer size for reading client request body. In case the request body is larger than the buffer, the whole body or only its part is written to a temporary file. 18 | * nginx_remove_default_site 19 | * Default: `true` 20 | * Description: Disables the default site. Set to false to enable the default site in nginx. 21 | * [nginx_client_max_body_size][] 22 | * Default: `1k` 23 | * Description: Sets the maximum allowed size of the client request body, specified in the “Content-Length” request header field. If the size in a request exceeds the configured value, the 41 24 | 3 (Request Entity Too Large) error is returned to the client. 25 | * [nginx_keepalive_timeout][] 26 | * Default: `5 5` 27 | * Description: The first parameter sets a timeout during which a keep-alive client connection will stay open on the server side. The zero value disables keep-alive client connections. The op 28 | tional second parameter sets a value in the “Keep-Alive: timeout=time” response header field. 29 | * [nginx_server_tokens][] 30 | * Default: `off` 31 | * Description: Disables emitting nginx version in error messages and in the "Server" response header field. Set to on to enable the nginx version in error messages and "Server" response head 32 | er. 33 | * [nginx_client_header_buffer_size][] 34 | * Default: `1k` 35 | * Description: Sets buffer size for reading client request header. For most requests, a buffer of 1K bytes is enough. 36 | * [nginx_large_client_header_buffers][] 37 | * Default: `2 1k` 38 | * Description: Sets the maximum number and size of buffers used for reading large client request header. 39 | * [nginx_client_body_timeout][] 40 | * Default: `10` 41 | * Description: Defines a timeout for reading client request body. 42 | * [nginx_client_header_timeout][] 43 | * Default: `10` 44 | * Description: Defines a timeout for reading client request header. 45 | * [nginx_send_timeout][] 46 | * Default: `10` 47 | * Description: Sets a timeout for transmitting a response to the client. 48 | * [nginx_limit_conn_zone][] 49 | * Default: `$binary_remote_addr zone=default:10m` 50 | * Description: Sets parameters for a shared memory zone that will keep states for various keys. 51 | * [nginx_limit_conn][] 52 | * Default: `default 5` 53 | * Description: Sets the shared memory zone and the maximum allowed number of connections for a given key value. 54 | * [nginx_add_header][] 55 | * Default: `[ "X-Frame-Options SAMEORIGIN", "X-Content-Type-Options nosniff", "X-XSS-Protection \"1; mode=block\"" ]` 56 | * Description:Adds the specified field to a response header provided that the response code equals 200, 201, 204, 206, 301, 302, 303, 304, or 307. 57 | * [nginx_ssl_protocols][] 58 | * Default: `TLSv1.2` 59 | * Description: Specifies the SSL protocol which should be used. 60 | * [nginx_ssl_ciphers][] 61 | * Default: *see defaults.yml* 62 | * Description: Specifies the TLS ciphers which should be used. 63 | * [nginx_ssl_prefer_server_ciphers][] 64 | * Default: `on` 65 | * Description: Specifies that server ciphers should be preferred over client ciphers when using the TLS protocols. Set to false to disable it. 66 | * [nginx_dh_size][] 67 | * Default: `2048` 68 | * Description: Specifies the length of DH parameters for EDH ciphers. 69 | 70 | ## Installation 71 | 72 | Install the role with ansible-galaxy: 73 | 74 | ``` 75 | ansible-galaxy install dev-sec.nginx-hardening 76 | ``` 77 | 78 | ## Example Playbook 79 | 80 | - hosts: localhost 81 | roles: 82 | - dev-sec.nginx-hardening 83 | 84 | ## Local Testing 85 | 86 | The preferred way of locally testing the role is to use Docker. You will have to install Docker on your system. See [Get started](https://docs.docker.com/) for a Docker package suitable to for your system. 87 | 88 | You can also use vagrant and Virtualbox or VMWare to run tests locally. You will have to install Virtualbox and Vagrant on your system. See [Vagrant Downloads](http://downloads.vagrantup.com/) for a vagrant package suitable for your system. For all our tests we use `test-kitchen`. If you are not familiar with `test-kitchen` please have a look at [their guide](http://kitchen.ci/docs/getting-started). 89 | 90 | Next install test-kitchen: 91 | 92 | ```bash 93 | # Install dependencies 94 | gem install bundler 95 | bundle install 96 | ``` 97 | 98 | ### Testing with Docker 99 | 100 | ``` 101 | # fast test on one machine 102 | bundle exec kitchen test default-ubuntu-1204 103 | 104 | # test on all machines 105 | bundle exec kitchen test 106 | 107 | # for development 108 | bundle exec kitchen create default-ubuntu-1204 109 | bundle exec kitchen converge default-ubuntu-1204 110 | ``` 111 | 112 | ### Testing with Virtualbox 113 | 114 | ``` 115 | # fast test on one machine 116 | KITCHEN_YAML=".kitchen.vagrant.yml" bundle exec kitchen test nginx-ansible-19-ubuntu-1404 117 | 118 | # test on all machines 119 | KITCHEN_YAML=".kitchen.vagrant.yml" bundle exec kitchen test 120 | 121 | # for development 122 | KITCHEN_YAML=".kitchen.vagrant.yml" bundle exec kitchen create nginx-ansible-19-ubuntu-1404 123 | KITCHEN_YAML=".kitchen.vagrant.yml" bundle exec kitchen converge nginx-ansible-19-ubuntu-1404 124 | ``` 125 | 126 | For more information see [test-kitchen](http://kitchen.ci/docs/getting-started) 127 | 128 | ## Contributing 129 | 130 | See [contributor guideline](CONTRIBUTING.md). 131 | 132 | ## License and Author 133 | 134 | * Author:: Sebastian Gumprich 135 | 136 | Licensed under the Apache License, Version 2.0 (the "License"); 137 | you may not use this file except in compliance with the License. 138 | You may obtain a copy of the License at 139 | 140 | http://www.apache.org/licenses/LICENSE-2.0 141 | 142 | Unless required by applicable law or agreed to in writing, software 143 | distributed under the License is distributed on an "AS IS" BASIS, 144 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 145 | See the License for the specific language governing permissions and 146 | limitations under the License. 147 | 148 | [1]: http://travis-ci.org/dev-sec/ansible-nginx-hardening 149 | [2]: https://gitter.im/dev-sec/general 150 | [3]: https://galaxy.ansible.com/dev-sec/nginx-hardening/ 151 | 152 | [nginx_client_body_buffer_size]: http://nginx.org/en/docs/http/ngx_http_core_module.html#client_body_buffer_size 153 | [nginx_client_max_body_size]: http://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size 154 | [nginx_keepalive_timeout]: http://nginx.org/en/docs/http/ngx_http_core_module.html#keepalive_timeout 155 | [nginx_server_tokens]: http://nginx.org/en/docs/http/ngx_http_core_module.html#server_tokens 156 | [nginx_more_clear_headers]: http://nginx.org/en/docs/http/ngx_http_headers_module.html#add_header 157 | [nginx_client_header_buffer_size]: http://nginx.org/en/docs/http/ngx_http_core_module.html#client_header_buffer_size 158 | [nginx_large_client_header_buffers]: http://nginx.org/en/docs/http/ngx_http_core_module.html#large_client_header_buffers 159 | [nginx_client_body_timeout]: http://nginx.org/en/docs/http/ngx_http_core_module.html#client_body_timeout 160 | [nginx_client_header_timeout]: http://nginx.org/en/docs/http/ngx_http_core_module.html#client_header_timeout 161 | [nginx_send_timeout]: http://nginx.org/en/docs/http/ngx_http_core_module.html#send_timeout 162 | [nginx_limit_conn_zone]: http://nginx.org/en/docs/http/ngx_http_limit_conn_module.html#limit_conn_zone 163 | [nginx_limit_conn]: http://nginx.org/en/docs/http/ngx_http_limit_conn_module.html#limit_conn 164 | [nginx_add_header]: http://nginx.org/en/docs/http/ngx_http_headers_module.html#add_header 165 | [nginx_ssl_protocols]: http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_protocols 166 | [nginx_ssl_ciphers]: http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_ciphers 167 | [nginx_ssl_prefer_server_ciphers]: http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_prefer_server_ciphers 168 | [nginx_dh_size]: http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_dhparam 169 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env rake 2 | # encoding: utf-8 3 | 4 | # Automatically generate a changelog for this project. Only loaded if 5 | # the necessary gem is installed. 6 | begin 7 | require 'github_changelog_generator/task' 8 | GitHubChangelogGenerator::RakeTask.new :changelog 9 | rescue LoadError 10 | puts '>>>>> GitHub Changelog Generator not loaded, omitting tasks' 11 | end 12 | -------------------------------------------------------------------------------- /ansible.cfg: -------------------------------------------------------------------------------- 1 | # config file for ansible -- http://ansible.com/ 2 | # ============================================== 3 | 4 | # nearly all parameters can be overridden in ansible-playbook 5 | # or with command line flags. ansible will read ANSIBLE_CONFIG, 6 | # ansible.cfg in the current working directory, .ansible.cfg in 7 | # the home directory or /etc/ansible/ansible.cfg, whichever it 8 | # finds first 9 | 10 | [defaults] 11 | ansible_managed = Ansible managed: {file} modified on %Y-%m-%d by {uid} on {host} 12 | 13 | role_path = /vagrant 14 | scp_if_ssh = True 15 | -------------------------------------------------------------------------------- /defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | nginx_client_body_buffer_size: '1k' 3 | nginx_remove_default_site: true 4 | nginx_client_max_body_size: '1k' 5 | nginx_keepalive_timeout: '5 5' 6 | nginx_server_tokens: 'off' 7 | nginx_client_header_buffer_size: "1k" 8 | nginx_large_client_header_buffers: "2 1k" 9 | nginx_client_body_timeout: "10" 10 | nginx_client_header_timeout: "10" 11 | nginx_send_timeout: "10" 12 | nginx_limit_conn_zone: "$binary_remote_addr zone=default:10m" 13 | nginx_limit_conn: "default 5" 14 | nginx_add_header: [ 15 | # vvoid clickjacking 16 | "X-Frame-Options SAMEORIGIN", 17 | # disable content-type sniffing 18 | "X-Content-Type-Options nosniff", 19 | # XSS filter 20 | "X-XSS-Protection \"1; mode=block\"", 21 | "Strict-Transport-Security max-age=15768000", 22 | "Content-Security-Policy \"script-src 'self'; object-src 'self'\"" ] 23 | 24 | nginx_set_cookie_flag: "* HttpOnly secure" 25 | nginx_ssl_prefer_server_ciphers: "on" 26 | nginx_ssl_protocols: "TLSv1.2" 27 | nginx_ssl_ciphers: "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256" 28 | nginx_ssl_session_tickets: "off" 29 | nginx_dh_size: "2048" 30 | -------------------------------------------------------------------------------- /handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: restart nginx 3 | service: 4 | name: "nginx" 5 | state: restarted 6 | -------------------------------------------------------------------------------- /kitchen_vagrant_block.rb: -------------------------------------------------------------------------------- 1 | # This is a Vagrant block to allow proxy settings to be carried into Kitchen 2 | # You need this for all of yum/apt etc. to work! 3 | unless ENV['http_proxy'].empty? || Vagrant.has_plugin?("vagrant-proxyconf") 4 | raise "Missing required plugin 'vagrant-proxyconf' to support HTTP(S) proxies, run `vagrant plugin install vagrant-proxyconf`" 5 | end 6 | 7 | Vagrant.configure(2) do |config| 8 | config.proxy.http = "#{ENV['http_proxy']}" 9 | config.proxy.https = "#{ENV['https_proxy']}" 10 | config.proxy.no_proxy = "localhost,127.0.0.1" 11 | 12 | # You may have vagrant-vbguest plugin installed to keep your images up to date 13 | # - but will probably have VBoxAddition build issues with the foreign boxes listed in .kitchen.vagrant.yml 14 | config.vbguest.auto_update = false 15 | end 16 | -------------------------------------------------------------------------------- /meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | galaxy_info: 3 | author: "Sebastian Gumprich" 4 | description: 'This Ansible role provides secure nginx configurations. http://dev-sec.io/' 5 | company: Hardening Framework Team 6 | license: Apache License 2.0 7 | min_ansible_version: '2.5' 8 | platforms: 9 | - name: EL 10 | versions: 11 | - 6 12 | - 7 13 | - name: Ubuntu 14 | versions: 15 | - xenial 16 | - bionic 17 | - name: Debian 18 | versions: 19 | - stretch 20 | - buster 21 | galaxy_tags: 22 | - system 23 | - security 24 | - hardening 25 | - nginx 26 | dependencies: [] 27 | -------------------------------------------------------------------------------- /requirements.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - src: nginxinc.nginx 3 | - src: geerlingguy.nginx 4 | -------------------------------------------------------------------------------- /tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: create additional configuration 3 | template: 4 | src: "hardening.conf.j2" 5 | dest: "/etc/nginx/conf.d/90.hardening.conf" 6 | mode: '0600' 7 | owner: "root" 8 | group: "root" 9 | notify: restart nginx 10 | 11 | - name: change configuration in main nginx.conf 12 | lineinfile: 13 | dest: "/etc/nginx/nginx.conf" 14 | regexp: '^\s*server_tokens' 15 | line: " server_tokens {{ nginx_server_tokens }};" 16 | insertafter: "http {" 17 | mode: '0640' 18 | owner: "root" 19 | group: "root" 20 | notify: restart nginx 21 | 22 | - name: change ssl_protocols in main nginx.conf 23 | lineinfile: 24 | dest: "/etc/nginx/nginx.conf" 25 | regexp: '^\s*ssl_protocols' 26 | line: " ssl_protocols {{ nginx_ssl_protocols }};" 27 | insertafter: "http {" 28 | mode: '0640' 29 | owner: "root" 30 | group: "root" 31 | notify: restart nginx 32 | 33 | - name: change ssl_prefer_server_ciphers in main nginx.conf 34 | lineinfile: 35 | dest: "/etc/nginx/nginx.conf" 36 | regexp: '^\s*ssl_prefer_server_ciphers' 37 | line: " ssl_prefer_server_ciphers {{ nginx_ssl_prefer_server_ciphers }};" 38 | insertafter: "http {" 39 | mode: '0640' 40 | owner: "root" 41 | group: "root" 42 | notify: restart nginx 43 | 44 | - name: change client_max_body_size in main nginx.conf 45 | lineinfile: 46 | dest: "/etc/nginx/nginx.conf" 47 | regexp: '^\s*client_max_body_size' 48 | line: " client_max_body_size {{ nginx_client_max_body_size }};" 49 | insertafter: "http {" 50 | mode: '0640' 51 | owner: "root" 52 | group: "root" 53 | notify: restart nginx 54 | 55 | - name: change client_body_buffer_size in main nginx.conf 56 | lineinfile: 57 | dest: "/etc/nginx/nginx.conf" 58 | regexp: '^\s*client_body_buffer_size' 59 | line: " client_body_buffer_size {{ nginx_client_body_buffer_size }};" 60 | insertafter: "http {" 61 | mode: '0640' 62 | owner: "root" 63 | group: "root" 64 | notify: restart nginx 65 | 66 | - name: change keepalive_timeout in main nginx.conf 67 | lineinfile: 68 | dest: "/etc/nginx/nginx.conf" 69 | regexp: '^\s*keepalive_timeout' 70 | line: " keepalive_timeout {{ nginx_keepalive_timeout }};" 71 | insertafter: "http {" 72 | mode: '0640' 73 | owner: "root" 74 | group: "root" 75 | notify: restart nginx 76 | 77 | - name: remove default.conf 78 | file: 79 | path: "{{ item }}" 80 | state: absent 81 | when: nginx_remove_default_site 82 | notify: restart nginx 83 | loop: 84 | - "/etc/nginx/conf.d/default.conf" 85 | - "/etc/nginx/sites-enabled/default" 86 | 87 | - name: generate dh group 88 | openssl_dhparam: 89 | path: "/etc/nginx/dh{{ nginx_dh_size }}.pem" 90 | size: "{{ nginx_dh_size }}" 91 | mode: '0640' 92 | owner: "root" 93 | group: "root" 94 | notify: restart nginx 95 | -------------------------------------------------------------------------------- /templates/hardening.conf.j2: -------------------------------------------------------------------------------- 1 | # {{ ansible_managed|comment }} 2 | # Additional configuration for Nginx. 3 | 4 | client_header_buffer_size {{ nginx_client_header_buffer_size }}; 5 | large_client_header_buffers {{ nginx_large_client_header_buffers }}; 6 | client_body_timeout {{ nginx_client_body_timeout }}; 7 | client_header_timeout {{ nginx_client_header_timeout }}; 8 | send_timeout {{ nginx_send_timeout }}; 9 | limit_conn_zone {{ nginx_limit_conn_zone }}; 10 | limit_conn {{ nginx_limit_conn }}; 11 | ssl_ciphers '{{ nginx_ssl_ciphers }}'; 12 | ssl_session_tickets {{ nginx_ssl_session_tickets }}; 13 | ssl_dhparam /etc/nginx/dh{{ nginx_dh_size }}.pem; 14 | {% for header in nginx_add_header %} 15 | add_header {{ header }}; 16 | {% endfor %} 17 | -------------------------------------------------------------------------------- /tests/official-nginx-role-debian.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: wrapper playbook for kitchen testing "ansible-nginx-hardening" with custom settings 3 | hosts: localhost 4 | vars: 5 | - nginx_main_template_enable: true 6 | - nginx_main_template: 7 | template_file: nginx.conf.j2 8 | conf_file_name: nginx.conf 9 | conf_file_location: /etc/nginx/ 10 | user: www-data 11 | worker_processes: auto 12 | error_level: warn 13 | worker_connections: 1024 14 | http_enable: true 15 | http_settings: 16 | keepalive_timeout: 65 17 | cache: false 18 | rate_limit: false 19 | keyval: false 20 | stream_enable: false 21 | http_global_autoindex: false 22 | pre_tasks: 23 | - apt_repository: 24 | repo: "deb http://ftp.debian.org/debian jessie-backports main" 25 | state: present 26 | when: ansible_distribution == 'Debian' and ansible_distribution_major_version == '8' 27 | - set_fact: 28 | nginx_default_release: "jessie-backports" 29 | when: ansible_distribution == 'Debian' and ansible_distribution_major_version == '8' 30 | - package: name="{{item}}" state=installed 31 | with_items: 32 | - "systemd" 33 | ignore_errors: true 34 | - apt: name="{{item}}" state=installed update_cache=true 35 | with_items: 36 | - "systemd" 37 | ignore_errors: true 38 | roles: 39 | - nginxinc.nginx 40 | - ansible-nginx-hardening 41 | -------------------------------------------------------------------------------- /tests/official-nginx-role-redhat.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: wrapper playbook for kitchen testing "ansible-nginx-hardening" with custom settings 3 | hosts: localhost 4 | pre_tasks: 5 | - package: name="{{item}}" state=installed 6 | with_items: 7 | - "systemd" 8 | ignore_errors: true 9 | - apt: name="{{item}}" state=installed update_cache=true 10 | with_items: 11 | - "systemd" 12 | ignore_errors: true 13 | roles: 14 | - nginxinc.nginx 15 | - ansible-nginx-hardening 16 | -------------------------------------------------------------------------------- /tests/test.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: wrapper playbook for kitchen testing "ansible-nginx-hardening" with custom settings 3 | hosts: localhost 4 | vars: 5 | - nginx_ppa_use: true 6 | - nginx_ppa_version: stable 7 | pre_tasks: 8 | - apt_repository: 9 | repo: "deb http://ftp.debian.org/debian jessie-backports main" 10 | state: present 11 | when: ansible_distribution == 'Debian' and ansible_distribution_major_version == '8' 12 | - set_fact: 13 | nginx_default_release: "jessie-backports" 14 | when: ansible_distribution == 'Debian' and ansible_distribution_major_version == '8' 15 | - package: name="{{item}}" state=installed 16 | with_items: 17 | - "systemd" 18 | ignore_errors: true 19 | - apt: name="{{item}}" state=installed update_cache=true 20 | with_items: 21 | - "systemd" 22 | ignore_errors: true 23 | roles: 24 | - geerlingguy.nginx 25 | - ansible-nginx-hardening 26 | -------------------------------------------------------------------------------- /vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | --------------------------------------------------------------------------------