diff --git a/.gitignore b/.gitignore index 0a1bd15..8963f9d 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ /static/ /media/ /tmp/ +/.keys/ /.vagrant/ /Vagrantfile.local /docs/_build/ diff --git a/README.md b/README.md index 05204c7..0684b42 100644 --- a/README.md +++ b/README.md @@ -66,8 +66,8 @@ Now access the admin panel with the user account you created earlier: http://loc ## Production notes -We suggest using Docker or [Dokku](http://dokku.viewdocs.io/) for automated deployment. There is a Makefile to help set up and manage the instance. +We use Ansible for automated deployment. You need to obtain an SSH and vault keys and place these in a `.keys` folder, then: -- Initial setup: `make setup` -- Startup: `make run-detached` -- Release: `make release` +``` +ansible-playbook -s ansible/docker.yaml -i ansible/inventories/production +``` diff --git a/ansible.cfg b/ansible.cfg new file mode 100644 index 0000000..452692a --- /dev/null +++ b/ansible.cfg @@ -0,0 +1,4 @@ +[defaults] +retry_files_enabled = False +roles_path = ansible/roles +vault_password_file = .keys/ansible.vault diff --git a/ansible/docker.yaml b/ansible/docker.yaml new file mode 100644 index 0000000..d382087 --- /dev/null +++ b/ansible/docker.yaml @@ -0,0 +1,9 @@ +- hosts: webservers + become: yes + become_method: 'sudo' + gather_facts: yes + roles: + - role: docker-ubuntu + docker_users: ansible + - role: docker-compose + docker_compose_version: 1.12.0 diff --git a/ansible/harden.yaml b/ansible/harden.yaml new file mode 100644 index 0000000..26fe918 --- /dev/null +++ b/ansible/harden.yaml @@ -0,0 +1,10 @@ +- hosts: webservers + become: True + gather_facts: True + roles: + - role: dev-sec.os-hardening + + - role: dev-sec.ssh-hardening + become: yes + + - dev-sec.nginx-hardening diff --git a/ansible/inventories/production/group_vars/webservers/vars.yaml b/ansible/inventories/production/group_vars/webservers/vars.yaml new file mode 100644 index 0000000..4372a9f --- /dev/null +++ b/ansible/inventories/production/group_vars/webservers/vars.yaml @@ -0,0 +1,18 @@ +--- + +django_project_name: publichealth + +elasticsearch_heap_size: 1g + +memcached_memory_allocation_mb: 256 + +nginx_worker_processes: 2 +nginx_worker_connections: 1024 + +domain: "{{ vault_domain }}" + +django_email_key: "{{ vault_django_email_key }}" +django_email_domain: "{{ vault_django_email_domain }}" +django_email_from: "{{ vault_django_email_from }}" + +django_secret_key: "{{ vault_django_secret_key }}" diff --git a/ansible/inventories/production/group_vars/webservers/vault.yaml b/ansible/inventories/production/group_vars/webservers/vault.yaml new file mode 100644 index 0000000..f5d367d --- /dev/null +++ b/ansible/inventories/production/group_vars/webservers/vault.yaml @@ -0,0 +1,18 @@ +$ANSIBLE_VAULT;1.1;AES256 +62653363616638643933303063396435316366376466643563316461343162633661346638343836 +6232346565306638373931396164303438373261396630610a343536386335336634663239626637 +36376137393433343636366137333336303764616536643864353630366535653936663531373439 +3966363565633630620a373238363535653936383365656366306231666365656265303638393864 +33323363373365613134393261393139656261323036656632366562316264346434393366656165 +35363861303036316465326134653163303835376333633132633332303438666363386330303463 +66376132396666396163306566663534646261653139343432663332323837666165653235633538 +34613037306339623032613939663639663161336539333832333438306466653439306535343134 +64363833666138626561363263323338376261343333623839366236343464363737616232633566 +62633934323463623834653539613039636138666539666665333434663165636639303532333233 +30363437633762366230326231653961373462383330336462313935643761316334666232613261 +64613538363763666666303832393632373934343162613439616535663666373434333632633664 +64623531383239636464393036346565373564356666626632613437303335653465386639623366 +61636231383561336238336334303462643137633465303466633764336630636462626535666633 +61386263316437346666633164616162636234623530343038343338373439616131333538343835 +39386435373035633837383264346266643433366363653566363863393434356337633366363635 +64666465613164353238626165623931633534666438386264633431356332616264 diff --git a/ansible/inventories/production/webservers b/ansible/inventories/production/webservers new file mode 100644 index 0000000..66f65f9 --- /dev/null +++ b/ansible/inventories/production/webservers @@ -0,0 +1,7 @@ +[webservers] +172.104.150.7 + +[all:vars] +ansible_connection=ssh +ansible_ssh_user=ansible +ansible_ssh_private_key_file=.keys/ansible.pem diff --git a/ansible/roles/dev-sec.nginx-hardening/.gitignore b/ansible/roles/dev-sec.nginx-hardening/.gitignore new file mode 100644 index 0000000..fbe87e0 --- /dev/null +++ b/ansible/roles/dev-sec.nginx-hardening/.gitignore @@ -0,0 +1,13 @@ +README.pdf +README.html +shared_test_repo/ +test/integration +.kitchen +coverage +Vagrantfile.erb + +Gemfile.lock +Berksfile.lock + +ansible.cfg +hosts diff --git a/ansible/roles/dev-sec.nginx-hardening/.kitchen.vagrant.yml b/ansible/roles/dev-sec.nginx-hardening/.kitchen.vagrant.yml new file mode 100644 index 0000000..216cd92 --- /dev/null +++ b/ansible/roles/dev-sec.nginx-hardening/.kitchen.vagrant.yml @@ -0,0 +1,65 @@ +--- +driver: + name: vagrant + +provisioner: + name: ansible_playbook + test_repo_uri: https://github.com/hardening-io/tests-ssh-hardening.git + hosts: all + require_ansible_repo: false + require_ansible_omnibus: true + require_chef_for_busser: false + require_ruby_for_busser: false + ansible_verbose: true + roles_path: ../ansible-ssh-hardening/ + playbook: default.yml + +platforms: +- name: ubuntu-12.04 + driver_config: + box: opscode-ubuntu-12.04 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_ubuntu-12.04_chef-provisionerless.box +- name: ubuntu-14.04 + driver_config: + box: opscode-ubuntu-14.04 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_ubuntu-14.04_chef-provisionerless.box +- name: centos-6.4 + driver_config: + box: opscode-centos-6.4 + box_url: https://opscode-vm.s3.amazonaws.com/vagrant/opscode_centos-6.4_provisionerless.box +- name: centos-6.5 + driver_config: + box: opscode-centos-6.5 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_centos-6.5_chef-provisionerless.box +- name: oracle-6.4 + driver_config: + box: oracle-6.4 + box_url: https://storage.us2.oraclecloud.com/v1/istoilis-istoilis/vagrant/oel64-64.box +- name: oracle-6.5 + driver_config: + box: oracle-6.5 + box_url: https://storage.us2.oraclecloud.com/v1/istoilis-istoilis/vagrant/oel65-64.box +- name: debian-6 + driver_config: + box: debian-6 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_debian-6.0.10_chef-provisionerless.box +- name: debian-7 + driver_config: + box: debian-7 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_debian-7.8_chef-provisionerless.box +- name: debian-8 + driver_config: + box: debian-8 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_debian-8.1_chef-provisionerless.box + +verifier: + name: inspec + sudo: true + inspec_tests: + - https://github.com/dev-sec/tests-ssh-hardening + +suites: +- name: nginx-ansible_1.9 + provisioner: + ansible_version: 1.9.4 +- name: nginx-ansible_latest diff --git a/ansible/roles/dev-sec.nginx-hardening/.kitchen.yml b/ansible/roles/dev-sec.nginx-hardening/.kitchen.yml new file mode 100644 index 0000000..fa2ecee --- /dev/null +++ b/ansible/roles/dev-sec.nginx-hardening/.kitchen.yml @@ -0,0 +1,60 @@ +--- +driver: + name: docker + use_sudo: false + +transport: + max_ssh_sessions: 5 + +provisioner: + name: ansible_playbook + hosts: all + require_ansible_repo: false + require_ansible_omnibus: true + require_chef_for_busser: false + require_ruby_for_busser: false + ansible_verbose: true + ansible_diff: true + requirements_path: requirements.yml + roles_path: ../ansible-nginx-hardening/ + playbook: default.yml + +platforms: +- name: ubuntu-12.04 + driver: + image: ubuntu:12.04 +- name: ubuntu-14.04 + driver: + image: ubuntu:14.04 +- name: ubuntu-16.04 + driver: + image: ubuntu:16.04 +- name: centos-6.7 + driver: + image: centos:6.7 +- name: centos-6.8 + driver: + image: centos:6.8 +- name: centos-7 + driver: + image: centos:7 + privileged: true + run_command: /usr/sbin/init +- name: debian-7 + driver: + image: debian:7 +- name: debian-8 + driver: + image: debian:8 + +verifier: + name: inspec + sudo: true + inspec_tests: + - https://github.com/dev-sec/tests-nginx-hardening + +suites: +- name: nginx-ansible_1.9 + provisioner: + ansible_version: 1.9.4 +- name: nginx-ansible_latest diff --git a/ansible/roles/dev-sec.nginx-hardening/.travis.yml b/ansible/roles/dev-sec.nginx-hardening/.travis.yml new file mode 100644 index 0000000..f562dec --- /dev/null +++ b/ansible/roles/dev-sec.nginx-hardening/.travis.yml @@ -0,0 +1,23 @@ +--- +notifications: + webhooks: https://galaxy.ansible.com/api/v1/notifications/ +language: python +python: "2.7" + +env: + - ANSIBLE_VERSION=latest + - ANSIBLE_VERSION=1.9.4 + +before_install: + - sudo apt-get update -qq + - sudo apt-get install -qq python-apt python-pycurl +install: + - if [ "$ANSIBLE_VERSION" = "latest" ]; then pip install ansible; else pip install ansible==$ANSIBLE_VERSION; fi + - echo -e 'localhost ansible_connection=local' > spec/inventory + - echo -e '[defaults]\nroles_path = ../\nhostfile = ./spec/inventory' > ansible.cfg + +script: + - ansible-galaxy install -r requirements.yml + - ansible-playbook --syntax-check spec/travis.yml + - ansible-playbook --sudo -v --diff spec/travis.yml + - ansible-playbook --sudo -v --diff spec/travis.yml --extra-vars "nginx_remove_default_site=false" diff --git a/ansible/roles/dev-sec.nginx-hardening/CHANGELOG.md b/ansible/roles/dev-sec.nginx-hardening/CHANGELOG.md new file mode 100644 index 0000000..7e5701d --- /dev/null +++ b/ansible/roles/dev-sec.nginx-hardening/CHANGELOG.md @@ -0,0 +1,23 @@ +# Change Log + +## [1.0.1](https://github.com/dev-sec/ansible-nginx-hardening/tree/1.0.1) (2016-09-23) +[Full Changelog](https://github.com/dev-sec/ansible-nginx-hardening/compare/1.0.0...1.0.1) + +**Fixed bugs:** + +- ssl\_dhparam [\#4](https://github.com/dev-sec/ansible-nginx-hardening/issues/4) + +**Closed issues:** + +- Make the owner /etc/nginx configurabe [\#3](https://github.com/dev-sec/ansible-nginx-hardening/issues/3) +- Running worker process as non-privileged user \(1 failed\) [\#2](https://github.com/dev-sec/ansible-nginx-hardening/issues/2) + +**Merged pull requests:** + +- create dhparam file. fix \#4 [\#5](https://github.com/dev-sec/ansible-nginx-hardening/pull/5) ([rndmh3ro](https://github.com/rndmh3ro)) +- 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)) + +## [1.0.0](https://github.com/dev-sec/ansible-nginx-hardening/tree/1.0.0) (2016-08-11) + + +\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)* diff --git a/ansible/roles/dev-sec.nginx-hardening/CONTRIBUTING.md b/ansible/roles/dev-sec.nginx-hardening/CONTRIBUTING.md new file mode 100644 index 0000000..783c10f --- /dev/null +++ b/ansible/roles/dev-sec.nginx-hardening/CONTRIBUTING.md @@ -0,0 +1,85 @@ +# Contributor Guideline + +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!! + +## Communication + +### GitHub repositories + +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. + +## git and GitHub + +In order to contribute code please: + +1. Fork the project on GitHub +2. Clone the project +3. Add changes (and tests) +4. Commit and push +5. Create a merge-request + +To have your code merged, see the expectations listed below. + +You can find a well-written guide [here](https://help.github.com/articles/fork-a-repo). + +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. + +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). + + +## Expectations + +### Don't reinvent the wheel + +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. + +* For Chef refer to the official [opscode community cookbooks](http://community.opscode.com/cookbooks). +* For Puppet head to the [Puppet Forge](https://forge.puppetlabs.com/) and take a node of the Puppet supported modules. +* For Ansible check the [Ansible Module Index](http://docs.ansible.com/list_of_all_modules.html) + +These projects are generally hosted on GitHub as well. + +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)). + + +### Be explicit + +* Please avoid using nonsensical property and variable names. +* Use self-describing attribute names for user configuration. +* 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. + + +### Add tests + +The security review of this project is done using integration tests. + +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. + +You may add a new feature request by creating a test for whatever value you need. + +All tests will be reviewed internally for their validity and overall project direction. + + +### Document your code + +As code is more often read than written, please provide documentation in all projects. + +Adhere to the respective guidelines for documentation: + +* Chef generally documents code based explicit readme files. For code documentation please use [yard-chef](https://github.com/rightscale/yard-chef) +* [Puppet module documentation](http://docs.puppetlabs.com/puppet/latest/reference/modules_documentation.html) + + +### Follow coding styles + +We generally include test for coding guidelines: + +* Chef follows [Foodcritic](http://acrmp.github.io/foodcritic/) +* Puppet is checked with [puppet-lint](http://puppet-lint.com/checks/) +* Ansible is checked by running the playbook with the syntax-check option, e.g. `ansible-playbook foo.yml --syntax-check` + +Remember: Code is generally read much more often than written. + +### Use Markdown + +Wherever possible, please refrain from any other formats and stick to simple markdown. diff --git a/ansible/roles/dev-sec.nginx-hardening/Gemfile b/ansible/roles/dev-sec.nginx-hardening/Gemfile new file mode 100644 index 0000000..837e89f --- /dev/null +++ b/ansible/roles/dev-sec.nginx-hardening/Gemfile @@ -0,0 +1,41 @@ +# encoding: utf-8 + +source 'https://rubygems.org' + +if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.2.2') + gem 'rack', '< 2.0' + gem 'ruby_dep', '< 1.4.0' + gem 'listen', '< 3.0.0' +end + +group :test do + gem 'rake' + gem 'foodcritic', '~> 4.0' + gem 'thor-foodcritic' + gem 'coveralls', require: false +end + +group :development do + gem 'guard' + gem 'guard-rspec' + gem 'guard-kitchen' +end + +group :integration do + gem 'test-kitchen', '~> 1.0' + gem 'kitchen-ansible' + gem 'kitchen-vagrant' + gem 'kitchen-inspec' + gem 'kitchen-sharedtests', '~> 0.2.0' + gem 'kitchen-sync' + gem 'kitchen-transport-rsync' + gem 'kitchen-docker' +end + +group :openstack do + gem 'kitchen-openstack' +end + +group :tools do + gem 'github_changelog_generator', '~> 1' +end diff --git a/ansible/roles/dev-sec.nginx-hardening/README.md b/ansible/roles/dev-sec.nginx-hardening/README.md new file mode 100644 index 0000000..e4aa674 --- /dev/null +++ b/ansible/roles/dev-sec.nginx-hardening/README.md @@ -0,0 +1,133 @@ +# nginx-hardening (Ansible Role) + +[![Build Status](http://img.shields.io/travis/dev-sec/ansible-nginx-hardening.svg)][1] +[![Gitter Chat](https://badges.gitter.im/Join%20Chat.svg)][2] +[![Ansible Galaxy](https://img.shields.io/badge/galaxy-nginx--hardening-660198.svg)][3] + +## Description + +This role provides secure nginx configuration. + +It works with the following nginx-roles, including, but not limited to: +* [geerlinggux.nginx](https://galaxy.ansible.com/geerlingguy/nginx/) +* [jdauphant.nginx](https://galaxy.ansible.com/jdauphant/nginx/) +* [franklinkim.nginx](https://galaxy.ansible.com/franklinkim/nginx/) + +**NOTE: This role does not work with nginx 1.0.15 or older! Please use the latest version from the official nginx repositories!** +## Requirements + +* Ansible + +## Role Variables + +| Name | Default Value | Description | +| -------------- | ------------- | -----------------------------------| +| [nginx_client_body_buffer_size][] | `1k` | 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. | +| nginx_remove_default_site | `true` | Disables the default site. Set to false to enable the default site in nginx. | +| [nginx_client_max_body_size][] | `1k` | 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 413 (Request Entity Too Large) error is returned to the client. | +| [nginx_keepalive_timeout][] | `5 5` | 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 optional second parameter sets a value in the “Keep-Alive: timeout=time” response header field. | +| [nginx_server_tokens][] | `off` | 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 header. | +| [nginx_client_header_buffer_size][] | `1k` | Sets buffer size for reading client request header. For most requests, a buffer of 1K bytes is enough. | +| [nginx_large_client_header_buffers][] | `2 1k` | Sets the maximum number and size of buffers used for reading large client request header. | +| [nginx_client_body_timeout][] | `10` | Defines a timeout for reading client request body. | +| [nginx_client_header_timeout][] | `10` | Defines a timeout for reading client request header. | +| [nginx_send_timeout][] | `10` | Sets a timeout for transmitting a response to the client. | +| [nginx_limit_conn_zone][] | `$binary_remote_addr zone=default:10m` | Sets parameters for a shared memory zone that will keep states for various keys. | +| [nginx_limit_conn][] | `default 5` | Sets the shared memory zone and the maximum allowed number of connections for a given key value. | +| [nginx_add_header][] | `[ "X-Frame-Options SAMEORIGIN", "X-Content-Type-Options nosniff", "X-XSS-Protection \"1; mode=block\"" ]` |Adds the specified field to a response header provided that the response code equals 200, 201, 204, 206, 301, 302, 303, 304, or 307. | +| [nginx_ssl_protocols][] | `TLSv1 TLSv1.1 TLSv1.2` | Specifies the SSL protocol which should be used. | +| [nginx_ssl_ciphers][] | *see defaults.yml* | Specifies the TLS ciphers which should be used. | +| [nginx_ssl_prefer_server_ciphers][] | `on` | Specifies that server ciphers should be preferred over client ciphers when using the TLS protocols. Set to false to disable it. | +| [nginx_dh-size][] | `2048` | Specifies the length of DH parameters for EDH ciphers. | + +## Example Playbook + + - hosts: localhost + roles: + - dev-sec.nginx-hardening + +## Local Testing + +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. + +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). + +Next install test-kitchen: + +```bash +# Install dependencies +gem install bundler +bundle install +``` + +### Testing with Docker + +``` +# fast test on one machine +bundle exec kitchen test default-ubuntu-1204 + +# test on all machines +bundle exec kitchen test + +# for development +bundle exec kitchen create default-ubuntu-1204 +bundle exec kitchen converge default-ubuntu-1204 +``` + +### Testing with Virtualbox + +``` +# fast test on one machine +KITCHEN_YAML=".kitchen.vagrant.yml" bundle exec kitchen test nginx-ansible-19-ubuntu-1404 + +# test on all machines +KITCHEN_YAML=".kitchen.vagrant.yml" bundle exec kitchen test + +# for development +KITCHEN_YAML=".kitchen.vagrant.yml" bundle exec kitchen create nginx-ansible-19-ubuntu-1404 +KITCHEN_YAML=".kitchen.vagrant.yml" bundle exec kitchen converge nginx-ansible-19-ubuntu-1404 +``` + +For more information see [test-kitchen](http://kitchen.ci/docs/getting-started) + +## Contributing + +See [contributor guideline](CONTRIBUTING.md). + +## License and Author + +* Author:: Sebastian Gumprich + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +[1]: http://travis-ci.org/dev-sec/ansible-nginx-hardening +[2]: https://gitter.im/dev-sec/general +[3]: https://galaxy.ansible.com/dev-sec/nginx-hardening/ + +[nginx_client_body_buffer_size]: http://nginx.org/en/docs/http/ngx_http_core_module.html#client_body_buffer_size +[nginx_client_max_body_size]: http://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size +[nginx_keepalive_timeout]: http://nginx.org/en/docs/http/ngx_http_core_module.html#keepalive_timeout +[nginx_server_tokens]: http://nginx.org/en/docs/http/ngx_http_core_module.html#server_tokens +[nginx_more_clear_headers]: http://nginx.org/en/docs/http/ngx_http_headers_module.html#add_header +[nginx_client_header_buffer_size]: http://nginx.org/en/docs/http/ngx_http_core_module.html#client_header_buffer_size +[nginx_large_client_header_buffers]: http://nginx.org/en/docs/http/ngx_http_core_module.html#large_client_header_buffers +[nginx_client_body_timeout]: http://nginx.org/en/docs/http/ngx_http_core_module.html#client_body_timeout +[nginx_client_header_timeout]: http://nginx.org/en/docs/http/ngx_http_core_module.html#client_header_timeout +[nginx_send_timeout]: http://nginx.org/en/docs/http/ngx_http_core_module.html#send_timeout +[nginx_limit_conn_zone]: http://nginx.org/en/docs/http/ngx_http_limit_conn_module.html#limit_conn_zone +[nginx_limit_conn]: http://nginx.org/en/docs/http/ngx_http_limit_conn_module.html#limit_conn +[nginx_add_header]: http://nginx.org/en/docs/http/ngx_http_headers_module.html#add_header +[nginx_ssl_protocols]: http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_protocols +[nginx_ssl_ciphers]: http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_ciphers +[nginx_ssl_prefer_server_ciphers]: http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_prefer_server_ciphers +[nginx_dh-size]: http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_dhparam diff --git a/ansible/roles/dev-sec.nginx-hardening/Rakefile b/ansible/roles/dev-sec.nginx-hardening/Rakefile new file mode 100644 index 0000000..02c5821 --- /dev/null +++ b/ansible/roles/dev-sec.nginx-hardening/Rakefile @@ -0,0 +1,11 @@ +#!/usr/bin/env rake +# encoding: utf-8 + +# Automatically generate a changelog for this project. Only loaded if +# the necessary gem is installed. +begin + require 'github_changelog_generator/task' + GitHubChangelogGenerator::RakeTask.new :changelog +rescue LoadError + puts '>>>>> GitHub Changelog Generator not loaded, omitting tasks' +end diff --git a/ansible/roles/dev-sec.nginx-hardening/default.yml b/ansible/roles/dev-sec.nginx-hardening/default.yml new file mode 100644 index 0000000..8020c5f --- /dev/null +++ b/ansible/roles/dev-sec.nginx-hardening/default.yml @@ -0,0 +1,6 @@ +--- +- name: wrapper playbook for kitchen testing "ansible-nginx-hardening" with custom settings + hosts: localhost + roles: + - geerlingguy.nginx + - ansible-nginx-hardening diff --git a/ansible/roles/dev-sec.nginx-hardening/defaults/main.yml b/ansible/roles/dev-sec.nginx-hardening/defaults/main.yml new file mode 100644 index 0000000..2a84b97 --- /dev/null +++ b/ansible/roles/dev-sec.nginx-hardening/defaults/main.yml @@ -0,0 +1,26 @@ +--- +nginx_client_body_buffer_size: '1k' +nginx_remove_default_site: true +nginx_client_max_body_size: '1k' +nginx_keepalive_timeout: '5 5' +nginx_server_tokens: 'off' +nginx_client_header_buffer_size: "1k" +nginx_large_client_header_buffers: "2 1k" +nginx_client_body_timeout: "10" +nginx_client_header_timeout: "10" +nginx_send_timeout: "10" +nginx_limit_conn_zone: "$binary_remote_addr zone=default:10m" +nginx_limit_conn: "default 5" +nginx_add_header: [ +# vvoid clickjacking +"X-Frame-Options SAMEORIGIN", +# disable content-type sniffing +"X-Content-Type-Options nosniff", +# XSS filter +"X-XSS-Protection \"1; mode=block\"" ] + +nginx_ssl_protocols: "TLSv1 TLSv1.1 TLSv1.2" +nginx_ssl_ciphers: "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA" +nginx_ssl_prefer_server_ciphers: "on" +nginx_dh_param: "{{nginx_root_dir}}/dh{{nginx_dh_size}}.pem" +nginx_dh_size: "2048" diff --git a/ansible/roles/dev-sec.nginx-hardening/handlers/main.yml b/ansible/roles/dev-sec.nginx-hardening/handlers/main.yml new file mode 100644 index 0000000..e2dd289 --- /dev/null +++ b/ansible/roles/dev-sec.nginx-hardening/handlers/main.yml @@ -0,0 +1,2 @@ +- name: reload nginx + service: name={{ nginx_service_name }} state=reloaded diff --git a/ansible/roles/dev-sec.nginx-hardening/meta/.galaxy_install_info b/ansible/roles/dev-sec.nginx-hardening/meta/.galaxy_install_info new file mode 100644 index 0000000..bef6dcf --- /dev/null +++ b/ansible/roles/dev-sec.nginx-hardening/meta/.galaxy_install_info @@ -0,0 +1 @@ +{install_date: 'Sun Apr 23 07:59:34 2017', version: 1.0.2} diff --git a/ansible/roles/dev-sec.nginx-hardening/meta/main.yml b/ansible/roles/dev-sec.nginx-hardening/meta/main.yml new file mode 100644 index 0000000..a6beea0 --- /dev/null +++ b/ansible/roles/dev-sec.nginx-hardening/meta/main.yml @@ -0,0 +1,27 @@ +--- +galaxy_info: + author: "Sebastian Gumprich" + description: 'This Ansible role provides secure nginx configurations. http://dev-sec.io/' + company: Hardening Framework Team + license: Apache License 2.0 + min_ansible_version: '1.9' + platforms: + - name: EL + versions: + - 6 + - 7 + - name: Ubuntu + versions: + - precise + - trusty + - xenial + - name: Debian + versions: + - wheezy + - jessie + galaxy_tags: + - system + - security + - hardening + - nginx +dependencies: [] diff --git a/ansible/roles/dev-sec.nginx-hardening/requirements.yml b/ansible/roles/dev-sec.nginx-hardening/requirements.yml new file mode 100644 index 0000000..86981f9 --- /dev/null +++ b/ansible/roles/dev-sec.nginx-hardening/requirements.yml @@ -0,0 +1 @@ +- src: geerlingguy.nginx diff --git a/ansible/roles/dev-sec.nginx-hardening/spec/travis.yml b/ansible/roles/dev-sec.nginx-hardening/spec/travis.yml new file mode 100644 index 0000000..bb64498 --- /dev/null +++ b/ansible/roles/dev-sec.nginx-hardening/spec/travis.yml @@ -0,0 +1,4 @@ +- hosts: localhost + roles: + - geerlingguy.nginx + - ansible-nginx-hardening diff --git a/ansible/roles/dev-sec.nginx-hardening/tasks/main.yml b/ansible/roles/dev-sec.nginx-hardening/tasks/main.yml new file mode 100644 index 0000000..e3484d8 --- /dev/null +++ b/ansible/roles/dev-sec.nginx-hardening/tasks/main.yml @@ -0,0 +1,35 @@ +--- +- name: add the OS specific variables + include_vars: "{{ ansible_os_family }}.yml" + +- name: config should not be worldwide read- or writeable + file: path="/etc/nginx" mode="o-rw" owner="root" group="root" recurse=yes + +- name: create additional configuration + template: src="hardening.conf.j2" dest="{{nginx_config_conf_dir}}/90.hardening.conf" owner="root" group="root" + notify: reload nginx + +- name: change configuration in main nginx.conf + lineinfile: dest="/etc/nginx/nginx.conf" regexp="^\s*server_tokens" line="server_tokens {{nginx_server_tokens}};" insertafter="http {" + notify: reload nginx + +- name: change client_max_body_size in main nginx.conf + lineinfile: dest="/etc/nginx/nginx.conf" regexp="^\s*client_max_body_size" line="client_max_body_size {{nginx_client_max_body_size}};" insertafter="http {" + notify: reload nginx + +- name: change client_body_buffer_size in main nginx.conf + lineinfile: dest="/etc/nginx/nginx.conf" regexp="^\s*client_body_buffer_size" line="client_body_buffer_size {{nginx_client_body_buffer_size}};" insertafter="http {" + notify: reload nginx + +- name: change keepalive_timeout in main nginx.conf + lineinfile: dest="/etc/nginx/nginx.conf" regexp="^\s*keepalive_timeout" line="keepalive_timeout {{nginx_keepalive_timeout}};" insertafter="http {" + notify: reload nginx + +- name: remove default.conf + file: path="{{nginx_default_conf}}" state=absent + when: nginx_remove_default_site + notify: reload nginx + +- name: generate dh group + command: openssl dhparam -out {{nginx_dh_param}} {{nginx_dh_size}} creates={{nginx_dh_param}} + notify: reload nginx diff --git a/ansible/roles/dev-sec.nginx-hardening/templates/hardening.conf.j2 b/ansible/roles/dev-sec.nginx-hardening/templates/hardening.conf.j2 new file mode 100644 index 0000000..c1ca086 --- /dev/null +++ b/ansible/roles/dev-sec.nginx-hardening/templates/hardening.conf.j2 @@ -0,0 +1,17 @@ +# {{ansible_managed}} +# Additional configuration for Nginx. + +client_header_buffer_size {{nginx_client_header_buffer_size}}; +large_client_header_buffers {{nginx_large_client_header_buffers}}; +client_body_timeout {{nginx_client_body_timeout}}; +client_header_timeout {{nginx_client_header_timeout}}; +send_timeout {{nginx_send_timeout}}; +limit_conn_zone {{nginx_limit_conn_zone}}; +limit_conn {{nginx_limit_conn}}; +ssl_protocols {{nginx_ssl_protocols}}; +ssl_ciphers {{nginx_ssl_ciphers}}; +ssl_dhparam {{nginx_dh_param}}; +ssl_prefer_server_ciphers {{nginx_ssl_prefer_server_ciphers}}; +{% for header in nginx_add_header %} +add_header {{header}}; +{% endfor %} diff --git a/ansible/roles/dev-sec.nginx-hardening/vars/Debian.yml b/ansible/roles/dev-sec.nginx-hardening/vars/Debian.yml new file mode 100644 index 0000000..3d85fa5 --- /dev/null +++ b/ansible/roles/dev-sec.nginx-hardening/vars/Debian.yml @@ -0,0 +1,4 @@ +nginx_root_dir: '/etc/nginx' +nginx_config_conf_dir: '/etc/nginx/conf.d' +nginx_default_conf: '/etc/nginx/sites-enabled/default' +nginx_service_name: 'nginx' diff --git a/ansible/roles/dev-sec.nginx-hardening/vars/Oracle Linux.yml b/ansible/roles/dev-sec.nginx-hardening/vars/Oracle Linux.yml new file mode 100644 index 0000000..6ec8bc2 --- /dev/null +++ b/ansible/roles/dev-sec.nginx-hardening/vars/Oracle Linux.yml @@ -0,0 +1,4 @@ +nginx_root_dir: '/etc/nginx' +nginx_config_conf_dir: '/etc/nginx/conf.d' +nginx_default_conf: '/etc/nginx/conf.d/default.conf' +nginx_service_name: 'nginx' diff --git a/ansible/roles/dev-sec.nginx-hardening/vars/RedHat.yml b/ansible/roles/dev-sec.nginx-hardening/vars/RedHat.yml new file mode 100644 index 0000000..6ec8bc2 --- /dev/null +++ b/ansible/roles/dev-sec.nginx-hardening/vars/RedHat.yml @@ -0,0 +1,4 @@ +nginx_root_dir: '/etc/nginx' +nginx_config_conf_dir: '/etc/nginx/conf.d' +nginx_default_conf: '/etc/nginx/conf.d/default.conf' +nginx_service_name: 'nginx' diff --git a/ansible/roles/dev-sec.nginx-hardening/vars/main.yml b/ansible/roles/dev-sec.nginx-hardening/vars/main.yml new file mode 100644 index 0000000..e69de29 diff --git a/ansible/roles/dev-sec.os-hardening/.gitignore b/ansible/roles/dev-sec.os-hardening/.gitignore new file mode 100644 index 0000000..1953eaa --- /dev/null +++ b/ansible/roles/dev-sec.os-hardening/.gitignore @@ -0,0 +1,3 @@ +.kitchen +hosts +Gemfile.lock diff --git a/ansible/roles/dev-sec.os-hardening/.kitchen.vagrant.yml b/ansible/roles/dev-sec.os-hardening/.kitchen.vagrant.yml new file mode 100644 index 0000000..d4afa3c --- /dev/null +++ b/ansible/roles/dev-sec.os-hardening/.kitchen.vagrant.yml @@ -0,0 +1,80 @@ +--- +driver: + name: vagrant + driver_config: + http_proxy: <%= ENV['http_proxy'] || nil %> + https_proxy: <%= ENV['https_proxy'] || nil %> + vagrantfiles: + - kitchen_vagrant_block.rb + +provisioner: + name: ansible_playbook + hosts: all + require_ansible_repo: false + require_ansible_omnibus: true + require_chef_for_busser: false + require_ruby_for_busser: false + ansible_verbose: true + roles_path: ../ansible-os-hardening/ + playbook: default.yml + http_proxy: <%= ENV['http_proxy'] || nil %> + https_proxy: <%= ENV['https_proxy'] || nil %> + +transport: + max_ssh_sessions: 5 + +platforms: +- name: ubuntu-12.04 + driver_config: + box: opscode-ubuntu-12.04 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_ubuntu-12.04_chef-provisionerless.box +- name: ubuntu-14.04 + driver_config: + box: opscode-ubuntu-14.04 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_ubuntu-14.04_chef-provisionerless.box +- name: ubuntu-16.04 + driver_config: + box: opscode-ubuntu-16.04 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_ubuntu-16.04_chef-provisionerless.box +- name: centos-6.4 + driver_config: + box: opscode-centos-6.4 + box_url: https://opscode-vm.s3.amazonaws.com/vagrant/opscode_centos-6.4_provisionerless.box +- name: centos-6.5 + driver_config: + box: opscode-centos-6.5 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_centos-6.5_chef-provisionerless.box +- name: centos-6.8 + driver_config: + box: bento/centos-6.8 +- name: centos-7 + driver_config: + box: bento/centos-7.2 +- name: oracle-6.4 + driver_config: + box: oracle-6.4 + box_url: https://storage.us2.oraclecloud.com/v1/istoilis-istoilis/vagrant/oel64-64.box +- name: oracle-6.5 + driver_config: + box: oracle-6.5 + box_url: https://storage.us2.oraclecloud.com/v1/istoilis-istoilis/vagrant/oel65-64.box +- name: oracle-7 + driver_config: + box: boxcutter/ol72 +- name: debian-7 + driver_config: + box: debian-7 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_debian-7.8_chef-provisionerless.box +- name: debian-8 + driver_config: + box: debian-8 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_debian-8.1_chef-provisionerless.box + +verifier: + name: inspec + sudo: true + inspec_tests: + - https://github.com/dev-sec/linux-baseline/ + +suites: +- name: os diff --git a/ansible/roles/dev-sec.os-hardening/.kitchen.yml b/ansible/roles/dev-sec.os-hardening/.kitchen.yml new file mode 100644 index 0000000..8b0802a --- /dev/null +++ b/ansible/roles/dev-sec.os-hardening/.kitchen.yml @@ -0,0 +1,77 @@ +--- +driver: + name: docker + use_sudo: false + privileged: true + http_proxy: <%= ENV['http_proxy'] || nil %> + https_proxy: <%= ENV['https_proxy'] || nil %> + +transport: + max_ssh_sessions: 5 + +provisioner: + name: ansible_playbook + hosts: all + require_ansible_repo: false + require_chef_for_busser: false + require_ruby_for_busser: false + ansible_verbose: true + ansible_diff: true + hosts: all + roles_path: ../ansible-os-hardening/ + http_proxy: <%= ENV['http_proxy'] || nil %> + https_proxy: <%= ENV['https_proxy'] || nil %> + playbook: default.yml + ansible_extra_flags: + - "--skip-tags=sysctl" + +platforms: +- name: centos6-ansible-latest + driver: + image: rndmh3ro/docker-centos6-ansible:latest + platform: centos +- name: centos7-ansible-latest + driver: + image: rndmh3ro/docker-centos7-ansible:latest + platform: centos +- name: oracle6-ansible-latest + driver: + image: rndmh3ro/docker-oracle6-ansible:latest + platform: centos +- name: oracle7-ansible-latest + driver: + image: rndmh3ro/docker-oracle7-ansible:latest + platform: centos +- name: ubuntu1204-ansible-latest + driver: + image: rndmh3ro/docker-ubuntu1204-ansible:latest + platform: ubuntu +- name: ubuntu1404-ansible-latest + driver: + image: rndmh3ro/docker-ubuntu1404-ansible:latest + platform: ubuntu +- name: ubuntu1604-ansible-latest + driver: + image: rndmh3ro/docker-ubuntu1604-ansible:latest + platform: ubuntu +- name: debian7-ansible-latest + driver: + image: rndmh3ro/docker-debian7-ansible:latest + platform: debian +- name: debian8-ansible-latest + driver: + image: rndmh3ro/docker-debian8-ansible:latest + platform: debian +- name: debian9-ansible-latest + driver: + image: rndmh3ro/docker-debian9-ansible:latest + platform: debian + +verifier: + name: inspec + sudo: true + inspec_tests: + - https://github.com/dev-sec/tests-os-hardening + +suites: +- name: os diff --git a/ansible/roles/dev-sec.os-hardening/.travis.yml b/ansible/roles/dev-sec.os-hardening/.travis.yml new file mode 100644 index 0000000..ce7281d --- /dev/null +++ b/ansible/roles/dev-sec.os-hardening/.travis.yml @@ -0,0 +1,65 @@ +--- +services: docker + +env: + - distro: centos6 + version: latest + init: /sbin/init + + - distro: centos7 + init: /usr/lib/systemd/systemd + run_opts: "--privileged --volume=/sys/fs/cgroup:/sys/fs/cgroup:ro" + version: latest + + - distro: oracle6 + version: latest + init: /sbin/init + + - distro: oracle7 + init: /usr/lib/systemd/systemd + run_opts: "--privileged --volume=/sys/fs/cgroup:/sys/fs/cgroup:ro" + version: latest + + - distro: ubuntu1604 + version: latest + init: /lib/systemd/systemd + run_opts: "--privileged --volume=/sys/fs/cgroup:/sys/fs/cgroup:ro" + + - distro: ubuntu1404 + version: latest + init: /sbin/init + + - distro: ubuntu1204 + version: latest + init: /sbin/init + + - distro: debian7 + version: latest + init: /sbin/init + + - distro: debian8 + version: latest + init: /sbin/init + + - distro: debian9 + version: latest + init: /lib/systemd/systemd + run_opts: "--privileged --volume=/sys/fs/cgroup:/sys/fs/cgroup:ro" + +before_install: + # Pull container + - 'docker pull rndmh3ro/docker-${distro}-ansible:${version}' + +script: + - container_id=$(mktemp) + # Run container in detached state. + - 'docker run --detach --volume="${PWD}":/etc/ansible/roles/ansible-os-hardening:ro ${run_opts} rndmh3ro/docker-${distro}-ansible:${version} "${init}" > "${container_id}"' + + # Test role. + - 'docker exec "$(cat ${container_id})" ansible-playbook /etc/ansible/roles/ansible-os-hardening/default.yml --skip-tags "sysctl"' + + # Verify role + - 'inspec exec https://github.com/dev-sec/linux-baseline/ -t docker://$(cat ${container_id}) --controls=os-01 os-02 os-03 os-04 os-05 os-06 os-07 package-01 package-02 package-03 package-04 package-05 package-06' + +notifications: + webhooks: https://galaxy.ansible.com/api/v1/notifications/ diff --git a/ansible/roles/dev-sec.os-hardening/CHANGELOG.md b/ansible/roles/dev-sec.os-hardening/CHANGELOG.md new file mode 100644 index 0000000..16742e8 --- /dev/null +++ b/ansible/roles/dev-sec.os-hardening/CHANGELOG.md @@ -0,0 +1,150 @@ +# Change Log + +## [3.2.0](https://github.com/dev-sec/ansible-os-hardening/tree/3.2.0) (2016-10-24) +[Full Changelog](https://github.com/dev-sec/ansible-os-hardening/compare/3.1.0...3.2.0) + +**Fixed bugs:** + +- CentOS 7 selinux dependencies [\#102](https://github.com/dev-sec/ansible-os-hardening/issues/102) +- ubuntu xenial warning during activate gpg-check for yum-repos [\#99](https://github.com/dev-sec/ansible-os-hardening/issues/99) +- rhel\_system\_auth.j2 is still using pam\_passwdqc.so for CentOS 7 [\#98](https://github.com/dev-sec/ansible-os-hardening/issues/98) +- Enable pam\_pwquality in rhel-family \> 7 [\#73](https://github.com/dev-sec/ansible-os-hardening/issues/73) +- "irc" user always changed after reboot [\#53](https://github.com/dev-sec/ansible-os-hardening/issues/53) + +**Merged pull requests:** + +- update template [\#101](https://github.com/dev-sec/ansible-os-hardening/pull/101) ([rndmh3ro](https://github.com/rndmh3ro)) +- fix deprecation warning for undefined error. \#99 [\#100](https://github.com/dev-sec/ansible-os-hardening/pull/100) ([rndmh3ro](https://github.com/rndmh3ro)) +- add rhel7 pam\_pwquality. fix \#73 [\#94](https://github.com/dev-sec/ansible-os-hardening/pull/94) ([rndmh3ro](https://github.com/rndmh3ro)) + +## [3.1.0](https://github.com/dev-sec/ansible-os-hardening/tree/3.1.0) (2016-08-03) +[Full Changelog](https://github.com/dev-sec/ansible-os-hardening/compare/3.1...3.1.0) + +## [3.1](https://github.com/dev-sec/ansible-os-hardening/tree/3.1) (2016-07-27) +[Full Changelog](https://github.com/dev-sec/ansible-os-hardening/compare/3.0.0...3.1) + +**Implemented enhancements:** + +- Supports --check mode [\#93](https://github.com/dev-sec/ansible-os-hardening/pull/93) ([conorsch](https://github.com/conorsch)) +- Adds support for CentOS 7 [\#91](https://github.com/dev-sec/ansible-os-hardening/pull/91) ([conorsch](https://github.com/conorsch)) +- Docker [\#90](https://github.com/dev-sec/ansible-os-hardening/pull/90) ([rndmh3ro](https://github.com/rndmh3ro)) +- debian 8 support [\#88](https://github.com/dev-sec/ansible-os-hardening/pull/88) ([rndmh3ro](https://github.com/rndmh3ro)) +- Ufw manage defaults [\#85](https://github.com/dev-sec/ansible-os-hardening/pull/85) ([fitz123](https://github.com/fitz123)) +- replace ignore\_errors to failed\_when to supress ugly error warnings [\#81](https://github.com/dev-sec/ansible-os-hardening/pull/81) ([fitz123](https://github.com/fitz123)) +- fix bare variables usage for loops [\#79](https://github.com/dev-sec/ansible-os-hardening/pull/79) ([fitz123](https://github.com/fitz123)) + +**Fixed bugs:** + +- Centos 7.1 fails at \[Change various sysctl-settings on rhel-hosts...\] [\#74](https://github.com/dev-sec/ansible-os-hardening/issues/74) +- Hardening fails on Centos 7.1 at task 'minimize access' [\#71](https://github.com/dev-sec/ansible-os-hardening/issues/71) + +**Closed issues:** + +- Permissions on /etc/shadow can lock out GUI users [\#86](https://github.com/dev-sec/ansible-os-hardening/issues/86) +- network related sysctl rewritten by ufw in ubuntu [\#82](https://github.com/dev-sec/ansible-os-hardening/issues/82) +- ansible \>= 2.0 complains: Using bare variables is deprecated [\#78](https://github.com/dev-sec/ansible-os-hardening/issues/78) +- Norm-Audit-Hardening-Audit [\#76](https://github.com/dev-sec/ansible-os-hardening/issues/76) + +**Merged pull requests:** + +- Fix a formatting issue in readme. [\#92](https://github.com/dev-sec/ansible-os-hardening/pull/92) ([vivekagr](https://github.com/vivekagr)) +- Permits overriding permissions on /etc/shadow [\#89](https://github.com/dev-sec/ansible-os-hardening/pull/89) ([conorsch](https://github.com/conorsch)) + +## [3.0.0](https://github.com/dev-sec/ansible-os-hardening/tree/3.0.0) (2016-03-13) +[Full Changelog](https://github.com/dev-sec/ansible-os-hardening/compare/2.0.0...3.0.0) + +**Implemented enhancements:** + +- update platforms in meta-file [\#69](https://github.com/dev-sec/ansible-os-hardening/pull/69) ([rndmh3ro](https://github.com/rndmh3ro)) +- add webhook for ansible galaxy [\#68](https://github.com/dev-sec/ansible-os-hardening/pull/68) ([rndmh3ro](https://github.com/rndmh3ro)) +- Move sysctl vars to defaults [\#67](https://github.com/dev-sec/ansible-os-hardening/pull/67) ([rndmh3ro](https://github.com/rndmh3ro)) +- make sys\_uid and sys\_gid configurable [\#62](https://github.com/dev-sec/ansible-os-hardening/pull/62) ([rndmh3ro](https://github.com/rndmh3ro)) +- Ansible 2.0 support [\#59](https://github.com/dev-sec/ansible-os-hardening/pull/59) ([rndmh3ro](https://github.com/rndmh3ro)) +- use inspec as test framework [\#58](https://github.com/dev-sec/ansible-os-hardening/pull/58) ([chris-rock](https://github.com/chris-rock)) +- Packages as attributes [\#57](https://github.com/dev-sec/ansible-os-hardening/pull/57) ([rndmh3ro](https://github.com/rndmh3ro)) +- Change categories to tags for upcoming ansible 2.0 [\#56](https://github.com/dev-sec/ansible-os-hardening/pull/56) ([rndmh3ro](https://github.com/rndmh3ro)) +- Add SINGLE and PROMPT parameters. [\#55](https://github.com/dev-sec/ansible-os-hardening/pull/55) ([rndmh3ro](https://github.com/rndmh3ro)) +- add changelog generator [\#54](https://github.com/dev-sec/ansible-os-hardening/pull/54) ([chris-rock](https://github.com/chris-rock)) + +**Fixed bugs:** + +- Updates "tags" parameters on includes in main.yml [\#66](https://github.com/dev-sec/ansible-os-hardening/pull/66) ([conorsch](https://github.com/conorsch)) +- Suid set def var, fix \#64 [\#63](https://github.com/dev-sec/ansible-os-hardening/pull/63) ([rndmh3ro](https://github.com/rndmh3ro)) +- ERROR! Include tasks should not specify tags in more than one way [\#60](https://github.com/dev-sec/ansible-os-hardening/pull/60) ([fitz123](https://github.com/fitz123)) + +**Closed issues:** + +- Hardening fails on Centos 7.1 at task 'remove suid/sgid bit from all binaries except in system and user whitelist' [\#72](https://github.com/dev-sec/ansible-os-hardening/issues/72) +- ansible 2.0 | "remove suid/sgid" task fails [\#64](https://github.com/dev-sec/ansible-os-hardening/issues/64) +- Custom sysctl [\#50](https://github.com/dev-sec/ansible-os-hardening/issues/50) + +**Merged pull requests:** + +- Release 3.0.0 [\#75](https://github.com/dev-sec/ansible-os-hardening/pull/75) ([rndmh3ro](https://github.com/rndmh3ro)) + +## [2.0.0](https://github.com/dev-sec/ansible-os-hardening/tree/2.0.0) (2015-11-28) +[Full Changelog](https://github.com/dev-sec/ansible-os-hardening/compare/1.0.0...2.0.0) + +**Closed issues:** + +- Fix directory structure. [\#48](https://github.com/dev-sec/ansible-os-hardening/issues/48) +- pam auth update error [\#47](https://github.com/dev-sec/ansible-os-hardening/issues/47) + +**Merged pull requests:** + +- Add explicit role-path to kitchen.yml [\#52](https://github.com/dev-sec/ansible-os-hardening/pull/52) ([rndmh3ro](https://github.com/rndmh3ro)) +- Fix pam passwdqc template [\#51](https://github.com/dev-sec/ansible-os-hardening/pull/51) ([rndmh3ro](https://github.com/rndmh3ro)) +- New dir layout [\#49](https://github.com/dev-sec/ansible-os-hardening/pull/49) ([rndmh3ro](https://github.com/rndmh3ro)) +- remove duplicate "update pam" task [\#46](https://github.com/dev-sec/ansible-os-hardening/pull/46) ([fitz123](https://github.com/fitz123)) +- Fix stuck in case pam files was updated before by force update [\#45](https://github.com/dev-sec/ansible-os-hardening/pull/45) ([fitz123](https://github.com/fitz123)) +- Fix nologin shell path [\#44](https://github.com/dev-sec/ansible-os-hardening/pull/44) ([fitz123](https://github.com/fitz123)) +- improved travis-tests to cover more cases [\#42](https://github.com/dev-sec/ansible-os-hardening/pull/42) ([rndmh3ro](https://github.com/rndmh3ro)) + +## [1.0.0](https://github.com/dev-sec/ansible-os-hardening/tree/1.0.0) (2015-09-01) +**Closed issues:** + +- ansible-os-hardening/tasks/minimize\_access.yml [\#38](https://github.com/dev-sec/ansible-os-hardening/issues/38) +- Role configuration. vars/main.yml? [\#34](https://github.com/dev-sec/ansible-os-hardening/issues/34) +- Sysctl reloading [\#18](https://github.com/dev-sec/ansible-os-hardening/issues/18) +- Add conditions for disabling of ip forwarding [\#15](https://github.com/dev-sec/ansible-os-hardening/issues/15) +- Disable System Accounts [\#6](https://github.com/dev-sec/ansible-os-hardening/issues/6) + +**Merged pull requests:** + +- Update kitchen-ansible, remove separate debian install [\#40](https://github.com/dev-sec/ansible-os-hardening/pull/40) ([rndmh3ro](https://github.com/rndmh3ro)) +- Add mode to su-binary task. Fix \#38 [\#39](https://github.com/dev-sec/ansible-os-hardening/pull/39) ([rndmh3ro](https://github.com/rndmh3ro)) +- update common kitchen.yml platforms \(ansible\), kitchen\_debian.yml platforms \(ansible\) [\#37](https://github.com/dev-sec/ansible-os-hardening/pull/37) ([chris-rock](https://github.com/chris-rock)) +- Change oneliner if-statements to be more readable [\#36](https://github.com/dev-sec/ansible-os-hardening/pull/36) ([rndmh3ro](https://github.com/rndmh3ro)) +- Separate system-vars from editable vars. Fix \#34 [\#35](https://github.com/dev-sec/ansible-os-hardening/pull/35) ([rndmh3ro](https://github.com/rndmh3ro)) +- Create limits.d-directory if it does not exist. [\#33](https://github.com/dev-sec/ansible-os-hardening/pull/33) ([rndmh3ro](https://github.com/rndmh3ro)) +- Add correct CONTRIB-file [\#32](https://github.com/dev-sec/ansible-os-hardening/pull/32) ([rndmh3ro](https://github.com/rndmh3ro)) +- Add Ansible Galaxy badge [\#31](https://github.com/dev-sec/ansible-os-hardening/pull/31) ([rndmh3ro](https://github.com/rndmh3ro)) +- Update readme, todo, changelog, vars [\#30](https://github.com/dev-sec/ansible-os-hardening/pull/30) ([rndmh3ro](https://github.com/rndmh3ro)) +- List-cleanup and follow symlinks added [\#29](https://github.com/dev-sec/ansible-os-hardening/pull/29) ([rndmh3ro](https://github.com/rndmh3ro)) +- Add module configuration [\#28](https://github.com/dev-sec/ansible-os-hardening/pull/28) ([rndmh3ro](https://github.com/rndmh3ro)) +- Fix two sysctl-settings [\#27](https://github.com/dev-sec/ansible-os-hardening/pull/27) ([rndmh3ro](https://github.com/rndmh3ro)) +- Add meta-files for Ansible Galaxy [\#26](https://github.com/dev-sec/ansible-os-hardening/pull/26) ([rndmh3ro](https://github.com/rndmh3ro)) +- Disable System Accounts. Fix \#6 [\#25](https://github.com/dev-sec/ansible-os-hardening/pull/25) ([rndmh3ro](https://github.com/rndmh3ro)) +- Use changed\_when to avoid changed tasks [\#24](https://github.com/dev-sec/ansible-os-hardening/pull/24) ([rndmh3ro](https://github.com/rndmh3ro)) +- Delete authconfig-task on rhel-systems [\#23](https://github.com/dev-sec/ansible-os-hardening/pull/23) ([rndmh3ro](https://github.com/rndmh3ro)) +- Add missing rhosts-include task [\#21](https://github.com/dev-sec/ansible-os-hardening/pull/21) ([rndmh3ro](https://github.com/rndmh3ro)) +- Change sysctl-task. Fix \#18 [\#20](https://github.com/dev-sec/ansible-os-hardening/pull/20) ([rndmh3ro](https://github.com/rndmh3ro)) +- Add travis-support [\#17](https://github.com/dev-sec/ansible-os-hardening/pull/17) ([rndmh3ro](https://github.com/rndmh3ro)) +- Add conditions for various tasks. Fix \#15 [\#16](https://github.com/dev-sec/ansible-os-hardening/pull/16) ([rndmh3ro](https://github.com/rndmh3ro)) +- fix configuration of playbook path [\#14](https://github.com/dev-sec/ansible-os-hardening/pull/14) ([chris-rock](https://github.com/chris-rock)) +- Make tasks clearer [\#13](https://github.com/dev-sec/ansible-os-hardening/pull/13) ([rndmh3ro](https://github.com/rndmh3ro)) +- Add remove suid/sgid function [\#12](https://github.com/dev-sec/ansible-os-hardening/pull/12) ([rndmh3ro](https://github.com/rndmh3ro)) +- Add task to remove unused repos and pkgs [\#11](https://github.com/dev-sec/ansible-os-hardening/pull/11) ([rndmh3ro](https://github.com/rndmh3ro)) +- Edit README to fit to os-hardening [\#10](https://github.com/dev-sec/ansible-os-hardening/pull/10) ([rndmh3ro](https://github.com/rndmh3ro)) +- ignore RAs on Ipv6 [\#9](https://github.com/dev-sec/ansible-os-hardening/pull/9) ([rndmh3ro](https://github.com/rndmh3ro)) +- Repair debian install script [\#8](https://github.com/dev-sec/ansible-os-hardening/pull/8) ([rndmh3ro](https://github.com/rndmh3ro)) +- Separate tasks into multiple smaller files [\#7](https://github.com/dev-sec/ansible-os-hardening/pull/7) ([rndmh3ro](https://github.com/rndmh3ro)) +- Enable gpg-check on all yum-repositories [\#5](https://github.com/dev-sec/ansible-os-hardening/pull/5) ([rndmh3ro](https://github.com/rndmh3ro)) +- Change playbook-path to accomodate test-repo [\#4](https://github.com/dev-sec/ansible-os-hardening/pull/4) ([rndmh3ro](https://github.com/rndmh3ro)) +- treat securetty config as an array [\#3](https://github.com/dev-sec/ansible-os-hardening/pull/3) ([arlimus](https://github.com/arlimus)) +- Add Securetty-support [\#2](https://github.com/dev-sec/ansible-os-hardening/pull/2) ([rndmh3ro](https://github.com/rndmh3ro)) +- Add profile.conf configuration [\#1](https://github.com/dev-sec/ansible-os-hardening/pull/1) ([rndmh3ro](https://github.com/rndmh3ro)) + + + +\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)* \ No newline at end of file diff --git a/ansible/roles/dev-sec.os-hardening/CONTRIBUTING.md b/ansible/roles/dev-sec.os-hardening/CONTRIBUTING.md new file mode 100644 index 0000000..783c10f --- /dev/null +++ b/ansible/roles/dev-sec.os-hardening/CONTRIBUTING.md @@ -0,0 +1,85 @@ +# Contributor Guideline + +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!! + +## Communication + +### GitHub repositories + +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. + +## git and GitHub + +In order to contribute code please: + +1. Fork the project on GitHub +2. Clone the project +3. Add changes (and tests) +4. Commit and push +5. Create a merge-request + +To have your code merged, see the expectations listed below. + +You can find a well-written guide [here](https://help.github.com/articles/fork-a-repo). + +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. + +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). + + +## Expectations + +### Don't reinvent the wheel + +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. + +* For Chef refer to the official [opscode community cookbooks](http://community.opscode.com/cookbooks). +* For Puppet head to the [Puppet Forge](https://forge.puppetlabs.com/) and take a node of the Puppet supported modules. +* For Ansible check the [Ansible Module Index](http://docs.ansible.com/list_of_all_modules.html) + +These projects are generally hosted on GitHub as well. + +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)). + + +### Be explicit + +* Please avoid using nonsensical property and variable names. +* Use self-describing attribute names for user configuration. +* 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. + + +### Add tests + +The security review of this project is done using integration tests. + +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. + +You may add a new feature request by creating a test for whatever value you need. + +All tests will be reviewed internally for their validity and overall project direction. + + +### Document your code + +As code is more often read than written, please provide documentation in all projects. + +Adhere to the respective guidelines for documentation: + +* Chef generally documents code based explicit readme files. For code documentation please use [yard-chef](https://github.com/rightscale/yard-chef) +* [Puppet module documentation](http://docs.puppetlabs.com/puppet/latest/reference/modules_documentation.html) + + +### Follow coding styles + +We generally include test for coding guidelines: + +* Chef follows [Foodcritic](http://acrmp.github.io/foodcritic/) +* Puppet is checked with [puppet-lint](http://puppet-lint.com/checks/) +* Ansible is checked by running the playbook with the syntax-check option, e.g. `ansible-playbook foo.yml --syntax-check` + +Remember: Code is generally read much more often than written. + +### Use Markdown + +Wherever possible, please refrain from any other formats and stick to simple markdown. diff --git a/ansible/roles/dev-sec.os-hardening/Gemfile b/ansible/roles/dev-sec.os-hardening/Gemfile new file mode 100644 index 0000000..c11b3bf --- /dev/null +++ b/ansible/roles/dev-sec.os-hardening/Gemfile @@ -0,0 +1,18 @@ +# encoding: utf-8 + +source 'https://rubygems.org' + +group :integration do + gem 'test-kitchen', '~> 1.0' + gem 'kitchen-ansible' + gem 'kitchen-vagrant' + gem 'kitchen-inspec' + gem 'kitchen-sharedtests', '~> 0.2.0' + gem 'kitchen-sync' + gem 'kitchen-transport-rsync' + gem 'kitchen-docker' +end + +group :tools do + gem 'github_changelog_generator', '~> 1' +end diff --git a/ansible/roles/dev-sec.os-hardening/README.md b/ansible/roles/dev-sec.os-hardening/README.md new file mode 100644 index 0000000..0002f03 --- /dev/null +++ b/ansible/roles/dev-sec.os-hardening/README.md @@ -0,0 +1,214 @@ +# os-hardening (Ansible Role) + +[![Build Status](http://img.shields.io/travis/dev-sec/ansible-os-hardening.svg)][1] +[![Gitter Chat](https://badges.gitter.im/Join%20Chat.svg)][2] +[![Ansible Galaxy](https://img.shields.io/badge/galaxy-os--hardening-660198.svg)][3] + +## Description + +This roles provides numerous security-related configurations, providing all-round base protection. + +It configures: + + * Configures package management e.g. allows only signed packages + * Remove packages with known issues + * Configures `pam` and `pam_limits` module + * Shadow password suite configuration + * Configures system path permissions + * Disable core dumps via soft limits + * Restrict Root Logins to System Console + * Set SUIDs + * Configures kernel parameters via sysctl + +It will not: + + * Update system packages + * Install security patches + +## Requirements + +* Ansible 2.2.1 + +## Variables + +| Name | Default Value | Description | +| -------------- | ------------- | -----------------------------------| +| `os_desktop_enable`| false | true if this is a desktop system, ie Xorg, KDE/GNOME/Unity/etc| +| `os_env_extra_user_paths`| [] | add additional paths to the user's `PATH` variable (default is empty).| +| `os_env_umask`| 027| set default permissions for new files to `750` | +| `os_auth_pw_max_age`| 60 | maximum password age (set to `99999` to effectively disable it) | +| `os_auth_pw_min_age`| 7 | minimum password age (before allowing any other password change)| +| `os_auth_retries`| 5 | the maximum number of authentication attempts, before the account is locked for some time| +| `os_auth_lockout_time`| 600 | time in seconds that needs to pass, if the account was locked due to too many failed authentication attempts| +| `os_auth_timeout`| 60 | authentication timeout in seconds, so login will exit if this time passes| +| `os_auth_allow_homeless`| false | true if to allow users without home to login| +| `os_auth_pam_passwdqc_enable`| true | true if you want to use strong password checking in PAM using passwdqc| +| `os_auth_pam_passwdqc_options`| "min=disabled,disabled,16,12,8" | set to any option line (as a string) that you want to pass to passwdqc| +| `os_security_users_allow`| [] | list of things, that a user is allowed to do. May contain `change_user`. +| `os_security_kernel_enable_module_loading`| true | true if you want to allowed to change kernel modules once the system is running (eg `modprobe`, `rmmod`)| +| `os_security_kernel_enable_sysrq`| false | sysrq is a 'magical' key combo you can hit which the kernel will respond to regardless of whatever else it is doing, unless it is completely locked up. | +| `os_security_kernel_enable_core_dump`| false | kernel is crashing or otherwise misbehaving and a kernel core dump is created | +| `os_security_suid_sgid_enforce`| true | true if you want to reduce SUID/SGID bits. There is already a list of items which are searched for configured, but you can also add your own| +| `os_security_suid_sgid_blacklist`| [] | a list of paths which should have their SUID/SGID bits removed| +| `os_security_suid_sgid_whitelist`| [] | a list of paths which should not have their SUID/SGID bits altered| +| `os_security_suid_sgid_remove_from_unknown`| false | true if you want to remove SUID/SGID bits from any file, that is not explicitly configured in a `blacklist`. This will make every Ansible-run search through the mounted filesystems looking for SUID/SGID bits that are not configured in the default and user blacklist. If it finds an SUID/SGID bit, it will be removed, unless this file is in your `whitelist`.| +| `os_security_packages_clean'`| true | removes packages with known issues. See section packages.| +| `ufw_manage_defaults` | true | true means apply all settings with `ufw_` prefix| +| `ufw_ipt_sysctl` | '' | by default it disables IPT_SYSCTL in /etc/default/ufw. If you want to overwrite /etc/sysctl.conf values using ufw - set it to your sysctl dictionary, for example `/etc/ufw/sysctl.conf` +| `ufw_default_input_policy` | DROP | set default input policy of ufw to `DROP` | +| `ufw_default_output_policy` | ACCEPT | set default output policy of ufw to `ACCEPT` | +| `ufw_default_forward_policy` | DROP| set default forward policy of ufw to `DROP` | + +## Packages + +We remove the following packages: + + * xinetd ([NSA](http://www.nsa.gov/ia/_files/os/redhat/rhel5-guide-i731.pdf), Chapter 3.2.1) + * inetd ([NSA](http://www.nsa.gov/ia/_files/os/redhat/rhel5-guide-i731.pdf), Chapter 3.2.1) + * tftp-server ([NSA](http://www.nsa.gov/ia/_files/os/redhat/rhel5-guide-i731.pdf), Chapter 3.2.5) + * ypserv ([NSA](http://www.nsa.gov/ia/_files/os/redhat/rhel5-guide-i731.pdf), Chapter 3.2.4) + * telnet-server ([NSA](http://www.nsa.gov/ia/_files/os/redhat/rhel5-guide-i731.pdf), Chapter 3.2.2) + * rsh-server ([NSA](http://www.nsa.gov/ia/_files/os/redhat/rhel5-guide-i731.pdf), Chapter 3.2.3) + +## Example Playbook + + - hosts: localhost + roles: + - dev-sec.os-hardening + + +## Changing sysctl variables + +If you want to overwrite sysctl-variables, you have to overwrite the *whole* dict, or else only the single overwritten will be actually used. +So for example if you want to change the IPv4 traffic forwarding variable to `1`, you must pass the whole dict like this: + +``` + - hosts: localhost + roles: + - dev-sec.os-hardening + vars: + sysctl_config: + # Disable IPv4 traffic forwarding. + net.ipv4.ip_forward: 1 + + # Disable IPv6 traffic forwarding. + net.ipv6.conf.all.forwarding: 0 + + # ignore RAs on Ipv6. + net.ipv6.conf.all.accept_ra: 0 + net.ipv6.conf.default.accept_ra: 0 + + # Enable RFC-recommended source validation feature. + net.ipv4.conf.all.rp_filter: 1 + net.ipv4.conf.default.rp_filter: 1 + + # Reduce the surface on SMURF attacks. + # Make sure to ignore ECHO broadcasts, which are only required in broad network analysis. + net.ipv4.icmp_echo_ignore_broadcasts: 1 + + # There is no reason to accept bogus error responses from ICMP, so ignore them instead. + net.ipv4.icmp_ignore_bogus_error_responses: 1 + + # Limit the amount of traffic the system uses for ICMP. + net.ipv4.icmp_ratelimit: 100 + + # Adjust the ICMP ratelimit to include ping, dst unreachable, + # source quench, ime exceed, param problem, timestamp reply, information reply + net.ipv4.icmp_ratemask: 88089 + + # Disable IPv6 + net.ipv6.conf.all.disable_ipv6: 1 + + # Protect against wrapping sequence numbers at gigabit speeds + net.ipv4.tcp_timestamps: 0 + + # Define restriction level for announcing the local source IP + net.ipv4.conf.all.arp_ignore: 1 + + # Define mode for sending replies in response to + # received ARP requests that resolve local target IP addresses + net.ipv4.conf.all.arp_announce: 2 + + # RFC 1337 fix F1 + net.ipv4.tcp_rfc1337: 1 +``` + +Alternatively you can change Ansible's [hash-behaviour](https://docs.ansible.com/ansible/intro_configuration.html#hash-behaviour) to `merge`, then you only have to overwrite the single hash you need to. But please be aware that changing the hash-behaviour changes it for all your playbooks and is not recommended by Ansible. + +## Local Testing + +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. + +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). + +Next install test-kitchen: + +```bash +# Install dependencies +gem install bundler +bundle install +``` + +### Testing with Docker +``` +# fast test on one machine +bundle exec kitchen test default-ubuntu-1204 + +# test on all machines +bundle exec kitchen test + +# for development +bundle exec kitchen create default-ubuntu-1204 +bundle exec kitchen converge default-ubuntu-1204 +``` + +### Testing with Virtualbox +``` +# fast test on one machine +KITCHEN_YAML=".kitchen.vagrant.yml" bundle exec kitchen test default-ubuntu-1204 + +# test on all machines +KITCHEN_YAML=".kitchen.vagrant.yml" bundle exec kitchen test + +# for development +KITCHEN_YAML=".kitchen.vagrant.yml" bundle exec kitchen create default-ubuntu-1204 +KITCHEN_YAML=".kitchen.vagrant.yml" bundle exec kitchen converge default-ubuntu-1204 +``` +For more information see [test-kitchen](http://kitchen.ci/docs/getting-started) + +## Contributors + Kudos + +... + +This role is mostly based on guides by: + +* [Arch Linux wiki, Sysctl hardening](https://wiki.archlinux.org/index.php/Sysctl) +* [NSA: Guide to the Secure Configuration of Red Hat Enterprise Linux 5](http://www.nsa.gov/ia/_files/os/redhat/rhel5-guide-i731.pdf) +* [Ubuntu Security/Features](https://wiki.ubuntu.com/Security/Features) +* [Deutsche Telekom, Group IT Security, Security Requirements (German)](http://www.telekom.com/static/-/155996/7/technische-sicherheitsanforderungen-si) + +Thanks to all of you! +## Contributing + +See [contributor guideline](CONTRIBUTING.md). + +## License and Author + +* Author:: Sebastian Gumprich + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +[1]: http://travis-ci.org/dev-sec/ansible-os-hardening +[2]: https://gitter.im/dev-sec/general +[3]: https://galaxy.ansible.com/dev-sec/os-hardening diff --git a/ansible/roles/dev-sec.os-hardening/Rakefile b/ansible/roles/dev-sec.os-hardening/Rakefile new file mode 100644 index 0000000..02c5821 --- /dev/null +++ b/ansible/roles/dev-sec.os-hardening/Rakefile @@ -0,0 +1,11 @@ +#!/usr/bin/env rake +# encoding: utf-8 + +# Automatically generate a changelog for this project. Only loaded if +# the necessary gem is installed. +begin + require 'github_changelog_generator/task' + GitHubChangelogGenerator::RakeTask.new :changelog +rescue LoadError + puts '>>>>> GitHub Changelog Generator not loaded, omitting tasks' +end diff --git a/ansible/roles/dev-sec.os-hardening/TODO.md b/ansible/roles/dev-sec.os-hardening/TODO.md new file mode 100644 index 0000000..94005dc --- /dev/null +++ b/ansible/roles/dev-sec.os-hardening/TODO.md @@ -0,0 +1,4 @@ +# TODO + +* [Adduser consistency](https://github.com/hardening-io/chef-os-hardening/pull/73) +* [add support for limiting password re-use](https://github.com/hardening-io/puppet-os-hardening/pull/61) diff --git a/ansible/roles/dev-sec.os-hardening/ansible.cfg b/ansible/roles/dev-sec.os-hardening/ansible.cfg new file mode 100644 index 0000000..b96835b --- /dev/null +++ b/ansible/roles/dev-sec.os-hardening/ansible.cfg @@ -0,0 +1,15 @@ +# config file for ansible -- http://ansible.com/ +# ============================================== + +# nearly all parameters can be overridden in ansible-playbook +# or with command line flags. ansible will read ANSIBLE_CONFIG, +# ansible.cfg in the current working directory, .ansible.cfg in +# the home directory or /etc/ansible/ansible.cfg, whichever it +# finds first + +[defaults] +ansible_managed = Ansible managed: {file} modified on %Y-%m-%d by {uid} on {host} + +# additional paths to search for roles in, colon separated +roles_path = ../ + diff --git a/ansible/roles/dev-sec.os-hardening/default.yml b/ansible/roles/dev-sec.os-hardening/default.yml new file mode 100644 index 0000000..379e5bc --- /dev/null +++ b/ansible/roles/dev-sec.os-hardening/default.yml @@ -0,0 +1,20 @@ +--- +- name: wrapper playbook for kitchen testing "ansible-os-hardening" with custom vars for testing + hosts: localhost + roles: + - ansible-os-hardening + vars: + os_security_users_allow: change_user + os_security_kernel_enable_core_dump: false + os_security_suid_sgid_remove_from_unknown: true + os_auth_pam_passwdqc_enable: false + os_desktop_enable: true + os_env_extra_user_paths: ['/home'] + os_auth_allow_homeless: true + os_security_suid_sgid_blacklist: ['/bin/umount'] + os_security_suid_sgid_whitelist: ['/usr/bin/rlogin'] + +- name: wrapper playbook for kitchen testing "ansible-os-hardening" + hosts: localhost + roles: + - ansible-os-hardening diff --git a/ansible/roles/dev-sec.os-hardening/defaults/main.yml b/ansible/roles/dev-sec.os-hardening/defaults/main.yml new file mode 100644 index 0000000..1745683 --- /dev/null +++ b/ansible/roles/dev-sec.os-hardening/defaults/main.yml @@ -0,0 +1,118 @@ +os_desktop_enable: false +os_env_extra_user_paths: [] +os_env_umask: '027' +os_auth_pw_max_age: 60 +os_auth_pw_min_age: 7 # discourage password cycling +os_auth_retries: 5 +os_auth_lockout_time: 600 # 10min +os_auth_timeout: 60 +os_auth_allow_homeless: false +os_auth_pam_passwdqc_enable: true +os_auth_pam_passwdqc_options: 'min=disabled,disabled,16,12,8' # used in RHEL6 +os_auth_pam_pwquality_options: 'try_first_pass retry=3 type=' # used in RHEL7 +os_auth_root_ttys: [console, tty1, tty2, tty3, tty4, tty5, tty6] +os_auth_uid_min: 1000 +os_auth_gid_min: 1000 +os_auth_sys_uid_min: 100 +os_auth_sys_uid_max: 999 +os_auth_sys_gid_min: 100 +os_auth_sys_gid_max: 999 + +os_chfn_restrict: '' +# may contain: change_user +os_security_users_allow: [] +# specify system accounts those login should not be disabled and password not changed +os_ignore_users: ['vagrant'] +os_security_kernel_enable_module_loading: true +os_security_kernel_enable_core_dump: false +os_security_suid_sgid_enforce: true +# user-defined blacklist and whitelist +os_security_suid_sgid_blacklist: [] +os_security_suid_sgid_whitelist: [] +# if this is true, remove any suid/sgid bits from files that were not in the whitelist +os_security_suid_sgid_remove_from_unknown: false + +# remove packages with known issues +os_security_packages_clean: true +os_security_packages_list: ['xinetd','inetd','ypserv','telnet-server','rsh-server','rsync'] + +# Allow interactive startup (rhel, centos) +os_security_init_prompt: true +# Require root password for single user mode. (rhel, centos) +os_security_init_single: false + +# Apply ufw defaults +ufw_manage_defaults: true + +# Empty variable disables IPT_SYSCTL in /etc/default/ufw +# by default in Ubuntu it set to: /etc/ufw/sysctl.conf +# CAUTION +# if you enable it - it'll overwrite /etc/sysctl.conf file, managed by hardening framework +ufw_ipt_sysctl: '' + +# Default ufw variables +ufw_default_input_policy: 'DROP' +ufw_default_output_policy: 'ACCEPT' +ufw_default_forward_policy: 'DROP' +ufw_default_application_policy: 'SKIP' +ufw_manage_builtins: 'no' +ufw_ipt_modules: 'nf_conntrack_ftp nf_nat_ftp nf_conntrack_netbios_ns' + +# CAUTION +# If you want to overwrite sysctl-variables, +# you have to overwrite the *whole* dict, or else only the single overwritten will be actually used. + +sysctl_config: + # Disable IPv4 traffic forwarding. + net.ipv4.ip_forward: 0 + + # Disable IPv6 traffic forwarding. + net.ipv6.conf.all.forwarding: 0 + + # ignore RAs on Ipv6. + net.ipv6.conf.all.accept_ra: 0 + net.ipv6.conf.default.accept_ra: 0 + + # Enable RFC-recommended source validation feature. + net.ipv4.conf.all.rp_filter: 1 + net.ipv4.conf.default.rp_filter: 1 + + # Reduce the surface on SMURF attacks. + # Make sure to ignore ECHO broadcasts, which are only required in broad network analysis. + net.ipv4.icmp_echo_ignore_broadcasts: 1 + + # There is no reason to accept bogus error responses from ICMP, so ignore them instead. + net.ipv4.icmp_ignore_bogus_error_responses: 1 + + # Limit the amount of traffic the system uses for ICMP. + net.ipv4.icmp_ratelimit: 100 + + # Adjust the ICMP ratelimit to include ping, dst unreachable, + # source quench, ime exceed, param problem, timestamp reply, information reply + net.ipv4.icmp_ratemask: 88089 + + # Disable IPv6 + net.ipv6.conf.all.disable_ipv6: 1 + + # Protect against wrapping sequence numbers at gigabit speeds + net.ipv4.tcp_timestamps: 0 + + # Define restriction level for announcing the local source IP + net.ipv4.conf.all.arp_ignore: 1 + + # Define mode for sending replies in response to + # received ARP requests that resolve local target IP addresses + net.ipv4.conf.all.arp_announce: 2 + + # RFC 1337 fix F1 + net.ipv4.tcp_rfc1337: 1 + +# CAUTION +# If you want to overwrite sysctl-variables, +# you have to overwrite the *whole* dict, or else only the single overwritten will be actually used. + +sysctl_rhel_config: + # ExecShield protection against buffer overflows + kernel.exec-shield: 1 + # Syncookies is used to prevent SYN-flooding attacks. + net.ipv4.tcp_syncookies: 1 diff --git a/ansible/roles/dev-sec.os-hardening/kitchen_vagrant_block.rb b/ansible/roles/dev-sec.os-hardening/kitchen_vagrant_block.rb new file mode 100644 index 0000000..1b7d870 --- /dev/null +++ b/ansible/roles/dev-sec.os-hardening/kitchen_vagrant_block.rb @@ -0,0 +1,5 @@ +# This is a Vagrant block to allow proxy settings to be carried into Kitchen +# You need this for all of yum/apt etc. to work! + +Vagrant.configure(2) do |config| +end diff --git a/ansible/roles/dev-sec.os-hardening/meta/.galaxy_install_info b/ansible/roles/dev-sec.os-hardening/meta/.galaxy_install_info new file mode 100644 index 0000000..717a591 --- /dev/null +++ b/ansible/roles/dev-sec.os-hardening/meta/.galaxy_install_info @@ -0,0 +1 @@ +{install_date: 'Sun Apr 23 07:59:14 2017', version: 4.0.0} diff --git a/ansible/roles/dev-sec.os-hardening/meta/main.yml b/ansible/roles/dev-sec.os-hardening/meta/main.yml new file mode 100644 index 0000000..2202126 --- /dev/null +++ b/ansible/roles/dev-sec.os-hardening/meta/main.yml @@ -0,0 +1,26 @@ +--- +galaxy_info: + author: "Sebastian Gumprich" + description: 'This Ansible role provides numerous security-related configurations, providing all-round base protection.' + company: Hardening Framework Team + license: Apache License 2.0 + min_ansible_version: '2.2.1' + platforms: + - name: EL + versions: + - 6 + - 7 + - name: Ubuntu + versions: + - precise + - trusty + - xenial + - name: Debian + versions: + - wheezy + - jessie + galaxy_tags: + - system + - security + - hardening +dependencies: [] diff --git a/ansible/roles/dev-sec.os-hardening/tasks/apt.yml b/ansible/roles/dev-sec.os-hardening/tasks/apt.yml new file mode 100644 index 0000000..0ab78d8 --- /dev/null +++ b/ansible/roles/dev-sec.os-hardening/tasks/apt.yml @@ -0,0 +1,6 @@ +--- +- name: remove packages + apt: name='{{item}}' state=removed + with_items: + - "{{os_security_packages_list}}" + when: os_security_packages_clean diff --git a/ansible/roles/dev-sec.os-hardening/tasks/limits.yml b/ansible/roles/dev-sec.os-hardening/tasks/limits.yml new file mode 100644 index 0000000..eedeab7 --- /dev/null +++ b/ansible/roles/dev-sec.os-hardening/tasks/limits.yml @@ -0,0 +1,9 @@ +--- + +- name: create limits.d-directory if it does not exist + file: path='/etc/security/limits.d' owner=root group=root mode=0755 state=directory + when: os_security_kernel_enable_core_dump + +- name: create sane limits.conf + template: src='limits.conf.j2' dest='/etc/security/limits.d/10.hardcore.conf' owner=root group=root mode=0440 + when: os_security_kernel_enable_core_dump diff --git a/ansible/roles/dev-sec.os-hardening/tasks/login_defs.yml b/ansible/roles/dev-sec.os-hardening/tasks/login_defs.yml new file mode 100644 index 0000000..9704b38 --- /dev/null +++ b/ansible/roles/dev-sec.os-hardening/tasks/login_defs.yml @@ -0,0 +1,4 @@ +--- +- name: create login.defs + template: src='login.defs.j2' dest='/etc/login.defs' owner=root group=root mode=0444 + diff --git a/ansible/roles/dev-sec.os-hardening/tasks/main.yml b/ansible/roles/dev-sec.os-hardening/tasks/main.yml new file mode 100644 index 0000000..e65a179 --- /dev/null +++ b/ansible/roles/dev-sec.os-hardening/tasks/main.yml @@ -0,0 +1,43 @@ +--- +- name: add the OS specific variables + include_vars: "{{ ansible_os_family }}.yml" + tags: always + +- include: limits.yml + tags: limits + +- include: login_defs.yml + tags: login_defs + +- include: minimize_access.yml + tags: minimize_acces + +- include: pam.yml + tags: pam + +- include: profile.yml + tags: profile + +- include: securetty.yml + tags: securetty + +- include: suid_sgid.yml + when: os_security_suid_sgid_enforce + tags: suid_sgid + +- include: sysctl.yml + tags: sysctl + +- include: user_accounts.yml + tags: user_accounts + +- include: rhosts.yml + tags: rhosts + +- include: yum.yml + when: ansible_os_family == 'RedHat' + tags: yum + +- include: apt.yml + when: ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu' + tags: apt diff --git a/ansible/roles/dev-sec.os-hardening/tasks/minimize_access.yml b/ansible/roles/dev-sec.os-hardening/tasks/minimize_access.yml new file mode 100644 index 0000000..a03c6b4 --- /dev/null +++ b/ansible/roles/dev-sec.os-hardening/tasks/minimize_access.yml @@ -0,0 +1,26 @@ +--- +# Using a two-pass approach for checking directories in order to support symlinks. +- name: find directories for minimizing access + stat: + path: "{{ item }}" + register: minimize_access_directories + with_items: + - '/usr/local/sbin' + - '/usr/local/bin' + - '/usr/sbin' + - '/usr/bin' + - '/sbin' + - '/bin' + - '{{os_env_extra_user_paths}}' + +- name: minimize access + file: path='{{item.stat.path}}' mode='go-w' recurse=yes + when: item.stat.isdir + with_items: "{{ minimize_access_directories.results }}" + +- name: change shadow ownership to root and mode to 0600 | DTAG SEC Req 3.21-7 + file: dest='/etc/shadow' owner={{ os_shadow_perms.owner }} group={{ os_shadow_perms.group }} mode={{ os_shadow_perms.mode }} + +- name: change su-binary to only be accessible to user and group root + file: dest='/bin/su' owner=root group=root mode=0750 + when: os_security_users_allow != None diff --git a/ansible/roles/dev-sec.os-hardening/tasks/pam.yml b/ansible/roles/dev-sec.os-hardening/tasks/pam.yml new file mode 100644 index 0000000..94e2c15 --- /dev/null +++ b/ansible/roles/dev-sec.os-hardening/tasks/pam.yml @@ -0,0 +1,61 @@ +--- +- name: update pam on Debian systems + command: 'pam-auth-update --package' + when: ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu' + changed_when: False + environment: + DEBIAN_FRONTEND: noninteractive + +- name: remove pam ccreds on Debian systems + apt: name='{{os_packages_pam_ccreds}}' state=absent + when: ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu' + +- name: remove pam ccreds on Redhat systems + yum: name='{{os_packages_pam_ccreds}}' state=absent + when: ansible_os_family == 'RedHat' + +- name: remove pam_cracklib, because it does not play nice with passwdqc + apt: name='{{os_packages_pam_cracklib}}' state=absent + when: (ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu') and os_auth_pam_passwdqc_enable + +- name: install the package for strong password checking + apt: name='{{os_packages_pam_passwdqc}}' state='installed' update_cache='yes' + when: (ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu') and os_auth_pam_passwdqc_enable + +- name: configure passwdqc + template: src='pam_passwdqd.j2' mode=0640 owner=root group=root dest='{{passwdqc_path}}' + when: (ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu') and os_auth_pam_passwdqc_enable + +- name: remove passwdqc + apt: name='{{os_packages_pam_passwdqc}}' state='absent' + when: (ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu') and not os_auth_pam_passwdqc_enable + +- name: install tally2 + apt: name='libpam-modules' state='installed' + when: (ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu') and not os_auth_pam_passwdqc_enable and os_auth_retries > 0 + +- name: configure tally2 + template: src='pam_tally2.j2' dest='{{tally2_path}}' mode=0640 owner=root group=root + when: (ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu') and not os_auth_pam_passwdqc_enable and os_auth_retries > 0 + +- name: delete tally2 when retries is 0 + file: path='{{tally2_path}}' state=absent + when: (ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu') and not os_auth_pam_passwdqc_enable and os_auth_retries == 0 + +- name: remove pam_cracklib, because it does not play nice with passwdqc + yum: name='{{os_packages_pam_cracklib}}' state='absent' + when: (ansible_os_family == 'RedHat' and ansible_distribution_version < '7') and os_auth_pam_passwdqc_enable + +- name: install the package for strong password checking + yum: name='{{os_packages_pam_passwdqc}}' state='installed' + when: (ansible_os_family == 'RedHat' and ansible_distribution_version < '7') and os_auth_pam_passwdqc_enable + +- name: remove passwdqc + yum: name='{{os_packages_pam_passwdqc}}' state='absent' + when: ansible_os_family == 'RedHat' and not os_auth_pam_passwdqc_enable + +- name: configure passwdqc and tally via central system-auth confic + template: src='rhel_system_auth.j2' dest='/etc/pam.d/system-auth-ac' mode=0640 owner=root group=root + +- name: NSA 2.3.3.5 Upgrade Password Hashing Algorithm to SHA-512 + template: src='rhel_libuser.conf.j2' dest='/etc/libuser.conf' mode=0640 owner=root group=root diff --git a/ansible/roles/dev-sec.os-hardening/tasks/profile.yml b/ansible/roles/dev-sec.os-hardening/tasks/profile.yml new file mode 100644 index 0000000..4152cad --- /dev/null +++ b/ansible/roles/dev-sec.os-hardening/tasks/profile.yml @@ -0,0 +1,4 @@ +--- +- name: create profile.conf + template: src='profile.conf.j2' dest='/etc/profile.d/pinerolo_profile.sh' owner=root group=root mode=0750 + when: not os_security_kernel_enable_core_dump diff --git a/ansible/roles/dev-sec.os-hardening/tasks/rhosts.yml b/ansible/roles/dev-sec.os-hardening/tasks/rhosts.yml new file mode 100644 index 0000000..2ea15e7 --- /dev/null +++ b/ansible/roles/dev-sec.os-hardening/tasks/rhosts.yml @@ -0,0 +1,13 @@ +--- +- name: Get user accounts | DTAG SEC Req 3.21-4 + command: "awk -F: '{print $1}' /etc/passwd" + changed_when: False + check_mode: no + register: users + +- name: delete rhosts-files from system | DTAG SEC Req 3.21-4 + file: dest='~{{ item }}/.rhosts' state=absent + with_items: '{{ users.stdout_lines | default(omit) }}' + +- name: delete hosts.equiv from system | DTAG SEC Req 3.21-4 + file: dest='/etc/hosts.equiv' state=absent diff --git a/ansible/roles/dev-sec.os-hardening/tasks/securetty.yml b/ansible/roles/dev-sec.os-hardening/tasks/securetty.yml new file mode 100644 index 0000000..27bcccf --- /dev/null +++ b/ansible/roles/dev-sec.os-hardening/tasks/securetty.yml @@ -0,0 +1,3 @@ +--- +- name: create securetty + template: src='securetty.j2' dest='/etc/securetty' owner=root group=root mode=0400 diff --git a/ansible/roles/dev-sec.os-hardening/tasks/suid_sgid.yml b/ansible/roles/dev-sec.os-hardening/tasks/suid_sgid.yml new file mode 100644 index 0000000..f692ad1 --- /dev/null +++ b/ansible/roles/dev-sec.os-hardening/tasks/suid_sgid.yml @@ -0,0 +1,24 @@ +--- +- name: remove suid/sgid bit from binaries in blacklist + file: path='{{item}}' mode='a-s' state=file follow=yes + failed_when: false + with_items: + - '{{ os_security_suid_sgid_system_blacklist }}' + - '{{ os_security_suid_sgid_blacklist }}' + +- name: find binaries with suid/sgid set + shell: find / -xdev \( -perm -4000 -o -perm -2000 \) -type f ! -path '/proc/*' -print 2>/dev/null + register: sbit_binaries + when: os_security_suid_sgid_remove_from_unknown + changed_when: False + +- name: gather files from which to remove suids/sgids and remove system white-listed files + set_fact: + suid: '{{ sbit_binaries.stdout_lines | difference(os_security_suid_sgid_system_whitelist) }}' + when: os_security_suid_sgid_remove_from_unknown + +- name: remove suid/sgid bit from all binaries except in system and user whitelist + file: path='{{item}}' mode='a-s' state=file follow=yes + with_items: + - '{{ suid | default(omit) | difference(os_security_suid_sgid_whitelist) }}' + when: os_security_suid_sgid_remove_from_unknown diff --git a/ansible/roles/dev-sec.os-hardening/tasks/sysctl.yml b/ansible/roles/dev-sec.os-hardening/tasks/sysctl.yml new file mode 100644 index 0000000..38f03cc --- /dev/null +++ b/ansible/roles/dev-sec.os-hardening/tasks/sysctl.yml @@ -0,0 +1,45 @@ +--- +- name: protect sysctl.conf + file: path='/etc/sysctl.conf' owner=root group=root mode=0440 + +- name: NSA 2.2.4.1 Set Daemon umask, do config for rhel-family + template: src='rhel_sysconfig_init.j2' dest='/etc/sysconfig/init' owner=root group=root mode=0544 + when: ansible_distribution == 'RedHat' or ansible_distribution == 'Fedora' or ansible_distribution == 'CentOS' + +- name: install initramfs-tools + apt: name='initramfs-tools' state=installed update_cache=true + when: ansible_os_family == 'Debian' and os_security_kernel_enable_module_loading + +- name: rebuild initramfs with starting pack of modules, if module loading at runtime is disabled + template: src='modules.j2' dest='/etc/initramfs-tools/modules' owner=root group=root mode=0440 + when: ansible_os_family == 'Debian' and os_security_kernel_enable_module_loading + register: initramfs + +- name: update-initramfs + command: 'update-initramfs -u' + when: initramfs.changed + +- name: Change various sysctl-settings, look at the sysctl-vars file for documentation + sysctl: + name: '{{ item.key }}' + value: '{{ item.value }}' + sysctl_set: yes + state: present + reload: yes + ignoreerrors: yes + with_dict: '{{ sysctl_config }}' + +- name: Change various sysctl-settings on rhel-hosts, look at the sysctl-vars file for documentation + sysctl: + name: '{{ item.key }}' + value: '{{ item.value }}' + state: present + reload: yes + ignoreerrors: yes + with_dict: '{{ sysctl_rhel_config }}' + when: ansible_distribution == 'RedHat' or ansible_distribution == 'Fedora' or ansible_distribution == 'CentOS' + +- name: Apply ufw defaults + template: src="ufw.j2" dest=/etc/default/ufw + when: ufw_manage_defaults and (ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu') + tags: ufw diff --git a/ansible/roles/dev-sec.os-hardening/tasks/user_accounts.yml b/ansible/roles/dev-sec.os-hardening/tasks/user_accounts.yml new file mode 100644 index 0000000..415581d --- /dev/null +++ b/ansible/roles/dev-sec.os-hardening/tasks/user_accounts.yml @@ -0,0 +1,35 @@ +--- + +- name: get UID_MIN from login.defs + shell: awk '/^\s*UID_MIN\s*([0-9]*).*?$/ {print $2}' /etc/login.defs removes=/etc/login.defs + register: uid_min + check_mode: no + changed_when: False + +- name: calculate UID_MAX from UID_MIN by substracting 1 + set_fact: uid_max='{{ uid_min.stdout | int - 1 }}' + when: uid_min is defined + +- name: set UID_MAX on Debian-systems if no login.defs exist + set_fact: uid_max='999' + when: ansible_os_family == 'Debian' and not uid_min + +- name: set UID_MAX on other systems if no login.defs exist + set_fact: uid_max='499' + when: not uid_min + +- name: get all system accounts + command: awk -F'':'' '{ if ( $3 <= {{uid_max|quote}} ) print $1}' /etc/passwd removes=/etc/passwd + changed_when: False + check_mode: no + register: sys_accs + +- name: remove always ignored system accounts from list + set_fact: + sys_accs_cond: '{{sys_accs.stdout_lines | difference(os_always_ignore_users) }}' + check_mode: no + +- name: change system accounts not on the user provided ignore-list + user: name='{{item}}' shell='{{os_nologin_shell_path}}' password='*' + with_items: + - '{{sys_accs_cond | default(omit) | difference(os_ignore_users) }}' diff --git a/ansible/roles/dev-sec.os-hardening/tasks/yum.yml b/ansible/roles/dev-sec.os-hardening/tasks/yum.yml new file mode 100644 index 0000000..128b095 --- /dev/null +++ b/ansible/roles/dev-sec.os-hardening/tasks/yum.yml @@ -0,0 +1,36 @@ +--- +- name: remove unused repositories + file: name='/etc/yum.repos.d/{{item}}.repo' state=absent + with_items: + - 'CentOS-Debuginfo' + - 'CentOS-Media' + - 'CentOS-Vault' + when: os_security_packages_clean + +- name: get yum-repository-files + shell: 'find /etc/yum.repos.d/ -type f -name *.repo' + changed_when: False + register: yum_repos + +- name: check if rhnplugin.conf exists + stat: path='/etc/yum/pluginconf.d/rhnplugin.conf' + register: rhnplugin_file + + # for the "default([])" see here: + # https://github.com/dev-sec/ansible-os-hardening/issues/99 and + # https://stackoverflow.com/questions/37067827/ansible-deprecation-warning-for-undefined-variable-despite-when-clause +- name: activate gpg-check for yum-repos + replace: dest='{{item}}' regexp='^\s*gpgcheck=0' replace='gpgcheck=1' + with_items: + - '/etc/yum.conf' + - '{{ yum_repos.stdout_lines| default([]) }}' + +- name: activate gpg-check for yum rhn if it exists + replace: dest='/etc/yum/pluginconf.d/rhnplugin.conf' regexp='^\s*gpgcheck=0' replace='gpgcheck=1' + when: rhnplugin_file.stat.exists + +- name: remove packages + yum: name='{{item}}' state=removed + with_items: + - "{{os_security_packages_list}}" + when: os_security_packages_clean diff --git a/ansible/roles/dev-sec.os-hardening/templates/limits.conf.j2 b/ansible/roles/dev-sec.os-hardening/templates/limits.conf.j2 new file mode 100644 index 0000000..9b9a019 --- /dev/null +++ b/ansible/roles/dev-sec.os-hardening/templates/limits.conf.j2 @@ -0,0 +1,3 @@ +# {{ansible_managed}} +# Prevent core dumps for all users. These are usually only needed by developers and may contain sensitive information. +* hard core 0 diff --git a/ansible/roles/dev-sec.os-hardening/templates/login.defs.j2 b/ansible/roles/dev-sec.os-hardening/templates/login.defs.j2 new file mode 100644 index 0000000..0fa8a3f --- /dev/null +++ b/ansible/roles/dev-sec.os-hardening/templates/login.defs.j2 @@ -0,0 +1,210 @@ +# Configuration control definitions for the login package. +# +# Three items must be defined: `MAIL_DIR`, `ENV_SUPATH`, and `ENV_PATH`. If unspecified, some arbitrary (and possibly incorrect) value will be assumed. All other items are optional - if not specified then the described action or option will be inhibited. +# +# Comment lines (lines beginning with `#`) and blank lines are ignored. +# +#-- Modified for Linux. --marekm + +# *REQUIRED for useradd/userdel/usermod* +# +# Directory where mailboxes reside, _or_ name of file, relative to the home directory. If you _do_ define `MAIL_DIR` and `MAIL_FILE`, `MAIL_DIR` takes precedence. +# Essentially: +# +# * `MAIL_DIR` defines the location of users mail spool files (for mbox use) by appending the username to `MAIL_DIR` as defined below. +# * `MAIL_FILE` defines the location of the users mail spool files as the fully-qualified filename obtained by prepending the user home directory before `$MAIL_FILE` +# +# *NOTE*: This is no more used for setting up users MAIL environment variable which is, starting from shadow 4.0.12-1 in Debian, entirely the job of the pam_mail PAM modules. +# +# See default PAM configuration files provided for login, su, etc. +# This is a temporary situation: setting these variables will soon move to `/etc/default/useradd` and the variables will then be no more supported +MAIL_DIR /var/mail +#MAIL_FILE .mail + +# Enable logging and display of `/var/log/faillog` login failure info. This option conflicts with the `pam_tally` PAM module. +FAILLOG_ENAB yes + +# Enable display of unknown usernames when login failures are recorded. +# +# *WARNING*: Unknown usernames may become world readable. See #290803 and #298773 for details about how this could become a security concern +LOG_UNKFAIL_ENAB no + +# Enable logging of successful logins +LOG_OK_LOGINS yes + +# Enable "syslog" logging of su activity - in addition to sulog file logging. +SYSLOG_SU_ENAB yes + +# Enable "syslog" logging of newgrp and sg. +SYSLOG_SG_ENAB yes + +# If defined, all su activity is logged to this file. +#SULOG_FILE /var/log/sulog + +# If defined, file which maps tty line to `TERM` environment parameter. Each line of the file is in a format something like "vt100 tty01". +#TTYTYPE_FILE /etc/ttytype + +# If defined, login failures will be logged here in a utmp format last, when invoked as lastb, will read `/var/log/btmp`, so... +FTMP_FILE /var/log/btmp + +# If defined, the command name to display when running "su -". For # example, if this is defined as "su" then a "ps" will display the command is "-su". If not defined, then "ps" would display the name of the shell actually being run, e.g. something like "-sh". +SU_NAME su + +# If defined, file which inhibits all the usual chatter during the login sequence. If a full pathname, then hushed mode will be enabled if the user's name or shell are found in the file. If not a full pathname, then hushed mode will be enabled if the file exists in the user's home directory. +#HUSHLOGIN_FILE /etc/hushlogins +HUSHLOGIN_FILE .hushlogin + +# *REQUIRED*: The default PATH settings, for superuser and normal users. (they are minimal, add the rest in the shell startup files) +ENV_SUPATH PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin +ENV_PATH PATH=/usr/local/bin:/usr/bin:/bin{{ os_env_extra_user_paths| join (':') }} + +# Terminal permissions +# -------------------- + +# Login tty will be assigned this group ownership. +# If you have a "write" program which is "setgid" to a special group which owns the terminals, define `TTYGROUP` to the group number and `TTYPERM` to `0620`. Otherwise leave `TTYGROUP` commented out and assign `TTYPERM` to either `622` or `600`. +TTYGROUP tty + +# Login tty will be set to this permission. +# In Debian `/usr/bin/bsd-write` or similar programs are setgid tty. However, the default and recommended value for `TTYPERM` is still `0600` to not allow anyone to write to anyone else console or terminal +# Users can still allow other people to write them by issuing the `mesg y` command. +TTYPERM 0600 + +# Login conf initializations +# -------------------------- + +# Terminal ERASE character ('\010' = backspace). Only used on System V. +ERASECHAR 0177 + +# Terminal KILL character ('\025' = CTRL/U). Only used on System V. +KILLCHAR 025 + +# The default umask value for `pam_umask` and is used by useradd and newusers to set the mode of the new home directories. +# If `USERGROUPS_ENAB` is set to `yes`, that will modify this `UMASK` default value for private user groups, i. e. the uid is the same as gid, and username is the same as the primary group name: for these, the user permissions will be used as group permissions, e. g. `022` will become `002`. +# Prefix these values with `0` to get octal, `0x` to get hexadecimal. +# `022` is the "historical" value in Debian for UMASK +# `027`, or even `077`, could be considered better for privacy. +UMASK {{os_env_umask}} + +# Enable setting of the umask group bits to be the same as owner bits (examples: `022` -> `002`, `077` -> `007`) for non-root users, if the uid is the same as gid, and username is the same as the primary group name. +# If set to yes, userdel will remove the user´s group if it contains no more members, and useradd will create by default a group with the name of the user. +USERGROUPS_ENAB yes + + +# Password aging controls +# ----------------------- + +# Maximum number of days a password may be used. +PASS_MAX_DAYS {{os_auth_pw_max_age}} + +# Minimum number of days allowed between password changes. +PASS_MIN_DAYS {{os_auth_pw_min_age}} + +# Number of days warning given before a password expires. +PASS_WARN_AGE 7 + +# Min/max values for automatic uid selection in useradd +UID_MIN {{os_auth_uid_min}} +UID_MAX 60000 +# System accounts +SYS_UID_MIN {{os_auth_sys_uid_min}} +SYS_UID_MAX {{os_auth_sys_uid_max}} + +# Min/max values for automatic gid selection in groupadd +GID_MIN {{os_auth_gid_min}} +GID_MAX 60000 +# System accounts +SYS_GID_MIN {{os_auth_sys_gid_min}} +SYS_GID_MAX {{os_auth_sys_gid_max}} + +# Max number of login retries if password is bad. This will most likely be overriden by PAM, since the default pam_unix module has it's own built in of 3 retries. However, this is a safe fallback in case you are using an authentication module that does not enforce PAM_MAXTRIES. +LOGIN_RETRIES {{os_auth_retries}} + +# Max time in seconds for login +LOGIN_TIMEOUT {{os_auth_timeout}} + +# Which fields may be changed by regular users using chfn - use any combination of letters "frwh" (full name, room number, work phone, home phone). If not defined, no changes are allowed. +# For backward compatibility, "yes" = "rwh" and "no" = "frwh". +{% if os_chfn_restrict %} +CHFN_RESTRICT {{ os_chfn_restrict }} +{% endif %} +# Should login be allowed if we can't cd to the home directory? +DEFAULT_HOME {{ 'yes' if os_auth_allow_homeless else 'no' }} + +# If defined, this command is run when removing a user. +# It should remove any at/cron/print jobs etc. owned by +# the user to be removed (passed as the first argument). +#USERDEL_CMD /usr/sbin/userdel_local + +# Instead of the real user shell, the program specified by this parameter will be launched, although its visible name (`argv[0]`) will be the shell's. The program may do whatever it wants (logging, additional authentification, banner, ...) before running the actual shell. +#FAKE_SHELL /bin/fakeshell + +# If defined, either full pathname of a file containing device names or a ":" delimited list of device names. Root logins will be allowed only upon these devices. +# This variable is used by login and su. +#CONSOLE /etc/consoles +#CONSOLE console:tty01:tty02:tty03:tty04 + +# List of groups to add to the user's supplementary group set when logging in on the console (as determined by the `CONSOLE` setting). Default is none. +# Use with caution - it is possible for users to gain permanent access to these groups, even when not logged in on the console. How to do it is left as an exercise for the reader... +# This variable is used by login and su. +#CONSOLE_GROUPS floppy:audio:cdrom + +# If set to `MD5`, MD5-based algorithm will be used for encrypting password +# If set to `SHA256`, SHA256-based algorithm will be used for encrypting password +# If set to `SHA512`, SHA512-based algorithm will be used for encrypting password +# If set to `DES`, DES-based algorithm will be used for encrypting password (default) +# Overrides the MD5_CRYPT_ENAB option +# +# Note: It is recommended to use a value consistent with +# the PAM modules configuration. +MD5_CRYPT_ENAB no +ENCRYPT_METHOD SHA512 + +# Only used if `ENCRYPT_METHOD` is set to `SHA256` or `SHA512`: Define the number of SHA rounds. +# With a lot of rounds, it is more difficult to brute forcing the password. But note also that it more CPU resources will be needed to authenticate users. +# If not specified, the libc will choose the default number of rounds (5000). The values must be inside the 1000-999999999 range. If only one of the MIN or MAX values is set, then this value will be used. +# If MIN > MAX, the highest value will be used. +#SHA_CRYPT_MIN_ROUNDS 5000 +#SHA_CRYPT_MAX_ROUNDS 5000 + + +# Obsoleted by PAM +# ================ +# These options are now handled by PAM. Please edit the appropriate file in `/etc/pam.d/` to enable the equivelants of them. +#MOTD_FILE +#DIALUPS_CHECK_ENAB +#LASTLOG_ENAB +#MAIL_CHECK_ENAB +#OBSCURE_CHECKS_ENAB +#PORTTIME_CHECKS_ENAB +#SU_WHEEL_ONLY +#CRACKLIB_DICTPATH +#PASS_CHANGE_TRIES +#PASS_ALWAYS_WARN +#ENVIRON_FILE +#NOLOGINS_FILE +#ISSUE_FILE +#PASS_MIN_LEN +#PASS_MAX_LEN +#ULIMIT +#ENV_HZ +#CHFN_AUTH +#CHSH_AUTH +#FAIL_DELAY + +# Obsoleted +# ========= +# These options are no more handled by shadow. +# Shadow utilities will display a warning if they still appear. +#CLOSE_SESSIONS +#LOGIN_STRING +#NO_PASSWORD_CONSOLE +#QMAIL_DIR + +# If set to `yes`, new passwords will be encrypted using the MD5-based algorithm compatible with the one used by recent releases of FreeBSD. It supports passwords of unlimited length and longer salt strings. +# Set to `no` if you need to copy encrypted passwords to other systems which don't understand the new algorithm. Default is `no`. +# This variable is deprecated. You should use ENCRYPT_METHOD. +# +#MD5_CRYPT_ENAB no + + diff --git a/ansible/roles/dev-sec.os-hardening/templates/modules.j2 b/ansible/roles/dev-sec.os-hardening/templates/modules.j2 new file mode 100644 index 0000000..1cd2187 --- /dev/null +++ b/ansible/roles/dev-sec.os-hardening/templates/modules.j2 @@ -0,0 +1,111 @@ +# {{ ansible_managed }} +# This file contains the names of kernel modules that should be loaded at boot time, one per line. Lines beginning with "#" are ignored. +# +# A list of all available kernel modules kann be found with `find /lib/modules/$(uname -r)/kernel/` +# We will sort by folder. + + +# Arch +# ---- +# +# Modules for certains builds, contains support modules and some CPU-specific optimizations. + +{% if ansible_architecture == 'x86_64' %} +# Optimize for x86_64 cryptographic features +twofish-x86_64-3way +twofish-x86_64 +aes-x86_64 +salsa20-x86_64 +blowfish-x86_64 +{% endif %} + +{% if 'amd' in ansible_processor %} +# AMD-specific optimizations +kvm-amd +{% else %} +# Intel-specific optimizations +ghash-clmulni-intel +aesni-intel +kvm-intel +{% endif %} + +kvm + +# Crypto +# ------ + +# Some core modules which comprise strong cryptography. +blowfish_common +blowfish_generic +ctr +cts +lrw +lzo +rmd160 +rmd256 +rmd320 +serpent +sha512_generic +twofish_common +twofish_generic +xts +zlib + + +# Drivers +# ------- + +# Basics +lp +rtc +loop + +# Filesystems +ext2 +btrfs + +{% if os_desktop_enable %} +# Desktop +psmouse +snd +snd_ac97_codec +snd_intel8x0 +snd_page_alloc +snd_pcm +snd_timer +soundcore +usbhid +{% endif %} + +# Lib +# --- +xz + + +# Net +# --- + +# All packets needed for netfilter rules (ie iptables, ebtables). +ip_tables +x_tables +iptable_filter +iptable_nat + +# Targets +ipt_LOG +ipt_REJECT + +# Modules +xt_connlimit +xt_tcpudp +xt_recent +xt_limit +xt_conntrack +nf_conntrack +nf_conntrack_ipv4 +nf_defrag_ipv4 +xt_state +nf_nat + +# Addons +xt_pknock diff --git a/ansible/roles/dev-sec.os-hardening/templates/pam_passwdqd.j2 b/ansible/roles/dev-sec.os-hardening/templates/pam_passwdqd.j2 new file mode 100644 index 0000000..3cb2375 --- /dev/null +++ b/ansible/roles/dev-sec.os-hardening/templates/pam_passwdqd.j2 @@ -0,0 +1,7 @@ +Name: passwdqc password strength enforcement +Default: yes +Priority: 1024 +Conflicts: cracklib +Password-Type: Primary +Password: + requisite pam_passwdqc.so {{os_auth_pam_passwdqc_options}} diff --git a/ansible/roles/dev-sec.os-hardening/templates/pam_tally2.j2 b/ansible/roles/dev-sec.os-hardening/templates/pam_tally2.j2 new file mode 100644 index 0000000..649fac2 --- /dev/null +++ b/ansible/roles/dev-sec.os-hardening/templates/pam_tally2.j2 @@ -0,0 +1,10 @@ +Name: tally2 lockout after failed attempts enforcement +Default: yes +Priority: 1024 +Conflicts: cracklib +Auth-Type: Primary +Auth-Initial: + required pam_tally2.so deny={{os_auth_retries}} onerr=fail unlock_time={{os_auth_lockout_time}} +Account-Type: Primary +Account-Initial: + required pam_tally2.so diff --git a/ansible/roles/dev-sec.os-hardening/templates/profile.conf.j2 b/ansible/roles/dev-sec.os-hardening/templates/profile.conf.j2 new file mode 100644 index 0000000..d34a7c9 --- /dev/null +++ b/ansible/roles/dev-sec.os-hardening/templates/profile.conf.j2 @@ -0,0 +1,4 @@ +# {{ ansible_managed }} + +# Disable core dumps via soft limits for all users. Compliance to this setting is voluntary and can be modified by users up to a hard limit. This setting is a sane default. +ulimit -S -c 0 > /dev/null 2>&1 diff --git a/ansible/roles/dev-sec.os-hardening/templates/rhel_libuser.conf.j2 b/ansible/roles/dev-sec.os-hardening/templates/rhel_libuser.conf.j2 new file mode 100644 index 0000000..a2f5a96 --- /dev/null +++ b/ansible/roles/dev-sec.os-hardening/templates/rhel_libuser.conf.j2 @@ -0,0 +1,90 @@ +# See libuser.conf(5) for more information. + +# {{ ansible_managed }} + +# Do not modify the default module list if you care about unattended calls +# to programs (i.e., scripts) working! + +[import] +# Data from these files is used when libuser.conf does not define a value. +# The mapping is documented in the man page. +login_defs = /etc/login.defs +default_useradd = /etc/default/useradd + +[defaults] +# The default (/usr/lib*/libuser) is usually correct +# moduledir = /your/custom/directory + +# The following variables are usually imported: +# skeleton = /etc/skel +# mailspooldir = /var/mail + +# NSA 2.3.3.5 Upgrade Password Hashing Algorithm to SHA-512 +crypt_style = sha512 + +modules = files shadow +create_modules = files shadow +# modules = files shadow ldap +# create_modules = ldap + +[userdefaults] +LU_USERNAME = %n +# LU_UIDNUMBER = 500 +LU_GIDNUMBER = %u +# LU_USERPASSWORD = !! +# LU_GECOS = %n +# LU_HOMEDIRECTORY = /home/%n +# LU_LOGINSHELL = /bin/bash + +# LU_SHADOWNAME = %n +# LU_SHADOWPASSWORD = !! +# LU_SHADOWLASTCHANGE = %d +# LU_SHADOWMIN = 0 +# LU_SHADOWMAX = 99999 +# LU_SHADOWWARNING = 7 +# LU_SHADOWINACTIVE = -1 +# LU_SHADOWEXPIRE = -1 +# LU_SHADOWFLAG = -1 + +[groupdefaults] +LU_GROUPNAME = %n +# LU_GIDNUMBER = 500 +# LU_GROUPPASSWORD = !! +# LU_MEMBERUID = +# LU_ADMINISTRATORUID = + +[files] +# This is useful for the case where some master files are used to +# populate a different NSS mechanism which this workstation uses. +# directory = /etc + +[shadow] +# This is useful for the case where some master files are used to +# populate a different NSS mechanism which this workstation uses. +# directory = /etc + +[ldap] +# Setting these is always necessary. +# server = ldap +# basedn = dc=example,dc=com + +# Setting these is rarely necessary, since it's usually correct. +# userBranch = ou=People +# groupBranch = ou=Group + +# Set only if your administrative user uses simple bind operations to +# connect to the server. +# binddn = cn=Manager,dc=example,dc=com + +# Set this only if the default user (as determined by SASL) is incorrect +# for SASL bind operations. Usually, it's correct, so you'll rarely need +# to set these. +# user = Manager +# authuser = Manager + +[sasl] +# Set these only if your sasldb is only used by a particular application, and +# in a particular domain. The default (all applications, all domains) is +# probably correct for most installations. +# appname = imap +# domain = EXAMPLE.COM diff --git a/ansible/roles/dev-sec.os-hardening/templates/rhel_sysconfig_init.j2 b/ansible/roles/dev-sec.os-hardening/templates/rhel_sysconfig_init.j2 new file mode 100644 index 0000000..18ae28d --- /dev/null +++ b/ansible/roles/dev-sec.os-hardening/templates/rhel_sysconfig_init.j2 @@ -0,0 +1,31 @@ +# {{ ansible_managed }} + +# color => new RH6.0 bootup +# verbose => old-style bootup +# anything else => new style bootup without ANSI colors or positioning +BOOTUP=color +# column to start "[ OK ]" label in +RES_COL=60 +# terminal sequence to move to that column. You could change this +# to something like "tput hpa ${RES_COL}" if your terminal supports it +MOVE_TO_COL="echo -en \\033[${RES_COL}G" +# terminal sequence to set color to a 'success' color (currently: green) +SETCOLOR_SUCCESS="echo -en \\033[0;32m" +# terminal sequence to set color to a 'failure' color (currently: red) +SETCOLOR_FAILURE="echo -en \\033[0;31m" +# terminal sequence to set color to a 'warning' color (currently: yellow) +SETCOLOR_WARNING="echo -en \\033[0;33m" +# terminal sequence to reset to the default color. +SETCOLOR_NORMAL="echo -en \\033[0;39m" +# Set to anything other than 'no' to allow hotkey interactive startup... +PROMPT={{ 'yes' if os_security_init_prompt else 'no' }} +# Set to 'yes' to allow probing for devices with swap signatures +AUTOSWAP=no +# What ttys should gettys be started on? +ACTIVE_CONSOLES=/dev/tty[1-6] +# Set to '/sbin/sulogin' to prompt for password on single-user mode +# Set to '/sbin/sushell' otherwise +SINGLE={{ '/sbin/sulogin' if os_security_init_single else '/sbin/sushell' }} + +# NSA 2.2.4.1 Set Daemon umask +umask 027 diff --git a/ansible/roles/dev-sec.os-hardening/templates/rhel_system_auth.j2 b/ansible/roles/dev-sec.os-hardening/templates/rhel_system_auth.j2 new file mode 100644 index 0000000..70749fb --- /dev/null +++ b/ansible/roles/dev-sec.os-hardening/templates/rhel_system_auth.j2 @@ -0,0 +1,40 @@ +# {{ ansible_managed }} +#--- + +#%PAM-1.0 +{% if os_auth_retries > 0 %} +auth required pam_tally2.so deny={{os_auth_retries}} onerr=fail unlock_time={{os_auth_lockout_time}} +{% endif %} +auth required pam_env.so +auth sufficient pam_unix.so nullok try_first_pass +auth requisite pam_succeed_if.so uid >= 500 quiet +auth required pam_deny.so + +{% if os_auth_retries > 0 %} +account required pam_tally2.so +{% endif %} +account required pam_unix.so +account sufficient pam_localuser.so +account sufficient pam_succeed_if.so uid < 500 quiet +account required pam_permit.so + +{% if os_auth_pam_passwdqc_enable %} + {%- if ansible_os_family == 'RedHat' and ansible_distribution_version >= '7' %} +password required pam_pwquality.so {{os_auth_pam_pwquality_options}} + {%- else %} +password requisite pam_passwdqc.so {{os_auth_pam_passwdqc_options}} + {%- endif %} +{% else %} +password requisite pam_cracklib.so try_first_pass retry=3 type= +{% endif %} + + +# NSA 2.3.3.5 Upgrade Password Hashing Algorithm to SHA-512 +# NSA 2.3.3.6 Limit Password Reuse +password sufficient pam_unix.so sha512 shadow nullok try_first_pass use_authtok remember=5 +password required pam_deny.so + +session optional pam_keyinit.so revoke +session required pam_limits.so +session [success=1 default=ignore] pam_succeed_if.so service in crond quiet use_uid +session required pam_unix.so diff --git a/ansible/roles/dev-sec.os-hardening/templates/securetty.j2 b/ansible/roles/dev-sec.os-hardening/templates/securetty.j2 new file mode 100644 index 0000000..d7b95bc --- /dev/null +++ b/ansible/roles/dev-sec.os-hardening/templates/securetty.j2 @@ -0,0 +1,6 @@ +# {{ ansible_managed }} + + +# A list of TTYs, from which root can log in +# see `man securetty` for reference +{{ "\n".join(os_auth_root_ttys) }} diff --git a/ansible/roles/dev-sec.os-hardening/templates/ufw.j2 b/ansible/roles/dev-sec.os-hardening/templates/ufw.j2 new file mode 100644 index 0000000..6043465 --- /dev/null +++ b/ansible/roles/dev-sec.os-hardening/templates/ufw.j2 @@ -0,0 +1,44 @@ +# /etc/default/ufw +# + +# Set to yes to apply rules to support IPv6 (no means only IPv6 on loopback +# accepted). You will need to 'disable' and then 'enable' the firewall for +# the changes to take affect. +IPV6={{ 'no' if sysctl_config['net.ipv6.conf.all.disable_ipv6'] is defined and sysctl_config['net.ipv6.conf.all.disable_ipv6'] == 1 else 'yes' }} + +# Set the default input policy to ACCEPT, DROP, or REJECT. Please note that if +# you change this you will most likely want to adjust your rules. +DEFAULT_INPUT_POLICY="{{ ufw_default_input_policy }}" + +# Set the default output policy to ACCEPT, DROP, or REJECT. Please note that if +# you change this you will most likely want to adjust your rules. +DEFAULT_OUTPUT_POLICY="{{ ufw_default_output_policy }}" + +# Set the default forward policy to ACCEPT, DROP or REJECT. Please note that +# if you change this you will most likely want to adjust your rules +DEFAULT_FORWARD_POLICY="{{ ufw_default_forward_policy }}" + +# Set the default application policy to ACCEPT, DROP, REJECT or SKIP. Please +# note that setting this to ACCEPT may be a security risk. See 'man ufw' for +# details +DEFAULT_APPLICATION_POLICY="{{ ufw_default_application_policy }}" + +# By default, ufw only touches its own chains. Set this to 'yes' to have ufw +# manage the built-in chains too. Warning: setting this to 'yes' will break +# non-ufw managed firewall rules +MANAGE_BUILTINS="{{ ufw_manage_builtins }}" + +# +# IPT backend +# +# only enable if using iptables backend and want to overwrite /etc/sysctl.conf +{% if ufw_ipt_sysctl == '' %}#{% endif %}IPT_SYSCTL={{ ufw_ipt_sysctl }} + +# Extra connection tracking modules to load. Complete list can be found in +# net/netfilter/Kconfig of your kernel source. Some common modules: +# nf_conntrack_irc, nf_nat_irc: DCC (Direct Client to Client) support +# nf_conntrack_netbios_ns: NetBIOS (samba) client support +# nf_conntrack_pptp, nf_nat_pptp: PPTP over stateful firewall/NAT +# nf_conntrack_ftp, nf_nat_ftp: active FTP support +# nf_conntrack_tftp, nf_nat_tftp: TFTP support (server side) +IPT_MODULES="{{ ufw_ipt_modules }}" diff --git a/ansible/roles/dev-sec.os-hardening/vars/Debian.yml b/ansible/roles/dev-sec.os-hardening/vars/Debian.yml new file mode 100644 index 0000000..c1a98e0 --- /dev/null +++ b/ansible/roles/dev-sec.os-hardening/vars/Debian.yml @@ -0,0 +1,14 @@ +os_packages_pam_ccreds: 'libpam-ccreds' +os_packages_pam_passwdqc: 'libpam-passwdqc' +os_packages_pam_cracklib: 'libpam-cracklib' +passwdqc_path: '/usr/share/pam-configs/passwdqc' +tally2_path: '/usr/share/pam-configs/tally2' +os_nologin_shell_path: '/usr/sbin/nologin' + +# Different distros use different standards for /etc/shadow perms, e.g. +# RHEL derivatives use root:root 0600, whereas Debian-based use root:shadow 0640. +# You must provide key/value pairs for owner, group, and mode if overriding. +os_shadow_perms: + owner: root + group: shadow + mode: "0640" diff --git a/ansible/roles/dev-sec.os-hardening/vars/Oracle Linux.yml b/ansible/roles/dev-sec.os-hardening/vars/Oracle Linux.yml new file mode 100644 index 0000000..39625e1 --- /dev/null +++ b/ansible/roles/dev-sec.os-hardening/vars/Oracle Linux.yml @@ -0,0 +1,12 @@ +os_packages_pam_ccreds: 'pam_ccreds' +os_packages_pam_passwdqc: 'pam_passwdqc' +os_packages_pam_cracklib: 'pam_cracklib' +os_nologin_shell_path: '/sbin/nologin' + +# Different distros use different standards for /etc/shadow perms, e.g. +# RHEL derivatives use root:root 0600, whereas Debian-based use root:shadow 0640. +# You must provide key/value pairs for owner, group, and mode if overriding. +os_shadow_perms: + owner: root + group: root + mode: "0600" diff --git a/ansible/roles/dev-sec.os-hardening/vars/RedHat.yml b/ansible/roles/dev-sec.os-hardening/vars/RedHat.yml new file mode 100644 index 0000000..6874ef7 --- /dev/null +++ b/ansible/roles/dev-sec.os-hardening/vars/RedHat.yml @@ -0,0 +1,12 @@ +os_packages_pam_ccreds: 'pam_ccreds' +os_packages_pam_passwdqc: 'pam_passwdqc' +os_packages_pam_cracklib: 'pam_cracklib' +os_nologin_shell_path: '/sbin/nologin' + +# Different distros use different standards for /etc/shadow perms, e.g. +# RHEL derivatives use root:root 0600, whereas Debian-based use root:shadow 0640. +# You must provide key/value pairs for owner, group, and mode if overriding. +os_shadow_perms: + owner: root + group: root + mode: "0600" diff --git a/ansible/roles/dev-sec.os-hardening/vars/main.yml b/ansible/roles/dev-sec.os-hardening/vars/main.yml new file mode 100644 index 0000000..bc0500e --- /dev/null +++ b/ansible/roles/dev-sec.os-hardening/vars/main.yml @@ -0,0 +1,111 @@ +# SYSTEM CONFIGURATION +# ==================== +# These are not meant to be modified by the user + +# suid and sgid blacklists and whitelists +# --------------------------------------- +# don't change values in the system_blacklist/whitelist +# adjust values for blacklist/whitelist instead, they can override system_blacklist/whitelist + +# list of suid/sgid entries that must be removed +os_security_suid_sgid_system_blacklist: + # blacklist as provided by NSA + - '/usr/bin/rcp' + - '/usr/bin/rlogin' + - '/usr/bin/rsh' + # sshd must not use host-based authentication (see ssh cookbook) + - '/usr/libexec/openssh/ssh-keysign' + - '/usr/lib/openssh/ssh-keysign' + # misc others + - '/sbin/netreport' # not normally required for user + - '/usr/sbin/usernetctl' # modify interfaces via functional accounts + # connecting to ... + - '/usr/sbin/userisdnctl' # no isdn... + - '/usr/sbin/pppd' # no ppp / dsl ... + # lockfile + - '/usr/bin/lockfile' + - '/usr/bin/mail-lock' + - '/usr/bin/mail-unlock' + - '/usr/bin/mail-touchlock' + - '/usr/bin/dotlockfile' + # need more investigation blacklist for now + - '/usr/bin/arping' + - '/usr/sbin/uuidd' + - '/usr/bin/mtr' # investigate current state... + - '/usr/lib/evolution/camel-lock-helper-1.2' # investigate current state... + - '/usr/lib/pt_chown' # pseudo-tty needed? + - '/usr/lib/eject/dmcrypt-get-device' + - '/usr/lib/mc/cons.saver' # midnight commander screensaver + +# list of suid/sgid entries that can remain untouched +os_security_suid_sgid_system_whitelist: + # whitelist as provided by NSA + - '/bin/mount' + - '/bin/ping' + - '/bin/su' + - '/bin/umount' + - '/sbin/pam_timestamp_check' + - '/sbin/unix_chkpwd' + - '/usr/bin/at' + - '/usr/bin/gpasswd' + - '/usr/bin/locate' + - '/usr/bin/newgrp' + - '/usr/bin/passwd' + - '/usr/bin/ssh-agent' + - '/usr/libexec/utempter/utempter' + - '/usr/sbin/lockdev' + - '/usr/sbin/sendmail.sendmail' + - '/usr/bin/expiry' + # whitelist ipv6 + - '/bin/ping6' + - '/usr/bin/traceroute6.iputils' + # whitelist nfs + - '/sbin/mount.nfs' + - '/sbin/umount.nfs' + # whitelist nfs4 + - '/sbin/mount.nfs4' + - '/sbin/umount.nfs4' + # whitelist cron + - '/usr/bin/crontab' + # whitelist consolemssaging + - '/usr/bin/wall' + - '/usr/bin/write' + # whitelist: only SGID with utmp group for multi-session access + # impact is limited; installation/usage has some remaining risk + - '/usr/bin/screen' + # whitelist locate + - '/usr/bin/mlocate' + # whitelist usermanagement + - '/usr/bin/chage' + - '/usr/bin/chfn' + - '/usr/bin/chsh' + # whitelist fuse + - '/bin/fusermount' + # whitelist pkexec + - '/usr/bin/pkexec' + # whitelist sudo + - '/usr/bin/sudo' + - '/usr/bin/sudoedit' + # whitelist postfix + - '/usr/sbin/postdrop' + - '/usr/sbin/postqueue' + # whitelist apache + - '/usr/sbin/suexec' + # whitelist squid + - '/usr/lib/squid/ncsa_auth' + - '/usr/lib/squid/pam_auth' + # whitelist kerberos + - '/usr/kerberos/bin/ksu' + # whitelist pam_caching + - '/usr/sbin/ccreds_validate' + # whitelist Xorg + - '/usr/bin/Xorg' # xorg + - '/usr/bin/X' # xorg + - '/usr/lib/dbus-1.0/dbus-daemon-launch-helper' # freedesktop ipc + - '/usr/lib/vte/gnome-pty-helper' # gnome + - '/usr/lib/libvte9/gnome-pty-helper' # gnome + - '/usr/lib/libvte-2.90-9/gnome-pty-helper' # gnome + +# system accounts that do not get their login disabled and pasword changed +os_always_ignore_users: ['root','sync','shutdown','halt'] + diff --git a/ansible/roles/dev-sec.ssh-hardening/.gitignore b/ansible/roles/dev-sec.ssh-hardening/.gitignore new file mode 100644 index 0000000..1953eaa --- /dev/null +++ b/ansible/roles/dev-sec.ssh-hardening/.gitignore @@ -0,0 +1,3 @@ +.kitchen +hosts +Gemfile.lock diff --git a/ansible/roles/dev-sec.ssh-hardening/.kitchen.vagrant.yml b/ansible/roles/dev-sec.ssh-hardening/.kitchen.vagrant.yml new file mode 100644 index 0000000..4dc2694 --- /dev/null +++ b/ansible/roles/dev-sec.ssh-hardening/.kitchen.vagrant.yml @@ -0,0 +1,81 @@ +--- +driver: + name: vagrant + driver_config: + http_proxy: <%= ENV['http_proxy'] || nil %> + https_proxy: <%= ENV['https_proxy'] || nil %> + vagrantfiles: + - kitchen_vagrant_block.rb + +provisioner: + name: ansible_playbook + hosts: all + require_ansible_repo: false + require_ansible_omnibus: true + require_chef_for_busser: false + require_ruby_for_busser: false + ansible_verbose: true + roles_path: ../ansible-ssh-hardening/ + playbook: default.yml + http_proxy: <%= ENV['http_proxy'] || nil %> + https_proxy: <%= ENV['https_proxy'] || nil %> + +transport: + max_ssh_sessions: 5 + +platforms: +- name: ubuntu-12.04 + driver_config: + box: opscode-ubuntu-12.04 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_ubuntu-12.04_chef-provisionerless.box +- name: ubuntu-14.04 + driver_config: + box: opscode-ubuntu-14.04 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_ubuntu-14.04_chef-provisionerless.box +- name: ubuntu-16.04 + driver_config: + box: opscode-ubuntu-16.04 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_ubuntu-16.04_chef-provisionerless.box +- name: centos-6.4 +- name: centos-7.2 + driver_config: + box: opscode-centos-7.2 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_centos-7.2_chef-provisionerless.box +- name: centos-6.5 + driver_config: + box: opscode-centos-6.5 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_centos-6.5_chef-provisionerless.box +- name: centos-6.8 + driver_config: + box: bento/centos-6.8 +- name: centos-7 + driver_config: + box: bento/centos-7.2 +- name: oracle-6.4 + driver_config: + box: oracle-6.4 + box_url: https://storage.us2.oraclecloud.com/v1/istoilis-istoilis/vagrant/oel64-64.box +- name: oracle-6.5 + driver_config: + box: oracle-6.5 + box_url: https://storage.us2.oraclecloud.com/v1/istoilis-istoilis/vagrant/oel65-64.box +- name: oracle-7 + driver_config: + box: boxcutter/ol72 +- name: debian-7 + driver_config: + box: debian-7 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_debian-7.8_chef-provisionerless.box +- name: debian-8 + driver_config: + box: debian-8 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_debian-8.1_chef-provisionerless.box + +verifier: + name: inspec + sudo: true + inspec_tests: + - https://github.com/dev-sec/ssh-baseline/ + +suites: +- name: ssh diff --git a/ansible/roles/dev-sec.ssh-hardening/.kitchen.yml b/ansible/roles/dev-sec.ssh-hardening/.kitchen.yml new file mode 100644 index 0000000..13c4076 --- /dev/null +++ b/ansible/roles/dev-sec.ssh-hardening/.kitchen.yml @@ -0,0 +1,81 @@ +--- +driver: + name: docker + use_sudo: false + privileged: true + http_proxy: <%= ENV['http_proxy'] || nil %> + https_proxy: <%= ENV['https_proxy'] || nil %> + +transport: + max_ssh_sessions: 5 + +transport: + max_ssh_sessions: 5 + + +provisioner: + name: ansible_playbook + hosts: all + require_ansible_repo: false + require_chef_for_busser: false + require_ruby_for_busser: false + ansible_verbose: true + ansible_diff: true + hosts: all + roles_path: ../ansible-ssh-hardening/ + http_proxy: <%= ENV['http_proxy'] || nil %> + https_proxy: <%= ENV['https_proxy'] || nil %> + playbook: default.yml + ansible_extra_flags: + - "--skip-tags=sysctl" + +platforms: +- name: centos6-ansible-latest + driver: + image: rndmh3ro/docker-centos6-ansible:latest + platform: centos +- name: centos7-ansible-latest + driver: + image: rndmh3ro/docker-centos7-ansible:latest + platform: centos +- name: oracle6-ansible-latest + driver: + image: rndmh3ro/docker-oracle6-ansible:latest + platform: centos +- name: oracle7-ansible-latest + driver: + image: rndmh3ro/docker-oracle7-ansible:latest + platform: centos +- name: ubuntu1204-ansible-latest + driver: + image: rndmh3ro/docker-ubuntu1204-ansible:latest + platform: ubuntu +- name: ubuntu1404-ansible-latest + driver: + image: rndmh3ro/docker-ubuntu1404-ansible:latest + platform: ubuntu +- name: ubuntu1604-ansible-latest + driver: + image: rndmh3ro/docker-ubuntu1604-ansible:latest + platform: ubuntu +- name: debian7-ansible-latest + driver: + image: rndmh3ro/docker-debian7-ansible:latest + platform: debian +- name: debian8-ansible-latest + driver: + image: rndmh3ro/docker-debian8-ansible:latest + platform: debian +#- name: debian9-ansible-latest +# driver: +# image: rndmh3ro/docker-debian9-ansible:latest +# platform: debian + +verifier: + name: inspec + sudo: true + inspec_tests: + - https://github.com/dev-sec/ssh-baseline + +suites: +- name: ssh diff --git a/ansible/roles/dev-sec.ssh-hardening/.travis.yml b/ansible/roles/dev-sec.ssh-hardening/.travis.yml new file mode 100644 index 0000000..8c8d12e --- /dev/null +++ b/ansible/roles/dev-sec.ssh-hardening/.travis.yml @@ -0,0 +1,67 @@ +--- +services: docker + +env: + - distro: centos6 + version: latest + init: /sbin/init + + - distro: centos7 + init: /usr/lib/systemd/systemd + run_opts: "--privileged --volume=/sys/fs/cgroup:/sys/fs/cgroup:ro" + version: latest + + - distro: oracle6 + version: latest + init: /sbin/init + + - distro: oracle7 + init: /usr/lib/systemd/systemd + run_opts: "--privileged --volume=/sys/fs/cgroup:/sys/fs/cgroup:ro" + version: latest + + - distro: ubuntu1604 + version: latest + init: /lib/systemd/systemd + run_opts: "--privileged --volume=/sys/fs/cgroup:/sys/fs/cgroup:ro" + + - distro: ubuntu1404 + version: latest + init: /sbin/init + + - distro: ubuntu1204 + version: latest + init: /sbin/init + + - distro: debian7 + version: latest + init: /sbin/init + + - distro: debian8 + version: latest + run_opts: "--privileged --volume=/sys/fs/cgroup:/sys/fs/cgroup:ro" + init: /sbin/init + +# - distro: debian9 +# version: latest +# init: /lib/systemd/systemd +# run_opts: "--privileged --volume=/sys/fs/cgroup:/sys/fs/cgroup:ro" + + +before_install: + # Pull container + - 'docker pull rndmh3ro/docker-${distro}-ansible:${version}' + +script: + - container_id=$(mktemp) + # Run container in detached state. + - 'docker run --detach --volume="${PWD}":/etc/ansible/roles/ansible-ssh-hardening:ro ${run_opts} rndmh3ro/docker-${distro}-ansible:${version} "${init}" > "${container_id}"' + + # Test role. + - 'docker exec "$(cat ${container_id})" ansible-playbook /etc/ansible/roles/ansible-ssh-hardening/default.yml' + + # Verify role + - 'inspec exec https://github.com/dev-sec/ssh-baseline/ -t docker://$(cat ${container_id})' + +notifications: + webhooks: https://galaxy.ansible.com/api/v1/notifications/ diff --git a/ansible/roles/dev-sec.ssh-hardening/CHANGELOG.md b/ansible/roles/dev-sec.ssh-hardening/CHANGELOG.md new file mode 100644 index 0000000..6408147 --- /dev/null +++ b/ansible/roles/dev-sec.ssh-hardening/CHANGELOG.md @@ -0,0 +1,174 @@ +# Change Log + +## [3.2.0](https://github.com/dev-sec/ansible-ssh-hardening/tree/3.2.0) (2016-10-24) +[Full Changelog](https://github.com/dev-sec/ansible-ssh-hardening/compare/3.1.0...3.2.0) + +**Implemented enhancements:** + +- CentOS 7 selinux dependencies [\#76](https://github.com/dev-sec/ansible-ssh-hardening/issues/76) +- Parameterise Banner and DebianBanner as defaults [\#77](https://github.com/dev-sec/ansible-ssh-hardening/pull/77) ([tsenart](https://github.com/tsenart)) + +**Fixed bugs:** + +- Some tasks are always run even if they are not needed [\#78](https://github.com/dev-sec/ansible-ssh-hardening/issues/78) +- Selinux issue [\#75](https://github.com/dev-sec/ansible-ssh-hardening/issues/75) +- Running the tests locally [\#61](https://github.com/dev-sec/ansible-ssh-hardening/issues/61) +- SELinux-specific task still runs on SELinux-disabled systems [\#74](https://github.com/dev-sec/ansible-ssh-hardening/issues/74) + +**Closed issues:** + +- Applied-Crypto-Hardening project and new cyphers. [\#28](https://github.com/dev-sec/ansible-ssh-hardening/issues/28) + +**Merged pull requests:** + +- install selinux dependencies, check for already installed semodule [\#79](https://github.com/dev-sec/ansible-ssh-hardening/pull/79) ([rndmh3ro](https://github.com/rndmh3ro)) + +## [3.1.0](https://github.com/dev-sec/ansible-ssh-hardening/tree/3.1.0) (2016-08-03) +[Full Changelog](https://github.com/dev-sec/ansible-ssh-hardening/compare/3.1...3.1.0) + +**Implemented enhancements:** + +- use new ciphers, kex, macs and privilege separation for redhat family 7 or later [\#72](https://github.com/dev-sec/ansible-ssh-hardening/issues/72) + +## [3.1](https://github.com/dev-sec/ansible-ssh-hardening/tree/3.1) (2016-08-03) +[Full Changelog](https://github.com/dev-sec/ansible-ssh-hardening/compare/3.0.0...3.1) + +**Implemented enhancements:** + +- Add Xenial / Ubuntu 16.04 LTS to meta/main.yml [\#63](https://github.com/dev-sec/ansible-ssh-hardening/issues/63) +- Use new ciphers, kex, macs and priv separation sandbox for redhat family 7 [\#73](https://github.com/dev-sec/ansible-ssh-hardening/pull/73) ([atomic111](https://github.com/atomic111)) +- add docker support [\#71](https://github.com/dev-sec/ansible-ssh-hardening/pull/71) ([rndmh3ro](https://github.com/rndmh3ro)) +- add always\_run: true to task. fix \#64 [\#69](https://github.com/dev-sec/ansible-ssh-hardening/pull/69) ([rndmh3ro](https://github.com/rndmh3ro)) +- Debian8 [\#68](https://github.com/dev-sec/ansible-ssh-hardening/pull/68) ([rndmh3ro](https://github.com/rndmh3ro)) +- Fixed KexAlgorithms Conditional Statement [\#66](https://github.com/dev-sec/ansible-ssh-hardening/pull/66) ([cjsheets](https://github.com/cjsheets)) +- Moves vars to defaults [\#60](https://github.com/dev-sec/ansible-ssh-hardening/pull/60) ([conorsch](https://github.com/conorsch)) + +**Fixed bugs:** + +- semodule ssh\_password error on AWS Centos 7 [\#64](https://github.com/dev-sec/ansible-ssh-hardening/issues/64) + +**Closed issues:** + +- `ssh\_server\_ports` a bit misleading in the vars section? [\#62](https://github.com/dev-sec/ansible-ssh-hardening/issues/62) +- sftp\_enabled: false will break Ansible's template module [\#55](https://github.com/dev-sec/ansible-ssh-hardening/issues/55) +- Move cipher/kex/mac vars to defaults [\#53](https://github.com/dev-sec/ansible-ssh-hardening/issues/53) + +**Merged pull requests:** + +- Add SCP/SFTP to FAQ [\#58](https://github.com/dev-sec/ansible-ssh-hardening/pull/58) ([rndmh3ro](https://github.com/rndmh3ro)) + +## [3.0.0](https://github.com/dev-sec/ansible-ssh-hardening/tree/3.0.0) (2016-03-13) +[Full Changelog](https://github.com/dev-sec/ansible-ssh-hardening/compare/2.0.0...3.0.0) + +**Implemented enhancements:** + +- Added sftp\_enabled, sftp\_chroot\_dir, and ssh\_client\_roaming from the … [\#57](https://github.com/dev-sec/ansible-ssh-hardening/pull/57) ([shirokatze](https://github.com/shirokatze)) +- add test support for ansible 1.9 and 2.0 [\#56](https://github.com/dev-sec/ansible-ssh-hardening/pull/56) ([rndmh3ro](https://github.com/rndmh3ro)) +- update platforms in meta-file [\#52](https://github.com/dev-sec/ansible-ssh-hardening/pull/52) ([rndmh3ro](https://github.com/rndmh3ro)) +- add webhook for ansible galaxy [\#51](https://github.com/dev-sec/ansible-ssh-hardening/pull/51) ([rndmh3ro](https://github.com/rndmh3ro)) +- Disable experimental client roaming. [\#49](https://github.com/dev-sec/ansible-ssh-hardening/pull/49) ([rndmh3ro](https://github.com/rndmh3ro)) +- use inspec as test framework [\#48](https://github.com/dev-sec/ansible-ssh-hardening/pull/48) ([chris-rock](https://github.com/chris-rock)) +- Change categories to tags for upcoming ansible 2.0 [\#47](https://github.com/dev-sec/ansible-ssh-hardening/pull/47) ([rndmh3ro](https://github.com/rndmh3ro)) +- add changelog generator [\#46](https://github.com/dev-sec/ansible-ssh-hardening/pull/46) ([chris-rock](https://github.com/chris-rock)) + +**Closed issues:** + +- Install from ansible galaxy missing files \(tasks\) [\#50](https://github.com/dev-sec/ansible-ssh-hardening/issues/50) +- should generate new ssh host key files [\#45](https://github.com/dev-sec/ansible-ssh-hardening/issues/45) + +**Merged pull requests:** + +- New release 3.0.0 [\#59](https://github.com/dev-sec/ansible-ssh-hardening/pull/59) ([rndmh3ro](https://github.com/rndmh3ro)) + +## [2.0.0](https://github.com/dev-sec/ansible-ssh-hardening/tree/2.0.0) (2015-11-28) +[Full Changelog](https://github.com/dev-sec/ansible-ssh-hardening/compare/1.2.1...2.0.0) + +**Closed issues:** + +- Fix directory structure. [\#43](https://github.com/dev-sec/ansible-ssh-hardening/issues/43) + +**Merged pull requests:** + +- New dir layout. Fix \#43 [\#44](https://github.com/dev-sec/ansible-ssh-hardening/pull/44) ([rndmh3ro](https://github.com/rndmh3ro)) +- Add var to travis job [\#42](https://github.com/dev-sec/ansible-ssh-hardening/pull/42) ([rndmh3ro](https://github.com/rndmh3ro)) +- sftp\_enable option [\#41](https://github.com/dev-sec/ansible-ssh-hardening/pull/41) ([fitz123](https://github.com/fitz123)) + +## [1.2.1](https://github.com/dev-sec/ansible-ssh-hardening/tree/1.2.1) (2015-10-16) +[Full Changelog](https://github.com/dev-sec/ansible-ssh-hardening/compare/1.2...1.2.1) + +**Merged pull requests:** + +- Allow whitelisted groups on ssh [\#40](https://github.com/dev-sec/ansible-ssh-hardening/pull/40) ([fheinle](https://github.com/fheinle)) + +## [1.2](https://github.com/dev-sec/ansible-ssh-hardening/tree/1.2) (2015-09-28) +[Full Changelog](https://github.com/dev-sec/ansible-ssh-hardening/compare/1.2.0...1.2) + +## [1.2.0](https://github.com/dev-sec/ansible-ssh-hardening/tree/1.2.0) (2015-09-28) +[Full Changelog](https://github.com/dev-sec/ansible-ssh-hardening/compare/1.1...1.2.0) + +**Merged pull requests:** + +- bugfix. Now option true for PrintLastLog is available again [\#39](https://github.com/dev-sec/ansible-ssh-hardening/pull/39) ([fitz123](https://github.com/fitz123)) +- Add more travis-tests [\#38](https://github.com/dev-sec/ansible-ssh-hardening/pull/38) ([rndmh3ro](https://github.com/rndmh3ro)) +- Support for selinux and pam. fix \#23 [\#35](https://github.com/dev-sec/ansible-ssh-hardening/pull/35) ([rndmh3ro](https://github.com/rndmh3ro)) + +## [1.1](https://github.com/dev-sec/ansible-ssh-hardening/tree/1.1) (2015-09-01) +[Full Changelog](https://github.com/dev-sec/ansible-ssh-hardening/compare/1.1.0...1.1) + +## [1.1.0](https://github.com/dev-sec/ansible-ssh-hardening/tree/1.1.0) (2015-09-01) +[Full Changelog](https://github.com/dev-sec/ansible-ssh-hardening/compare/1.0.0...1.1.0) + +**Closed issues:** + +- ssh\_ports - individual client/server config [\#33](https://github.com/dev-sec/ansible-ssh-hardening/issues/33) +- UsePAM should probably default to yes on Red Hat Linux 7 [\#23](https://github.com/dev-sec/ansible-ssh-hardening/issues/23) + +**Merged pull requests:** + +- Change variable for hmac from server to client [\#37](https://github.com/dev-sec/ansible-ssh-hardening/pull/37) ([rndmh3ro](https://github.com/rndmh3ro)) +- Update kitchen-ansible, remove separate debian install [\#36](https://github.com/dev-sec/ansible-ssh-hardening/pull/36) ([rndmh3ro](https://github.com/rndmh3ro)) +- Separate ssh client and server ports. Fix \#33 [\#34](https://github.com/dev-sec/ansible-ssh-hardening/pull/34) ([rndmh3ro](https://github.com/rndmh3ro)) +- update common kitchen.yml platforms \(ansible\), kitchen\_debian.yml platforms \(ansible\) [\#32](https://github.com/dev-sec/ansible-ssh-hardening/pull/32) ([chris-rock](https://github.com/chris-rock)) +- Make MaxAuthTries configurable [\#31](https://github.com/dev-sec/ansible-ssh-hardening/pull/31) ([rndmh3ro](https://github.com/rndmh3ro)) +- Change oneliner if-statements to be more readable [\#30](https://github.com/dev-sec/ansible-ssh-hardening/pull/30) ([rndmh3ro](https://github.com/rndmh3ro)) +- Make ssh client password login configurable. [\#29](https://github.com/dev-sec/ansible-ssh-hardening/pull/29) ([ypid](https://github.com/ypid)) +- Fix join-filter, jinja-cases, intendation [\#27](https://github.com/dev-sec/ansible-ssh-hardening/pull/27) ([rndmh3ro](https://github.com/rndmh3ro)) +- Short role review. Fixed role when ssh\_client\_weak\_kex == true. [\#26](https://github.com/dev-sec/ansible-ssh-hardening/pull/26) ([ypid](https://github.com/ypid)) +- Make it configurable to only harden ssh client/server or both \(default\). [\#25](https://github.com/dev-sec/ansible-ssh-hardening/pull/25) ([ypid](https://github.com/ypid)) +- Separate system-vars from editable vars [\#24](https://github.com/dev-sec/ansible-ssh-hardening/pull/24) ([rndmh3ro](https://github.com/rndmh3ro)) +- Add correct CONTRIB-file [\#22](https://github.com/dev-sec/ansible-ssh-hardening/pull/22) ([rndmh3ro](https://github.com/rndmh3ro)) +- Add Ansible Galaxy badge [\#21](https://github.com/dev-sec/ansible-ssh-hardening/pull/21) ([rndmh3ro](https://github.com/rndmh3ro)) +- fix configuration of playbook path [\#20](https://github.com/dev-sec/ansible-ssh-hardening/pull/20) ([chris-rock](https://github.com/chris-rock)) +- Debian install script [\#19](https://github.com/dev-sec/ansible-ssh-hardening/pull/19) ([rndmh3ro](https://github.com/rndmh3ro)) + +## [1.0.0](https://github.com/dev-sec/ansible-ssh-hardening/tree/1.0.0) (2015-04-30) +**Implemented enhancements:** + +- Update variable-documentation [\#12](https://github.com/dev-sec/ansible-ssh-hardening/pull/12) ([rndmh3ro](https://github.com/rndmh3ro)) + +**Closed issues:** + +- add travis test for ubuntu 12.04 [\#7](https://github.com/dev-sec/ansible-ssh-hardening/issues/7) +- Use handler for sshd restart [\#6](https://github.com/dev-sec/ansible-ssh-hardening/issues/6) +- Running test-kitchen fails [\#2](https://github.com/dev-sec/ansible-ssh-hardening/issues/2) + +**Merged pull requests:** + +- add self as author [\#18](https://github.com/dev-sec/ansible-ssh-hardening/pull/18) ([chris-rock](https://github.com/chris-rock)) +- add badges [\#17](https://github.com/dev-sec/ansible-ssh-hardening/pull/17) ([chris-rock](https://github.com/chris-rock)) +- fix meta.yml [\#16](https://github.com/dev-sec/ansible-ssh-hardening/pull/16) ([chris-rock](https://github.com/chris-rock)) +- add more information to changelog [\#15](https://github.com/dev-sec/ansible-ssh-hardening/pull/15) ([chris-rock](https://github.com/chris-rock)) +- Add meta-information for Ansible Galaxy [\#14](https://github.com/dev-sec/ansible-ssh-hardening/pull/14) ([rndmh3ro](https://github.com/rndmh3ro)) +- Update CHANGELOG.md [\#13](https://github.com/dev-sec/ansible-ssh-hardening/pull/13) ([rndmh3ro](https://github.com/rndmh3ro)) +- Add handler to restart ssh only if necessary. Fix \#6 [\#11](https://github.com/dev-sec/ansible-ssh-hardening/pull/11) ([rndmh3ro](https://github.com/rndmh3ro)) +- add more descriptions [\#10](https://github.com/dev-sec/ansible-ssh-hardening/pull/10) ([chris-rock](https://github.com/chris-rock)) +- add travis config for ansible [\#9](https://github.com/dev-sec/ansible-ssh-hardening/pull/9) ([chris-rock](https://github.com/chris-rock)) +- update .kitchen.yml to find playbook role in tests [\#8](https://github.com/dev-sec/ansible-ssh-hardening/pull/8) ([chris-rock](https://github.com/chris-rock)) +- Oracle support [\#5](https://github.com/dev-sec/ansible-ssh-hardening/pull/5) ([rndmh3ro](https://github.com/rndmh3ro)) +- Remove custom Vagrantfile-reference. Fix \#2 [\#4](https://github.com/dev-sec/ansible-ssh-hardening/pull/4) ([rndmh3ro](https://github.com/rndmh3ro)) +- Remove custom Vagrantfile-reference. Fix \#2 [\#3](https://github.com/dev-sec/ansible-ssh-hardening/pull/3) ([rndmh3ro](https://github.com/rndmh3ro)) +- Fix missing gem [\#1](https://github.com/dev-sec/ansible-ssh-hardening/pull/1) ([chris-rock](https://github.com/chris-rock)) + + + +\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)* diff --git a/ansible/roles/dev-sec.ssh-hardening/CONTRIBUTING.md b/ansible/roles/dev-sec.ssh-hardening/CONTRIBUTING.md new file mode 100644 index 0000000..783c10f --- /dev/null +++ b/ansible/roles/dev-sec.ssh-hardening/CONTRIBUTING.md @@ -0,0 +1,85 @@ +# Contributor Guideline + +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!! + +## Communication + +### GitHub repositories + +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. + +## git and GitHub + +In order to contribute code please: + +1. Fork the project on GitHub +2. Clone the project +3. Add changes (and tests) +4. Commit and push +5. Create a merge-request + +To have your code merged, see the expectations listed below. + +You can find a well-written guide [here](https://help.github.com/articles/fork-a-repo). + +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. + +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). + + +## Expectations + +### Don't reinvent the wheel + +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. + +* For Chef refer to the official [opscode community cookbooks](http://community.opscode.com/cookbooks). +* For Puppet head to the [Puppet Forge](https://forge.puppetlabs.com/) and take a node of the Puppet supported modules. +* For Ansible check the [Ansible Module Index](http://docs.ansible.com/list_of_all_modules.html) + +These projects are generally hosted on GitHub as well. + +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)). + + +### Be explicit + +* Please avoid using nonsensical property and variable names. +* Use self-describing attribute names for user configuration. +* 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. + + +### Add tests + +The security review of this project is done using integration tests. + +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. + +You may add a new feature request by creating a test for whatever value you need. + +All tests will be reviewed internally for their validity and overall project direction. + + +### Document your code + +As code is more often read than written, please provide documentation in all projects. + +Adhere to the respective guidelines for documentation: + +* Chef generally documents code based explicit readme files. For code documentation please use [yard-chef](https://github.com/rightscale/yard-chef) +* [Puppet module documentation](http://docs.puppetlabs.com/puppet/latest/reference/modules_documentation.html) + + +### Follow coding styles + +We generally include test for coding guidelines: + +* Chef follows [Foodcritic](http://acrmp.github.io/foodcritic/) +* Puppet is checked with [puppet-lint](http://puppet-lint.com/checks/) +* Ansible is checked by running the playbook with the syntax-check option, e.g. `ansible-playbook foo.yml --syntax-check` + +Remember: Code is generally read much more often than written. + +### Use Markdown + +Wherever possible, please refrain from any other formats and stick to simple markdown. diff --git a/ansible/roles/dev-sec.ssh-hardening/Gemfile b/ansible/roles/dev-sec.ssh-hardening/Gemfile new file mode 100644 index 0000000..c11b3bf --- /dev/null +++ b/ansible/roles/dev-sec.ssh-hardening/Gemfile @@ -0,0 +1,18 @@ +# encoding: utf-8 + +source 'https://rubygems.org' + +group :integration do + gem 'test-kitchen', '~> 1.0' + gem 'kitchen-ansible' + gem 'kitchen-vagrant' + gem 'kitchen-inspec' + gem 'kitchen-sharedtests', '~> 0.2.0' + gem 'kitchen-sync' + gem 'kitchen-transport-rsync' + gem 'kitchen-docker' +end + +group :tools do + gem 'github_changelog_generator', '~> 1' +end diff --git a/ansible/roles/dev-sec.ssh-hardening/README.md b/ansible/roles/dev-sec.ssh-hardening/README.md new file mode 100644 index 0000000..18f305b --- /dev/null +++ b/ansible/roles/dev-sec.ssh-hardening/README.md @@ -0,0 +1,165 @@ +# ssh-hardening (Ansible Role) + +[![Build Status](http://img.shields.io/travis/dev-sec/ansible-ssh-hardening.svg)][1] +[![Gitter Chat](https://badges.gitter.im/Join%20Chat.svg)][2] +[![Ansible Galaxy](https://img.shields.io/badge/galaxy-ssh--hardening-660198.svg)][3] + +## Description + +This role provides secure ssh-client and ssh-server configurations. + +Warning: This role disables root-login on the target server! Please make sure you have another user with su or sudo permissions that can login into the server. + +## Requirements + +* Ansible > 2.2.1 + +## Role Variables +| Name | Default Value | Description | +| -------------- | ------------- | -----------------------------------| +|`network_ipv6_enable` | false |true if IPv6 is needed| +|`ssh_client_cbc_required` | false |true if CBC for ciphers is required. This is usually only necessary, if older M2M mechanism need to communicate with SSH, that don't have any of the configured secure ciphers enabled. CBC is a weak alternative. Anything weaker should be avoided and is thus not available.| +|`ssh_server_cbc_required` | false |true if CBC for ciphers is required. This is usually only necessary, if older M2M mechanism need to communicate with SSH, that don't have any of the configured secure ciphers enabled. CBC is a weak alternative. Anything weaker should be avoided and is thus not available.| +|`ssh_client_weak_hmac` | false |true if weaker HMAC mechanisms are required. This is usually only necessary, if older M2M mechanism need to communicate with SSH, that don't have any of the configured secure HMACs enabled.| +|`ssh_server_weak_hmac` | false |true if weaker HMAC mechanisms are required. This is usually only necessary, if older M2M mechanism need to communicate with SSH, that don't have any of the configured secure HMACs enabled.| +|`ssh_client_weak_kex` | false |true if weaker Key-Exchange (KEX) mechanisms are required. This is usually only necessary, if older M2M mechanism need to communicate with SSH, that don't have any of the configured secure KEXs enabled.| +|`ssh_server_weak_kex` | false |true if weaker Key-Exchange (KEX) mechanisms are required. This is usually only necessary, if older M2M mechanism need to communicate with SSH, that don't have any of the configured secure KEXs enabled.| +|`ssh_server_ports` | ['22'] |ports on which ssh-server should listen| +|`ssh_client_port` | '22' |port to which ssh-client should connect| +|`ssh_listen_to` | ['0.0.0.0'] |one or more ip addresses, to which ssh-server should listen to. Default is all adresseses, but should be configured to specific addresses for security reasons!| +|`ssh_host_key_files` | ['/etc/ssh/ssh_host_rsa_key', '/etc/ssh/ssh_host_dsa_key', '/etc/ssh/ssh_host_ecdsa_key'] |Host keys to look for when starting sshd.| +|`ssh_client_alive_interval` | 600 | specifies an interval for sending keepalive messages | +|`ssh_client_alive_count` | 3 | defines how often keep-alive messages are sent | +|`ssh_remote_hosts` | [] | one or more hosts and their custom options for the ssh-client. Default is empty. See examples in `defaults/main.yml`.| +|`ssh_allow_root_with_key` | false | false to disable root login altogether. Set to true to allow root to login via key-based mechanism.| +|`ssh_allow_tcp_forwarding` | false | false to disable TCP Forwarding. Set to true to allow TCP Forwarding.| +|`ssh_allow_agent_forwarding` | false | false to disable Agent Forwarding. Set to true to allow Agent Forwarding.| +|`ssh_use_pam` | false | false to disable pam authentication.| +|`ssh_deny_users` | '' | if specified, login is disallowed for user names that match one of the patterns.| +|`ssh_allow_users` | '' | if specified, login is allowed only for user names that match one of the patterns.| +|`ssh_deny_groups` | '' | if specified, login is disallowed for users whose primary group or supplementary group list matches one of the patterns.| +|`ssh_allow_groups` | '' | if specified, login is allowed only for users whose primary group or supplementary group list matches one of the patterns.| +|`ssh_print_motd` | false | false to disable printing of the MOTD| +|`ssh_print_last_log` | false | false to disable display of last login information| +|`sftp_enabled` | false | true to enable sftp configuration| +|`sftp_chroot_dir` | /home/%u | change default sftp chroot location| +|`ssh_client_roaming` | false | enable experimental client roaming| +|`sshd_moduli_minimum` | 2048 | remove Diffie-Hellman parameters smaller than the defined size to mitigate logjam| +|`ssh_challengeresponseauthentication` | false | Specifies whether challenge-response authentication is allowed (e.g. via PAM) | + +## Example Playbook + + - hosts: localhost + roles: + - dev-sec.ssh-hardening + +## Local Testing + +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. + +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). + +Next install test-kitchen: + +```bash +# Install dependencies +gem install bundler +bundle install +``` + +### Testing with Docker +``` +# fast test on one machine +bundle exec kitchen test default-ubuntu-1204 + +# test on all machines +bundle exec kitchen test + +# for development +bundle exec kitchen create default-ubuntu-1204 +bundle exec kitchen converge default-ubuntu-1204 +``` + +### Testing with Virtualbox +``` +# fast test on one machine +KITCHEN_YAML=".kitchen.vagrant.yml" bundle exec kitchen test default-ubuntu-1204 + +# test on all machines +KITCHEN_YAML=".kitchen.vagrant.yml" bundle exec kitchen test + +# for development +KITCHEN_YAML=".kitchen.vagrant.yml" bundle exec kitchen create default-ubuntu-1204 +KITCHEN_YAML=".kitchen.vagrant.yml" bundle exec kitchen converge default-ubuntu-1204 +``` +For more information see [test-kitchen](http://kitchen.ci/docs/getting-started) + +## FAQ / Pitfalls + +**I can't log into my account. I have registered the client key, but it still doesn't let me it.** + +If you have exhausted all typical issues (firewall, network, key missing, wrong key, account disabled etc.), it may be that your account is locked. The quickest way to find out is to look at the password hash for your user: + + sudo grep myuser /etc/shadow + +If the hash includes an `!`, your account is locked: + + myuser:!:16280:7:60:7::: + +The proper way to solve this is to unlock the account (`passwd -u myuser`). If the user doesn't have a password, you should can unlock it via: + + usermod -p "*" myuser + +Alternatively, if you intend to use PAM, you enabled it via `ssh_use_pam: true`. PAM will allow locked users to get in with keys. + + +**Why doesn't my application connect via SSH anymore?** + +Always look into log files first and if possible look at the negotation between client and server that is completed when connecting. + +We have seen some issues in applications (based on python and ruby) that are due to their use of an outdated crypto set. This collides with this hardening module, which reduced the list of ciphers, message authentication codes (MACs) and key exchange (KEX) algorithms to a more secure selection. + +If you find this isn't enough, feel free to activate the attributes `cbc_requires` for ciphers, `weak_hmac` for MACs and `weak_kex`for KEX in the variables `ssh_client` or `ssh_server` based on where you want to support them. + +**After using the role Ansibles template/copy/file module does not work anymore!** + +This role deactivates SFTP. Ansible uses by default SFTP to transfer files to the remote hosts. You have to set `scp_if_ssh = True` in your ansible.cfg. This way Ansible uses SCP to copy files. + +**Cannot restart sshd-service due to lack of privileges** + +If you get the following error when running handler "restart sshd" +``` +Unable to restart service ssh: Failed to restart ssh.service: Access denied +``` +or +``` +failure 1 running systemctl show for 'ssh': Failed to connect to bus: No such file or directory +``` +either run the playbook as `root` (without `become: yes` at the playbook level), or add `become: yes` to the handler. + +This is a bug with Ansible: see [here](https://github.com/dev-sec/ansible-ssh-hardening/pull/81) and [here](https://github.com/ansible/ansible/issues/17490) for more information. + +## Contributing + +See [contributor guideline](CONTRIBUTING.md). + +## License and Author + +* Author:: Sebastian Gumprich +* Author:: Christoph Hartmann + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +[1]: http://travis-ci.org/dev-sec/ansible-ssh-hardening +[2]: https://gitter.im/dev-sec/general +[3]: https://galaxy.ansible.com/dev-sec/ssh-hardening/ diff --git a/ansible/roles/dev-sec.ssh-hardening/Rakefile b/ansible/roles/dev-sec.ssh-hardening/Rakefile new file mode 100644 index 0000000..639c958 --- /dev/null +++ b/ansible/roles/dev-sec.ssh-hardening/Rakefile @@ -0,0 +1,25 @@ +#!/usr/bin/env rake +# encoding: utf-8 + +require 'foodcritic' +require 'rspec/core/rake_task' + +# Rubocop before rspec so we don't lint vendored cookbooks +desc 'Run all tests except Kitchen (default task)' +task default: [:integration] + +# Automatically generate a changelog for this project. Only loaded if +# the necessary gem is installed. +begin + require 'github_changelog_generator/task' + GitHubChangelogGenerator::RakeTask.new :changelog +rescue LoadError + puts '>>>>> GitHub Changelog Generator not loaded, omitting tasks' +end + +desc 'Run integration tests' +task :integration do + concurrency = ENV['CONCURRENCY'] || 1 + os = ENV['OS'] || '' + sh('sh', '-c', "bundle exec kitchen test -c #{concurrency} #{os}") +end diff --git a/ansible/roles/dev-sec.ssh-hardening/ansible.cfg b/ansible/roles/dev-sec.ssh-hardening/ansible.cfg new file mode 100644 index 0000000..d7cda5b --- /dev/null +++ b/ansible/roles/dev-sec.ssh-hardening/ansible.cfg @@ -0,0 +1,14 @@ +# config file for ansible -- http://ansible.com/ +# ============================================== + +# nearly all parameters can be overridden in ansible-playbook +# or with command line flags. ansible will read ANSIBLE_CONFIG, +# ansible.cfg in the current working directory, .ansible.cfg in +# the home directory or /etc/ansible/ansible.cfg, whichever it +# finds first + +[defaults] +ansible_managed = Ansible managed: {file} modified on %Y-%m-%d by {uid} on {host} + +role_path = /vagrant +scp_if_ssh = True diff --git a/ansible/roles/dev-sec.ssh-hardening/default.yml b/ansible/roles/dev-sec.ssh-hardening/default.yml new file mode 100644 index 0000000..b75135b --- /dev/null +++ b/ansible/roles/dev-sec.ssh-hardening/default.yml @@ -0,0 +1,34 @@ +--- +- name: wrapper playbook for kitchen testing "ansible-ssh-hardening" with custom settings + hosts: localhost + pre_tasks: + - package: name="{{item}}" state=installed + with_items: + - "openssh-clients" + - "openssh-server" + ignore_errors: true + - apt: name="{{item}}" state=installed update_cache=true + with_items: + - "openssh-client" + - "openssh-server" + ignore_errors: true + - file: path="/var/run/sshd" state=directory + roles: + - ansible-ssh-hardening + vars: + network_ipv6_enable: true + ssh_allow_root_with_key: true + ssh_client_password_login: true + ssh_client_cbc_required: true + ssh_server_weak_hmac: true + ssh_client_weak_kex: true + ssh_remote_hosts: + - names: ['example.com', 'example2.com'] + options: ['Port 2222', 'ForwardAgent yes'] + - names: ['example3.com'] + options: ['StrictHostKeyChecking no'] + +- name: wrapper playbook for kitchen testing "ansible-ssh-hardening" with default settings + hosts: localhost + roles: + - ansible-ssh-hardening diff --git a/ansible/roles/dev-sec.ssh-hardening/defaults/main.yml b/ansible/roles/dev-sec.ssh-hardening/defaults/main.yml new file mode 100644 index 0000000..f36045a --- /dev/null +++ b/ansible/roles/dev-sec.ssh-hardening/defaults/main.yml @@ -0,0 +1,155 @@ +# true if IPv6 is needed +network_ipv6_enable: false # sshd + ssh + +# For which components (client and server) to generate the configuration for. Can be useful when running against a client without an SSH server. +ssh_client_hardening: true # ssh +ssh_server_hardening: true # sshd + +# true if CBC for ciphers is required. This is usually only necessary, if older M2M mechanism need to communicate with SSH, that don't have any of the configured secure ciphers enabled. CBC is a weak alternative. Anything weaker should be avoided and is thus not available. +ssh_client_cbc_required: false # ssh +ssh_server_cbc_required: false # sshd + +# true if weaker HMAC mechanisms are required. This is usually only necessary, if older M2M mechanism need to communicate with SSH, that don't have any of the configured secure HMACs enabled. +ssh_client_weak_hmac: false # ssh +ssh_server_weak_hmac: false # sshd + +# true if weaker Key-Exchange (KEX) mechanisms are required. This is usually only necessary, if older M2M mechanism need to communicate with SSH, that don't have any of the configured secure KEXs enabled. +ssh_client_weak_kex: false # ssh +ssh_server_weak_kex: false # sshd + +# If true, password login is allowed. For sshd, it is always set to no password login. +ssh_client_password_login: false # ssh + +# ports on which ssh-server should listen +ssh_server_ports: ['22'] # sshd + +# port to which ssh-client should connect +ssh_client_port: '22' # ssh + +# one or more ip addresses, to which ssh-server should listen to. Default is empty, but should be configured for security reasons! +ssh_listen_to: ['0.0.0.0'] # sshd + +# Host keys to look for when starting sshd. +ssh_host_key_files: ['/etc/ssh/ssh_host_rsa_key', '/etc/ssh/ssh_host_ecdsa_key', '/etc/ssh/ssh_host_ed25519_key'] # sshd + +# Specifies the maximum number of authentication attempts permitted per connection. Once the number of failures reaches half this value, additional failures are logged. +ssh_max_auth_retries: 2 + +ssh_client_alive_interval: 600 # sshd +ssh_client_alive_count: 3 # sshd + +# Hosts with custom options. # ssh +# Example: +# ssh_remote_hosts: +# - names: ['example.com', 'example2.com'] +# options: ['Port 2222', 'ForwardAgent yes'] +# - names: ['example3.com'] +# options: ['StrictHostKeyChecking no'] +ssh_remote_hosts: [] + +# false to disable root login altogether. Set to true to allow root to login via key-based mechanism. +ssh_allow_root_with_key: false # sshd + +# false to disable TCP Forwarding. Set to true to allow TCP Forwarding. +ssh_allow_tcp_forwarding: false # sshd + +# false to disable Agent Forwarding. Set to true to allow Agent Forwarding. +ssh_allow_agent_forwarding: false # sshd + +# false to disable pam authentication. +ssh_use_pam: false # sshd + +# if specified, login is disallowed for user names that match one of the patterns. +ssh_deny_users: '' # sshd + +# if specified, login is allowed only for user names that match one of the patterns. +ssh_allow_users: '' # sshd + +# if specified, login is disallowed for users whose primary group or supplementary group list matches one of the patterns. +ssh_deny_groups: '' # sshd + +# if specified, login is allowed only for users whose primary group or supplementary group list matches one of the patterns. +ssh_allow_groups: '' # sshd + +# false to disable printing of the MOTD +ssh_print_motd: false # sshd + +# false to disable display of last login information +ssh_print_last_log: false # sshd + +# false to disable serving /etc/ssh/banner.txt before authentication is allowed +ssh_banner: false # sshd + +# false to disable distribution version leakage during initial protocol handshake +ssh_print_debian_banner: false # sshd (Debian OS family only) + +# true to enable sftp configuration +sftp_enabled: false + +# change default sftp chroot location +sftp_chroot_dir: /home/%u + +# enable experimental client roaming +ssh_client_roaming: false + + +ssh_ps53: 'yes' +ssh_ps59: 'sandbox' + +ssh_macs_53_default: + - hmac-ripemd160 + - hmac-sha1 + +ssh_macs_59_default: + - hmac-sha2-512 + - hmac-sha2-256 + - hmac-ripemd160 + +ssh_macs_59_weak: "{{ ssh_macs_59_default + ['hmac-sha1'] }}" + +ssh_macs_66_default: + - hmac-sha2-512-etm@openssh.com + - hmac-sha2-256-etm@openssh.com + - hmac-ripemd160-etm@openssh.com + - umac-128-etm@openssh.com + - hmac-sha2-512 + - hmac-sha2-256 + - hmac-ripemd160 + +ssh_macs_66_weak: "{{ ssh_macs_66_default + ['hmac-sha1'] }}" + +ssh_ciphers_53_default: + - aes256-ctr + - aes192-ctr + - aes128-ctr + +ssh_ciphers_53_weak: "{{ ssh_ciphers_53_default + ['aes256-cbc', 'aes192-cbc', 'aes128-cbc'] }}" + +ssh_ciphers_66_default: + - chacha20-poly1305@openssh.com + - aes256-gcm@openssh.com + - aes128-gcm@openssh.com + - aes256-ctr + - aes192-ctr + - aes128-ctr + +ssh_ciphers_66_weak: "{{ ssh_ciphers_66_default + ['aes256-cbc', 'aes192-cbc', 'aes128-cbc'] }}" + +ssh_kex_59_default: + - diffie-hellman-group-exchange-sha256 + +ssh_kex_59_weak: "{{ ssh_kex_59_default + ['diffie-hellman-group14-sha1', 'diffie-hellman-group-exchange-sha1', 'diffie-hellman-group1-sha1'] }}" + +ssh_kex_66_default: + - curve25519-sha256@libssh.org + - diffie-hellman-group-exchange-sha256 + +ssh_kex_66_weak: "{{ ssh_kex_66_default + ['diffie-hellman-group14-sha1', 'diffie-hellman-group-exchange-sha1', 'diffie-hellman-group1-sha1'] }}" + +# directory where to store ssh_password policy +ssh_custom_selinux_dir: '/etc/selinux/local-policies' + +sshd_moduli_minimum: 2048 + +# disable ChallengeResponseAuthentication +ssh_challengeresponseauthentication: false diff --git a/ansible/roles/dev-sec.ssh-hardening/files/ssh_password b/ansible/roles/dev-sec.ssh-hardening/files/ssh_password new file mode 100644 index 0000000..160d739 --- /dev/null +++ b/ansible/roles/dev-sec.ssh-hardening/files/ssh_password @@ -0,0 +1,10 @@ +module ssh_password 1.0; + +require { + type sshd_t; + type shadow_t; + class file { read open }; +} + +#============= sshd_t ============== +allow sshd_t shadow_t:file { read open }; diff --git a/ansible/roles/dev-sec.ssh-hardening/handlers/main.yml b/ansible/roles/dev-sec.ssh-hardening/handlers/main.yml new file mode 100644 index 0000000..da55b03 --- /dev/null +++ b/ansible/roles/dev-sec.ssh-hardening/handlers/main.yml @@ -0,0 +1,2 @@ +- name: restart sshd + service: name={{ sshd_service_name }} state=restarted diff --git a/ansible/roles/dev-sec.ssh-hardening/kitchen_vagrant_block.rb b/ansible/roles/dev-sec.ssh-hardening/kitchen_vagrant_block.rb new file mode 100644 index 0000000..2814cdc --- /dev/null +++ b/ansible/roles/dev-sec.ssh-hardening/kitchen_vagrant_block.rb @@ -0,0 +1,19 @@ +# This is a Vagrant block to allow proxy settings to be carried into Kitchen +# You need this for all of yum/apt etc. to work! +unless ENV['http_proxy'].nil? || Vagrant.has_plugin?("vagrant-proxyconf") + raise "Missing required plugin 'vagrant-proxyconf' to support HTTP(S) proxies, run `vagrant plugin install vagrant-proxyconf`" +end + +Vagrant.configure(2) do |config| + if Vagrant.has_plugin?("vagrant-proxyconf") + config.proxy.http = "#{ENV['http_proxy']}" + config.proxy.https = "#{ENV['https_proxy']}" + config.proxy.no_proxy = "localhost,127.0.0.1" + end + + # You may have vagrant-vbguest plugin installed to keep your images up to date + # - but will probably have VBoxAddition build issues with the foreign boxes listed in .kitchen.vagrant.yml + if Vagrant.has_plugin?("vagrant-vbguest") + config.vbguest.auto_update = false + end +end diff --git a/ansible/roles/dev-sec.ssh-hardening/meta/.galaxy_install_info b/ansible/roles/dev-sec.ssh-hardening/meta/.galaxy_install_info new file mode 100644 index 0000000..0e52249 --- /dev/null +++ b/ansible/roles/dev-sec.ssh-hardening/meta/.galaxy_install_info @@ -0,0 +1 @@ +{install_date: 'Sun Apr 23 07:59:24 2017', version: 4.0.0} diff --git a/ansible/roles/dev-sec.ssh-hardening/meta/main.yml b/ansible/roles/dev-sec.ssh-hardening/meta/main.yml new file mode 100644 index 0000000..3e03c03 --- /dev/null +++ b/ansible/roles/dev-sec.ssh-hardening/meta/main.yml @@ -0,0 +1,26 @@ +--- +galaxy_info: + author: "Sebastian Gumprich" + description: 'This Ansible role provides numerous security-related ssh configurations, providing all-round base protection.' + company: Hardening Framework Team + license: Apache License 2.0 + min_ansible_version: '2.2.1' + platforms: + - name: EL + versions: + - 6 + - 7 + - name: Ubuntu + versions: + - precise + - trusty + - xenial + - name: Debian + versions: + - wheezy + - jessie + galaxy_tags: + - system + - security + - hardening +dependencies: [] diff --git a/ansible/roles/dev-sec.ssh-hardening/tasks/main.yml b/ansible/roles/dev-sec.ssh-hardening/tasks/main.yml new file mode 100644 index 0000000..11043d2 --- /dev/null +++ b/ansible/roles/dev-sec.ssh-hardening/tasks/main.yml @@ -0,0 +1,105 @@ +--- +- name: Set OS dependent variables + include_vars: "{{ item }}" + with_first_found: + - "{{ ansible_distribution }}_{{ ansible_distribution_major_version }}.yml" + - "{{ ansible_distribution }}.yml" + - "{{ ansible_os_family }}_{{ ansible_distribution_major_version }}.yml" + - "{{ ansible_os_family }}.yml" + +- name: get openssh-version + shell: ssh -V 2>&1 | sed -r 's/.*_([0-9]*\.[0-9]).*/\1/g' + changed_when: false + register: sshd_version + +- name: set hostkeys according to openssh-version + set_fact: + ssh_host_key_files: ['/etc/ssh/ssh_host_rsa_key'] + when: sshd_version.stdout >= '5.3' + +- name: set hostkeys according to openssh-version + set_fact: + ssh_host_key_files: ['/etc/ssh/ssh_host_rsa_key', '/etc/ssh/ssh_host_ecdsa_key'] + when: sshd_version.stdout >= '6.0' + +- name: set hostkeys according to openssh-version + set_fact: + ssh_host_key_files: ['/etc/ssh/ssh_host_rsa_key', '/etc/ssh/ssh_host_ecdsa_key', '/etc/ssh/ssh_host_ed25519_key'] + when: sshd_version.stdout >= '6.3' + +- name: create sshd_config and set permissions to root/600 + template: src='opensshd.conf.j2' dest='/etc/ssh/sshd_config' mode=0600 owner="{{ ssh_owner }}" group="{{ ssh_group }}" validate="/usr/sbin/sshd -T -f %s" + notify: restart sshd + when: ssh_server_hardening + +- name: create ssh_config and set permissions to root/644 + template: src='openssh.conf.j2' dest='/etc/ssh/ssh_config' mode=0644 owner="{{ ssh_owner }}" group="{{ ssh_group }}" + when: ssh_client_hardening + +- name: Check if /etc/ssh/moduli contains weak DH parameters + shell: awk '$5 < {{ sshd_moduli_minimum }}' /etc/ssh/moduli + register: sshd_register_moduli + changed_when: false + check_mode: no + +- name: remove all small primes + shell: awk '$5 >= {{ sshd_moduli_minimum }}' /etc/ssh/moduli > /etc/ssh/moduli.new ; + [ -r /etc/ssh/moduli.new -a -s /etc/ssh/moduli.new ] && mv /etc/ssh/moduli.new /etc/ssh/moduli || true + notify: restart sshd + when: sshd_register_moduli.stdout + +- name: test to see if selinux is running + command: getenforce + register: sestatus + failed_when: false + changed_when: false + check_mode: no + +- block: # only runs when selinux is running + - name: install selinux dependencies when selinux is installed on RHEL or Oracle Linux + yum: name="{{item}}" state=installed + with_items: + - policycoreutils-python + - checkpolicy + when: ansible_os_family == 'RedHat' or ansible_os_family == 'Oracle Linux' + + - name: install selinux dependencies when selinux is installed on Debian or Ubuntu + apt: name="{{item}}" state=installed + with_items: + - policycoreutils + - checkpolicy + when: ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu' + + - name: check if ssh_password module is already installed + shell: "semodule -l| grep ssh_password" + register: ssh_password_module + failed_when: false + changed_when: false + check_mode: no + + # The following tasks only get executed when selinux is in state enforcing, UsePam is "no" and the ssh_password module is installed. + # See this issue for more info: https://github.com/hardening-io/ansible-ssh-hardening/issues/23 + - block: + - name: Create selinux custom policy drop folder + file: path='{{ ssh_custom_selinux_dir }}' state=directory owner=root group=root mode=0750 + + - name: Distributing custom selinux policies + copy: src='ssh_password' dest='{{ ssh_custom_selinux_dir }}' + + - name: check and compile policy + shell: checkmodule -M -m -o {{ ssh_custom_selinux_dir }}/ssh_password.mod {{ ssh_custom_selinux_dir }}/ssh_password + + - name: create selinux policy module package + shell: semodule_package -o {{ ssh_custom_selinux_dir }}/ssh_password.pp -m {{ ssh_custom_selinux_dir }}/ssh_password.mod + + - name: install selinux policy + shell: semodule -i {{ ssh_custom_selinux_dir }}/ssh_password.pp + + when: not ssh_use_pam and sestatus.stdout != 'Disabled' and ssh_password_module.stdout.find('ssh_password') != 0 + + # The following tasks only get executed when selinux is in state enforcing, UsePam is "yes" and the ssh_password module is installed. + - name: remove selinux-policy when Pam is used, because Allowing sshd to read the shadow file directly is considered a potential security risk (http://danwalsh.livejournal.com/12333.html) + shell: semodule -r ssh_password + when: ssh_use_pam and ssh_password_module.stdout.find('ssh_password') == 0 + + when: sestatus.rc == 0 diff --git a/ansible/roles/dev-sec.ssh-hardening/templates/openssh.conf.j2 b/ansible/roles/dev-sec.ssh-hardening/templates/openssh.conf.j2 new file mode 100644 index 0000000..1d0106f --- /dev/null +++ b/ansible/roles/dev-sec.ssh-hardening/templates/openssh.conf.j2 @@ -0,0 +1,148 @@ +# {{ansible_managed}} + +# This is the ssh client system-wide configuration file. +# See ssh_config(5) for more information on any settings used. Comments will be added only to clarify why a configuration was chosen. +# +# Basic configuration +# =================== + +# Address family should always be limited to the active network configuration. +AddressFamily {{ 'any' if network_ipv6_enable else 'inet' }} + +{% for host in ssh_remote_hosts -%} +{% if loop.first %} +# Host-specific configuration +{% endif %} +Host {{ host.names | join(' ') }} + {{ host.options | join("\n") | indent(2) }} + +{% endfor -%} + +# Global defaults for all Hosts +Host * + +# The port at the destination should be defined +Port {{ ssh_client_port }} + +# Identity file configuration. You may restrict available identity files. Otherwise ssh will search for a pattern and use any that matches. +#IdentityFile ~/.ssh/identity +#IdentityFile ~/.ssh/id_rsa +#IdentityFile ~/.ssh/id_dsa + + +# Security configuration +# ====================== + +# Set the protocol version to 2 for security reasons. Disables legacy support. +Protocol 2 + +# Make sure passphrase querying is enabled +BatchMode no + +# Prevent IP spoofing by checking to host IP against the `known_hosts` file. +CheckHostIP yes + +# Always ask before adding keys to the `known_hosts` file. Do not set to `yes`. +StrictHostKeyChecking ask + +# **Ciphers** -- If your clients don't support CTR (eg older versions), cbc will be added +# CBC: is true if you want to connect with OpenSSL-base libraries +# eg ruby Net::SSH::Transport::CipherFactory requires cbc-versions of the given openssh ciphers to work +# -- see: (http://net-ssh.github.com/net-ssh/classes/Net/SSH/Transport/CipherFactory.html) +# +{% if ssh_client_cbc_required -%} + {% if (ansible_distribution == 'Ubuntu' and ansible_distribution_version >= '14.04') or (ansible_distribution == 'Debian' and ansible_distribution_version >= '8') or (ansible_os_family in ['Oracle Linux', 'RedHat'] and ansible_distribution_major_version >= '7') or (ansible_distribution == 'FreeBSD' and ansible_distribution_version >= '11') -%} + Ciphers {{ ssh_ciphers_66_weak | join(',') }} + {% else -%} + Ciphers {{ ssh_ciphers_53_weak | join(',') }} + {% endif %} +{% else -%} + {% if (ansible_distribution == 'Ubuntu' and ansible_distribution_version >= '14.04') or (ansible_distribution == 'Debian' and ansible_distribution_version >= '8') or (ansible_os_family in ['Oracle Linux', 'RedHat'] and ansible_distribution_major_version >= '7') or (ansible_distribution == 'FreeBSD' and ansible_distribution_version >= '11') -%} + Ciphers {{ ssh_ciphers_66_default | join(',') }} + {% else -%} + Ciphers {{ ssh_ciphers_53_default | join(',') }} + {% endif %} +{% endif %} + +# **Hash algorithms** -- Make sure not to use SHA1 for hashing, unless it is really necessary. +# Weak HMAC is sometimes required if older package versions are used +# eg Ruby's Net::SSH at around 2.2.* doesn't support sha2 for hmac, so this will have to be set true in this case. +# +{% if ssh_client_weak_hmac -%} + {% if (ansible_distribution == 'Ubuntu' and ansible_distribution_version >= '14.04') or (ansible_distribution == 'Debian' and ansible_distribution_version >= '8') or (ansible_os_family in ['Oracle Linux', 'RedHat'] and ansible_distribution_major_version >= '7') or (ansible_distribution == 'FreeBSD' and ansible_distribution_version >= '11') -%} + MACs {{ ssh_macs_66_weak | join(',') }} + {% elif ansible_os_family in ['Oracle Linux', 'RedHat'] and ansible_distribution_major_version <= '6' -%} + MACs {{ ssh_macs_53_default | join(',') }} + {% endif %} +{% else -%} + {% if (ansible_distribution == 'Ubuntu' and ansible_distribution_version >= '14.04') or (ansible_distribution == 'Debian' and ansible_distribution_version >= '8') or (ansible_os_family in ['Oracle Linux', 'RedHat'] and ansible_distribution_major_version >= '7') or (ansible_distribution == 'FreeBSD' and ansible_distribution_version >= '11') -%} + MACs {{ ssh_macs_66_default | join(',') }} + {% elif ansible_os_family in ['Oracle Linux', 'RedHat'] and ansible_distribution_major_version <= '6' -%} + MACs {{ ssh_macs_53_default | join(',') }} + {% else -%} + MACs {{ ssh_macs_59_default | join(',') }} + {% endif %} +{% endif %} + +# Alternative setting, if OpenSSH version is below v5.9 +#MACs hmac-ripemd160 + +# **Key Exchange Algorithms** -- Make sure not to use SHA1 for kex, unless it is really necessary +# Weak kex is sometimes required if older package versions are used +# eg ruby's Net::SSH at around 2.2.* doesn't support sha2 for kex, so this will have to be set true in this case. +# +{% if (ansible_distribution == 'Ubuntu' and ansible_distribution_version >= '14.04') or (ansible_distribution == 'Debian' and ansible_distribution_version >= '8') or (ansible_os_family in ['Oracle Linux', 'RedHat'] and ansible_distribution_major_version >= '7') or (ansible_distribution == 'FreeBSD' and ansible_distribution_version >= '11') -%} + {% if ssh_client_weak_kex -%} + KexAlgorithms {{ ssh_kex_66_weak | join(',') }} + {% else -%} + KexAlgorithms {{ ssh_kex_66_default | join(',') }} + {% endif %} +{% else -%} + {% if ansible_os_family in ['Oracle Linux', 'RedHat'] and ansible_distribution_major_version <= '6' -%} + #KexAlgorithms + {% elif ssh_client_weak_kex -%} + KexAlgorithms {{ ssh_kex_59_weak | join(',') }} + {% else -%} + KexAlgorithms {{ ssh_kex_59_default | join(',') }} + {% endif %} +{% endif %} + + +# Disable agent forwarding, since local agent could be accessed through forwarded connection. +ForwardAgent no + +# Disable X11 forwarding, since local X11 display could be accessed through forwarded connection. +ForwardX11 no + +# Never use host-based authentication. It can be exploited. +HostbasedAuthentication no +RhostsRSAAuthentication no + +# Enable RSA authentication via identity files. +RSAAuthentication yes + +# Disable password-based authentication, it can allow for potentially easier brute-force attacks. +PasswordAuthentication {{ 'yes' if ssh_client_password_login else 'no' }} + +# Only use GSSAPIAuthentication if implemented on the network. +GSSAPIAuthentication no +GSSAPIDelegateCredentials no + +# Disable tunneling +Tunnel no + +# Disable local command execution. +PermitLocalCommand no + + +# Misc. configuration +# =================== + +# Enable compression. More pressure on the CPU, less on the network. +Compression yes + +#EscapeChar ~ +#VisualHostKey yes + +# Disable experimental client roaming. This is known to cause potential issues with secrets being disclosed to malicious servers and defaults to being disabled. +UseRoaming {{ 'yes' if ssh_client_roaming else 'no' }} diff --git a/ansible/roles/dev-sec.ssh-hardening/templates/opensshd.conf.j2 b/ansible/roles/dev-sec.ssh-hardening/templates/opensshd.conf.j2 new file mode 100644 index 0000000..11087cc --- /dev/null +++ b/ansible/roles/dev-sec.ssh-hardening/templates/opensshd.conf.j2 @@ -0,0 +1,224 @@ +# {{ansible_managed}} + +# This is the ssh client system-wide configuration file. +# See sshd_config(5) for more information on any settings used. Comments will be added only to clarify why a configuration was chosen. + +# Basic configuration +# =================== + +# Either disable or only allowssh root login via certificates. +PermitRootLogin {{ 'without-password' if ssh_allow_root_with_key else 'no' }} + +# Define which port sshd should listen to. Default to `22`. +{% for port in ssh_server_ports -%} +Port {{port}} +{% endfor %} + +# Address family should always be limited to the active network configuration. +AddressFamily {{ 'any' if network_ipv6_enable else 'inet' }} + +# Define which addresses sshd should listen to. Default to `0.0.0.0`, ie make sure you put your desired address in here, since otherwise sshd will listen to everyone. +{% for address in ssh_listen_to -%} +ListenAddress {{address}} +{% endfor %} + +# List HostKeys here. +{% for key in ssh_host_key_files -%} +HostKey {{key}} # Req 20 +{% endfor %} + +# Security configuration +# ====================== + +# Set the protocol version to 2 for security reasons. Disables legacy support. +Protocol 2 + +# Make sure sshd checks file modes and ownership before accepting logins. This prevents accidental misconfiguration. +StrictModes yes + +# Logging, obsoletes QuietMode and FascistLogging +SyslogFacility AUTH +LogLevel VERBOSE + +# Cryptography +# ------------ + +# **Ciphers** -- If your clients don't support CTR (eg older versions), cbc will be added +# CBC: is true if you want to connect with OpenSSL-base libraries +# eg ruby Net::SSH::Transport::CipherFactory requires cbc-versions of the given openssh ciphers to work +# -- see: (http://net-ssh.github.com/net-ssh/classes/Net/SSH/Transport/CipherFactory.html) +# +{% if ssh_server_cbc_required -%} + {% if (ansible_distribution == 'Ubuntu' and ansible_distribution_version >= '14.04') or (ansible_distribution == 'Debian' and ansible_distribution_version >= '8') or (ansible_os_family in ['Oracle Linux', 'RedHat'] and ansible_distribution_major_version >= '7') or (ansible_distribution == 'FreeBSD' and ansible_distribution_version >= '11') -%} + Ciphers {{ ssh_ciphers_66_weak | join(',') }} + {% else %} + Ciphers {{ ssh_ciphers_53_weak | join(',') }} + {% endif %} +{% else -%} + {% if (ansible_distribution == 'Ubuntu' and ansible_distribution_version >= '14.04') or (ansible_distribution == 'Debian' and ansible_distribution_version >= '8') or (ansible_os_family in ['Oracle Linux', 'RedHat'] and ansible_distribution_major_version >= '7') or (ansible_distribution == 'FreeBSD' and ansible_distribution_version >= '11') -%} + Ciphers {{ ssh_ciphers_66_default | join(',') }} + {% else -%} + Ciphers {{ ssh_ciphers_53_default | join(',') }} + {% endif %} +{% endif %} + +# **Hash algorithms** -- Make sure not to use SHA1 for hashing, unless it is really necessary. +# Weak HMAC is sometimes required if older package versions are used +# eg Ruby's Net::SSH at around 2.2.* doesn't support sha2 for hmac, so this will have to be set true in this case. +# + +{% if ssh_server_weak_hmac -%} + {% if (ansible_distribution == 'Ubuntu' and ansible_distribution_version >= '14.04') or (ansible_distribution == 'Debian' and ansible_distribution_version >= '8') or (ansible_os_family in ['Oracle Linux', 'RedHat'] and ansible_distribution_major_version >= '7') or (ansible_distribution == 'FreeBSD' and ansible_distribution_version >= '11') -%} + MACs {{ ssh_macs_66_weak | join(',') }} + {% elif ansible_os_family in ['Oracle Linux', 'RedHat'] and ansible_distribution_major_version <= '6' -%} + MACs {{ ssh_macs_53_default | join(',') }} + {% endif %} +{% else -%} + {% if (ansible_distribution == 'Ubuntu' and ansible_distribution_version >= '14.04') or (ansible_distribution == 'Debian' and ansible_distribution_version >= '8') or (ansible_os_family in ['Oracle Linux', 'RedHat'] and ansible_distribution_major_version >= '7') or (ansible_distribution == 'FreeBSD' and ansible_distribution_version >= '11') -%} + MACs {{ ssh_macs_66_default | join(',') }} + {% elif ansible_os_family in ['Oracle Linux', 'RedHat'] and ansible_distribution_major_version <= '6' -%} + MACs {{ ssh_macs_53_default | join(',') }} + {% else -%} + MACs {{ ssh_macs_59_default | join(',') }} + {% endif %} +{% endif %} + +# Alternative setting, if OpenSSH version is below v5.9 +#MACs hmac-ripemd160 + +# **Key Exchange Algorithms** -- Make sure not to use SHA1 for kex, unless it is really necessary +# Weak kex is sometimes required if older package versions are used +# eg ruby's Net::SSH at around 2.2.* doesn't support sha2 for kex, so this will have to be set true in this case. +# based on: https://bettercrypto.org/static/applied-crypto-hardening.pdf +{% if (ansible_distribution == 'Ubuntu' and ansible_distribution_version >= '14.04') or (ansible_distribution == 'Debian' and ansible_distribution_version >= '8') or (ansible_os_family in ['Oracle Linux', 'RedHat'] and ansible_distribution_major_version >= '7') or (ansible_distribution == 'FreeBSD' and ansible_distribution_version >= '11') -%} + {% if ssh_server_weak_kex -%} + KexAlgorithms {{ ssh_kex_66_weak | join(',') }} + {% else -%} + KexAlgorithms {{ ssh_kex_66_default | join(',') }} + {% endif %} +{% else -%} + {% if ansible_os_family in ['Oracle Linux', 'RedHat'] and ansible_distribution_major_version <= '6' -%} + #KexAlgorithms + {% elif ssh_server_weak_kex -%} + KexAlgorithms {{ sshd_kex_59_weak | join(',') }} + {% else -%} + KexAlgorithms {{ ssh_kex_59_default | join(',') }} + {% endif %} +{% endif %} + +# Authentication +# -------------- + +# Secure Login directives. +UseLogin no +UsePrivilegeSeparation {% if (ansible_distribution == 'Debian' and ansible_distribution_major_version <= '6') or (ansible_os_family in ['Oracle Linux', 'RedHat'] and ansible_distribution_major_version <= '6') -%}{{ssh_ps53}}{% else %}{{ssh_ps59}}{% endif %} + +PermitUserEnvironment no +LoginGraceTime 30s +MaxAuthTries {{ssh_max_auth_retries}} +MaxSessions 10 +MaxStartups 10:30:100 + +# Enable public key authentication +PubkeyAuthentication yes + +# Never use host-based authentication. It can be exploited. +IgnoreRhosts yes +IgnoreUserKnownHosts yes +HostbasedAuthentication no + +# Enable PAM to enforce system wide rules +UsePAM {{ 'yes' if ssh_use_pam else 'no' }} + +# Disable password-based authentication, it can allow for potentially easier brute-force attacks. +PasswordAuthentication no +PermitEmptyPasswords no +ChallengeResponseAuthentication {{ 'yes' if ssh_challengeresponseauthentication else 'no' }} + +# Only enable Kerberos authentication if it is configured. +KerberosAuthentication no +KerberosOrLocalPasswd no +KerberosTicketCleanup yes +#KerberosGetAFSToken no + +# Only enable GSSAPI authentication if it is configured. +GSSAPIAuthentication no +GSSAPICleanupCredentials yes + +# In case you don't use PAM (`UsePAM no`), you can alternatively restrict users and groups here. For key-based authentication this is not necessary, since all keys must be explicitely enabled. +{% if ssh_deny_users -%} +DenyUsers {{ssh_deny_users}} +{% endif %} + +{% if ssh_allow_users -%} +AllowUsers {{ssh_allow_users}} +{% endif %} + +{% if ssh_deny_groups -%} +DenyGroups {{ssh_deny_groups}} +{% endif %} + +{% if ssh_allow_groups -%} +AllowGroups {{ssh_allow_groups}} +{% endif %} + +# Network +# ------- + +# Disable TCP keep alive since it is spoofable. Use ClientAlive messages instead, they use the encrypted channel +TCPKeepAlive no + +# Manage `ClientAlive..` signals via interval and maximum count. This will periodically check up to a `..CountMax` number of times within `..Interval` timeframe, and abort the connection once these fail. +ClientAliveInterval {{ssh_client_alive_interval}} +ClientAliveCountMax {{ssh_client_alive_count}} + +# Disable tunneling +PermitTunnel no + +# Disable forwarding tcp connections. +# no real advantage without denied shell access +AllowTcpForwarding {{ 'yes' if ssh_allow_tcp_forwarding else 'no' }} + +# Disable agent formwarding, since local agent could be accessed through forwarded connection. +# no real advantage without denied shell access +AllowAgentForwarding {{ 'yes' if ssh_allow_agent_forwarding else 'no' }} + +# Do not allow remote port forwardings to bind to non-loopback addresses. +GatewayPorts no + +# Disable X11 forwarding, since local X11 display could be accessed through forwarded connection. +X11Forwarding no +X11UseLocalhost yes + + +# Misc. configuration +# =================== + +PrintMotd {{ 'yes' if ssh_print_motd else 'no' }} + +{% if ansible_os_family != 'FreeBSD' %} +PrintLastLog {{ 'yes' if ssh_print_last_log else 'no' }} +{% endif %} + +Banner {{ '/etc/ssh/banner.txt' if ssh_banner else 'none' }} + +{% if ansible_os_family == 'Debian' %} +DebianBanner {{ 'yes' if ssh_print_debian_banner else 'no' }} +{% endif %} + +{% if sftp_enabled %} +# Configuration, in case SFTP is used +## override default of no subsystems +## Subsystem sftp /opt/app/openssh5/libexec/sftp-server +Subsystem sftp internal-sftp -l INFO -f LOCAL6 +# +## These lines must appear at the *end* of sshd_config +Match Group sftponly +ForceCommand internal-sftp -l INFO -f LOCAL6 +ChrootDirectory {{ sftp_chroot_dir }} +AllowTcpForwarding no +AllowAgentForwarding no +PasswordAuthentication no +PermitRootLogin no +X11Forwarding no +{% endif %} diff --git a/ansible/roles/dev-sec.ssh-hardening/vars/Debian.yml b/ansible/roles/dev-sec.ssh-hardening/vars/Debian.yml new file mode 100644 index 0000000..cd26ce0 --- /dev/null +++ b/ansible/roles/dev-sec.ssh-hardening/vars/Debian.yml @@ -0,0 +1,3 @@ +sshd_service_name: ssh +ssh_owner: root +ssh_group: root diff --git a/ansible/roles/dev-sec.ssh-hardening/vars/FreeBSD.yml b/ansible/roles/dev-sec.ssh-hardening/vars/FreeBSD.yml new file mode 100644 index 0000000..173b78a --- /dev/null +++ b/ansible/roles/dev-sec.ssh-hardening/vars/FreeBSD.yml @@ -0,0 +1,3 @@ +sshd_service_name: sshd +ssh_owner: root +ssh_group: wheel diff --git a/ansible/roles/dev-sec.ssh-hardening/vars/Oracle Linux.yml b/ansible/roles/dev-sec.ssh-hardening/vars/Oracle Linux.yml new file mode 100644 index 0000000..62dd98f --- /dev/null +++ b/ansible/roles/dev-sec.ssh-hardening/vars/Oracle Linux.yml @@ -0,0 +1,3 @@ +sshd_service_name: sshd +ssh_owner: root +ssh_group: root diff --git a/ansible/roles/dev-sec.ssh-hardening/vars/RedHat.yml b/ansible/roles/dev-sec.ssh-hardening/vars/RedHat.yml new file mode 100644 index 0000000..62dd98f --- /dev/null +++ b/ansible/roles/dev-sec.ssh-hardening/vars/RedHat.yml @@ -0,0 +1,3 @@ +sshd_service_name: sshd +ssh_owner: root +ssh_group: root diff --git a/ansible/roles/dev-sec.ssh-hardening/vars/main.yml b/ansible/roles/dev-sec.ssh-hardening/vars/main.yml new file mode 100644 index 0000000..e69de29 diff --git a/ansible/roles/docker-compose/.gitignore b/ansible/roles/docker-compose/.gitignore new file mode 100644 index 0000000..089c44d --- /dev/null +++ b/ansible/roles/docker-compose/.gitignore @@ -0,0 +1,5 @@ +*.retry +.vagrant +tests/_roles +!tests/_roles/.gitkeep +.DS_Store diff --git a/ansible/roles/docker-compose/.travis.yml b/ansible/roles/docker-compose/.travis.yml new file mode 100644 index 0000000..1e21436 --- /dev/null +++ b/ansible/roles/docker-compose/.travis.yml @@ -0,0 +1,24 @@ +--- +language: python +python: "2.7" + +sudo: required +dist: trusty + +addons: + apt: + sources: + - sourceline: ppa:ansible/ansible + packages: + - ansible + +before_install: cd tests + +install: +- ansible-galaxy install -r roles.yml + +script: +- ansible-playbook -i localhost test.yml + +notifications: + webhooks: https://galaxy.ansible.com/api/v1/notifications/ diff --git a/ansible/roles/docker-compose/LICENSE b/ansible/roles/docker-compose/LICENSE new file mode 100644 index 0000000..a962b15 --- /dev/null +++ b/ansible/roles/docker-compose/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2016 Suzuki Shunsuke + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/ansible/roles/docker-compose/README.md b/ansible/roles/docker-compose/README.md new file mode 100644 index 0000000..133ffc7 --- /dev/null +++ b/ansible/roles/docker-compose/README.md @@ -0,0 +1,39 @@ +docker-compose +=============== + +[![Build Status](https://travis-ci.org/suzuki-shunsuke/ansible-docker-compose.svg?branch=master)](https://travis-ci.org/suzuki-shunsuke/ansible-docker-compose) + +Install Docker Compose. + +https://galaxy.ansible.com/suzuki-shunsuke/docker-compose/ + +Requirements +------------ + +* Docker Engine + +Role Variables +-------------- + +* docker_compose_path: the path where docker-compose is installed. The default is /usr/local/bin +* docker_compose_mode: the permission of the docker-compose. The default is 0755 +* docker_compose_version: docker-compose version. The default is `1.11.2` + +Dependencies +------------ + +Nothing. + +Example Playbook +---------------- + +```yaml +- hosts: servers + roles: + - role: suzuki-shunsuke.docker-compose +``` + +License +------- + +MIT diff --git a/ansible/roles/docker-compose/defaults/main.yml b/ansible/roles/docker-compose/defaults/main.yml new file mode 100644 index 0000000..b3c43bf --- /dev/null +++ b/ansible/roles/docker-compose/defaults/main.yml @@ -0,0 +1,5 @@ +--- +# defaults file for docker-compose +docker_compose_path: /usr/local/bin +docker_compose_mode: 0755 +docker_compose_version: 1.11.2 diff --git a/ansible/roles/docker-compose/meta/.galaxy_install_info b/ansible/roles/docker-compose/meta/.galaxy_install_info new file mode 100644 index 0000000..7beff61 --- /dev/null +++ b/ansible/roles/docker-compose/meta/.galaxy_install_info @@ -0,0 +1 @@ +{install_date: 'Mon Apr 24 12:06:46 2017', version: 1.2.0} diff --git a/ansible/roles/docker-compose/meta/main.yml b/ansible/roles/docker-compose/meta/main.yml new file mode 100644 index 0000000..0a6d588 --- /dev/null +++ b/ansible/roles/docker-compose/meta/main.yml @@ -0,0 +1,15 @@ +galaxy_info: + author: Suzuki Shunsuke + description: Install Docker Compose + license: MIT + min_ansible_version: 1.2 + github_branch: master + platforms: + - name: GenericUnix + versions: + - all + galaxy_tags: + - docker + - docker compose + +dependencies: [] diff --git a/ansible/roles/docker-compose/tasks/main.yml b/ansible/roles/docker-compose/tasks/main.yml new file mode 100644 index 0000000..2cb7b51 --- /dev/null +++ b/ansible/roles/docker-compose/tasks/main.yml @@ -0,0 +1,7 @@ +--- +# tasks file for docker-compose +- name: Install docker-compose + get_url: + url: https://github.com/docker/compose/releases/download/{{docker_compose_version}}/docker-compose-{{ansible_system}}-{{ansible_architecture}} + dest: "{{'{}/docker-compose'.format(docker_compose_path)}}" + mode: "{{docker_compose_mode}}" diff --git a/ansible/roles/docker-compose/tests/Vagrantfile b/ansible/roles/docker-compose/tests/Vagrantfile new file mode 100644 index 0000000..99d36be --- /dev/null +++ b/ansible/roles/docker-compose/tests/Vagrantfile @@ -0,0 +1,12 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : + +Vagrant.configure(2) do |config| + config.vm.box = "bento/ubuntu-16.04" + config.vm.provider "virtualbox" do |vb| + vb.memory = "2048" + end + config.vm.provision "ansible" do |ansible| + ansible.playbook = "./test.yml" + end +end diff --git a/ansible/roles/docker-compose/tests/ansible.cfg b/ansible/roles/docker-compose/tests/ansible.cfg new file mode 100644 index 0000000..fe431f6 --- /dev/null +++ b/ansible/roles/docker-compose/tests/ansible.cfg @@ -0,0 +1,6 @@ +[defaults] +roles_path = ./_roles:../../ + +[ssh_connection] +ssh_args = -o ControlPersist=1800s -o ControlMaster=auto +pipelining = True diff --git a/ansible/roles/docker-compose/tests/localhost b/ansible/roles/docker-compose/tests/localhost new file mode 100644 index 0000000..3c11959 --- /dev/null +++ b/ansible/roles/docker-compose/tests/localhost @@ -0,0 +1,2 @@ +[default] +localhost ansible_connection=local diff --git a/ansible/roles/docker-compose/tests/roles.yml b/ansible/roles/docker-compose/tests/roles.yml new file mode 100644 index 0000000..167337f --- /dev/null +++ b/ansible/roles/docker-compose/tests/roles.yml @@ -0,0 +1 @@ +- src: suzuki-shunsuke.docker-ubuntu diff --git a/ansible/roles/docker-compose/tests/test.yml b/ansible/roles/docker-compose/tests/test.yml new file mode 100644 index 0000000..9150dd4 --- /dev/null +++ b/ansible/roles/docker-compose/tests/test.yml @@ -0,0 +1,11 @@ +--- +- hosts: default + roles: + - suzuki-shunsuke.docker-ubuntu + - ansible-docker-compose + tasks: + - command: docker-compose --version + register: result + changed_when: false + - debug: + var: result diff --git a/ansible/roles/docker-compose/vars/main.yml b/ansible/roles/docker-compose/vars/main.yml new file mode 100644 index 0000000..6349b1c --- /dev/null +++ b/ansible/roles/docker-compose/vars/main.yml @@ -0,0 +1,3 @@ +--- +# vars file for docker-compose +docker_compose_nonroot: "{{ (ansible_env.HOME == '/root') | ternary('no', 'yes') }}" diff --git a/ansible/roles/docker-ubuntu/.gitignore b/ansible/roles/docker-ubuntu/.gitignore new file mode 100644 index 0000000..4a96db2 --- /dev/null +++ b/ansible/roles/docker-ubuntu/.gitignore @@ -0,0 +1,2 @@ +*.retry +.vagrant diff --git a/ansible/roles/docker-ubuntu/.travis.yml b/ansible/roles/docker-ubuntu/.travis.yml new file mode 100644 index 0000000..90b757f --- /dev/null +++ b/ansible/roles/docker-ubuntu/.travis.yml @@ -0,0 +1,23 @@ +--- +language: python +python: "2.7" + +sudo: required +dist: trusty + +addons: + apt: + sources: + - sourceline: ppa:ansible/ansible + packages: + - ansible + +before_script: +- ansible --version +- cd tests + +script: +- ansible-playbook -i inventory-local test.yml + +notifications: + webhooks: https://galaxy.ansible.com/api/v1/notifications/ diff --git a/ansible/roles/docker-ubuntu/README.md b/ansible/roles/docker-ubuntu/README.md new file mode 100644 index 0000000..f90078c --- /dev/null +++ b/ansible/roles/docker-ubuntu/README.md @@ -0,0 +1,42 @@ +docker-ubuntu +=============== + +[![Build Status](https://travis-ci.org/suzuki-shunsuke/ansible-docker-ubuntu.svg?branch=master)](https://travis-ci.org/suzuki-shunsuke/ansible-docker-ubuntu) + +Install docker on Ubuntu. + +(With modifications by @loleg) + +https://galaxy.ansible.com/suzuki-shunsuke/docker-ubuntu/ + +Requirements +------------ + +Nothing. + +Role Variables +-------------- + +* docker_nonroot: Whether the remote_user is root or not. This variable is set automatically, and is used to execute tasks with the become option. +* docker_users: Users who are added the docker group. The default value is an empty array. + +Dependencies +------------ + +Nothing. + +Example Playbook +---------------- + +```yaml +- hosts: servers + roles: + - role: suzuki-shunsuke.docker-ubuntu + docker_users: + - ubuntu +``` + +License +------- + +MIT diff --git a/ansible/roles/docker-ubuntu/defaults/main.yml b/ansible/roles/docker-ubuntu/defaults/main.yml new file mode 100644 index 0000000..efb98ae --- /dev/null +++ b/ansible/roles/docker-ubuntu/defaults/main.yml @@ -0,0 +1,3 @@ +--- +# defaults file for docker-ubuntu +docker_users: [] diff --git a/ansible/roles/docker-ubuntu/handlers/main.yml b/ansible/roles/docker-ubuntu/handlers/main.yml new file mode 100644 index 0000000..d4917f2 --- /dev/null +++ b/ansible/roles/docker-ubuntu/handlers/main.yml @@ -0,0 +1,2 @@ +--- +# handlers file for docker-ubuntu diff --git a/ansible/roles/docker-ubuntu/meta/.galaxy_install_info b/ansible/roles/docker-ubuntu/meta/.galaxy_install_info new file mode 100644 index 0000000..62464f8 --- /dev/null +++ b/ansible/roles/docker-ubuntu/meta/.galaxy_install_info @@ -0,0 +1 @@ +{install_date: 'Sun Apr 23 07:58:36 2017', version: 1.0.4} diff --git a/ansible/roles/docker-ubuntu/meta/main.yml b/ansible/roles/docker-ubuntu/meta/main.yml new file mode 100644 index 0000000..456f946 --- /dev/null +++ b/ansible/roles/docker-ubuntu/meta/main.yml @@ -0,0 +1,16 @@ +galaxy_info: + author: Suzuki Shunsuke + description: Install docker on Ubuntu + license: MIT + min_ansible_version: 1.2 + github_branch: master + + platforms: + - name: Ubuntu + versions: + - all + + galaxy_tags: + - docker + +dependencies: [] diff --git a/ansible/roles/docker-ubuntu/tasks/main.yml b/ansible/roles/docker-ubuntu/tasks/main.yml new file mode 100644 index 0000000..2658ae8 --- /dev/null +++ b/ansible/roles/docker-ubuntu/tasks/main.yml @@ -0,0 +1,42 @@ +--- +# tasks file for docker-ubuntu +- name: Install apt-transport-https, ca-certificates + apt: + name: "{{item}}" + update_cache: no + install_recommends: no + with_items: + - apt-transport-https + - ca-certificates + +- name: Add the new GPG key + apt_key: + keyserver: hkp://p80.pool.sks-keyservers.net:80 + id: 58118E89F3A912897C070ADBF76221572C52609D + +- name: Template a sources.list + template: + src: docker.list + dest: /etc/apt/sources.list.d/docker.list + owner: root + group: root + +- name: Install the recommended virtual package + apt: + name: linux-image-extra-virtual + update_cache: no + install_recommends: yes + +- name: Install docker-engine + apt: + name: docker-engine + update_cache: no + install_recommends: yes + +- name: Add users to the docker group + user: + name: "{{item}}" + groups: docker + append: yes + + with_items: "{{docker_users}}" diff --git a/ansible/roles/docker-ubuntu/templates/docker.list b/ansible/roles/docker-ubuntu/templates/docker.list new file mode 100644 index 0000000..f2be97e --- /dev/null +++ b/ansible/roles/docker-ubuntu/templates/docker.list @@ -0,0 +1 @@ +deb https://apt.dockerproject.org/repo ubuntu-{{ ansible_distribution_release }} main diff --git a/ansible/roles/docker-ubuntu/tests/Vagrantfile b/ansible/roles/docker-ubuntu/tests/Vagrantfile new file mode 100644 index 0000000..99d36be --- /dev/null +++ b/ansible/roles/docker-ubuntu/tests/Vagrantfile @@ -0,0 +1,12 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : + +Vagrant.configure(2) do |config| + config.vm.box = "bento/ubuntu-16.04" + config.vm.provider "virtualbox" do |vb| + vb.memory = "2048" + end + config.vm.provision "ansible" do |ansible| + ansible.playbook = "./test.yml" + end +end diff --git a/ansible/roles/docker-ubuntu/tests/ansible.cfg b/ansible/roles/docker-ubuntu/tests/ansible.cfg new file mode 100644 index 0000000..ac09f6c --- /dev/null +++ b/ansible/roles/docker-ubuntu/tests/ansible.cfg @@ -0,0 +1,2 @@ +[defaults] +roles_path = ../../ diff --git a/ansible/roles/docker-ubuntu/tests/inventory-local b/ansible/roles/docker-ubuntu/tests/inventory-local new file mode 100644 index 0000000..3c11959 --- /dev/null +++ b/ansible/roles/docker-ubuntu/tests/inventory-local @@ -0,0 +1,2 @@ +[default] +localhost ansible_connection=local diff --git a/ansible/roles/docker-ubuntu/tests/test.yml b/ansible/roles/docker-ubuntu/tests/test.yml new file mode 100644 index 0000000..3ea6ad5 --- /dev/null +++ b/ansible/roles/docker-ubuntu/tests/test.yml @@ -0,0 +1,11 @@ +--- +- hosts: default + roles: + - ansible-docker-ubuntu + tasks: + - name: Check docker version + command: docker --version + register: result + changed_when: false + - debug: + var: result.stdout diff --git a/ansible/roles/docker-ubuntu/vars/main.yml b/ansible/roles/docker-ubuntu/vars/main.yml new file mode 100644 index 0000000..0cdbe85 --- /dev/null +++ b/ansible/roles/docker-ubuntu/vars/main.yml @@ -0,0 +1,3 @@ +--- +# vars file for docker-ubuntu +docker_nonroot: "{{ (ansible_env.HOME == '/root') | ternary('no', 'yes') }}" diff --git a/ansible/roles/web/handlers/main.yml b/ansible/roles/web/handlers/main.yml new file mode 100644 index 0000000..32dcc07 --- /dev/null +++ b/ansible/roles/web/handlers/main.yml @@ -0,0 +1,11 @@ +--- + +- name: restart nginx + service: name=nginx state=restarted + +- name: validate nginx + command: nginx -t -c /etc/nginx/nginx.conf + changed_when: False + +- name: reload nginx + service: name=nginx state=reloaded diff --git a/ansible/roles/web/meta/main.yaml b/ansible/roles/web/meta/main.yaml new file mode 100644 index 0000000..cc5a976 --- /dev/null +++ b/ansible/roles/web/meta/main.yaml @@ -0,0 +1,19 @@ +--- + +galaxy_info: + author: Oleg Lavrovsky + description: Install Wagtail on Ubuntu with docker-compose + license: MIT + min_ansible_version: 1.2 + github_branch: master + + platforms: + - name: Ubuntu + versions: + - all + + galaxy_tags: + - docker + - wagtail + +dependencies: [] diff --git a/ansible/roles/web/tasks/install.yaml b/ansible/roles/web/tasks/install.yaml new file mode 100644 index 0000000..e0ebf94 --- /dev/null +++ b/ansible/roles/web/tasks/install.yaml @@ -0,0 +1,17 @@ +--- + +- name: Create release directory + file: path={{ release_dir }} state=directory + +- name: Checkout code branch from git + git: + repo: 'https://github.com/datalets/public-health-ch' + dest: "{{ release_dir }}" + +- name: Copy Docker site configuration + template: + src: docker-compose.j2 + dest: "{{ release_dir }}/docker-compose.yml" + +- name: Deploy Docker site + shell: docker-compose build diff --git a/ansible/roles/web/tasks/main.yaml b/ansible/roles/web/tasks/main.yaml new file mode 100644 index 0000000..e6ef8cb --- /dev/null +++ b/ansible/roles/web/tasks/main.yaml @@ -0,0 +1,19 @@ +--- + +- include: nginx.yaml + tags: + - nginx + - setup + +- include: install.yaml + tags: + - install + - docker + - setup + +- include: manage.yaml + tags: + - manage + - docker + - setup + - run diff --git a/ansible/roles/web/tasks/manage.yaml b/ansible/roles/web/tasks/manage.yaml new file mode 100644 index 0000000..2444a1d --- /dev/null +++ b/ansible/roles/web/tasks/manage.yaml @@ -0,0 +1,4 @@ +--- + +- name: Start Docker site + shell: docker-compose up diff --git a/ansible/roles/web/tasks/nginx.yaml b/ansible/roles/web/tasks/nginx.yaml new file mode 100644 index 0000000..d2d61bd --- /dev/null +++ b/ansible/roles/web/tasks/nginx.yaml @@ -0,0 +1,19 @@ +--- + +- name: Test and reload Nginx + service: name=nginx state=reloaded + +# Note that changing the domain leaves the old config in place +- name: Copy Nginx site config + template: + src: nginx.conf.j2 + dest: /etc/nginx/sites-available/{{ domain }} + +- name: Activate Nginx site config + file: + state: link + src: /etc/nginx/sites-available/{{ domain }} + path: /etc/nginx/sites-enabled/{{ domain }} + notify: + - validate nginx + - reload nginx diff --git a/ansible/roles/web/templates/docker-compose.j2 b/ansible/roles/web/templates/docker-compose.j2 new file mode 100644 index 0000000..914b200 --- /dev/null +++ b/ansible/roles/web/templates/docker-compose.j2 @@ -0,0 +1,36 @@ +web: + build: . + dockerfile: .dockerfile + command: gunicorn {{ django_project_name }}.wsgi:application -b 0.0.0.0:5000 -w 5 --log-file=- + volumes: + - ./:/usr/src/app + - {{ django_log_dir }}:/var/log/wagtail + links: + - redis + - postgres + - elasticsearch + ports: + - "5000:5000" + environment: + - DJANGO_SETTINGS_MODULE={{ django_project_name }}.settings.production + - ALLOWED_HOSTS={{ domain }},{{ ipv4_addresses|join(':5000,') }} + - SECRET_KEY={{ django_secret_key }} + - MAILGUN_KEY={{ django_email_key }} + - MAILGUN_DOMAIN={{ django_email_domain }} + - MAILGUN_FROM={{ django_email_from }} + - DATABASE_URL=postgres://postgres:@postgres:5432/postgres + - ELASTICSEARCH_URL=http://elasticsearch:9200 + - REDIS_URL=redis://redis:6379 + - LOG_DIR=/var/log/wagtail +redis: + image: redis + ports: + - "6379:6379" +postgres: + image: postgres + ports: + - "5432:5432" +elasticsearch: + image: orchardup/elasticsearch + ports: + - "9200:9200" diff --git a/ansible/roles/web/templates/nginx.conf.j2 b/ansible/roles/web/templates/nginx.conf.j2 new file mode 100644 index 0000000..2882c0c --- /dev/null +++ b/ansible/roles/web/templates/nginx.conf.j2 @@ -0,0 +1,70 @@ +#{{ ansible_managed }} + +upstream wagtail-site { + server localhost:5000; +} +server { + listen [::]:80; + listen 80; + server_name {{ domain }} www.{{ domain }}; + + client_max_body_size 16M; + + gzip on; + gzip_types text/plain text/css application/x-javascript image/svg+xml; + gzip_comp_level 1; + gzip_disable msie6; + gzip_http_version 1.0; + gzip_proxied any; + gzip_vary on; + + location /static/ { + access_log off; + expires 3600; + alias {{ release_dir }}/static/; + add_header Cache-Control "public"; + add_header Access-Control-Allow-Origin https://{{ domain }}; + } + + # Set a longer expiry for CACHE/, because the filenames are unique. + location /static/CACHE/ { + access_log off; + expires 864000; + alias {{ release_dir }}/static/CACHE/; + } + + # Only serve /media/images by default, not e.g. original_images/. + location /media/images { + alias {{ release_dir }}/media/images; + access_log off; + expires max; + add_header Cache-Control "public"; + } + + location / { + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Host $http_host; + proxy_redirect off; + proxy_pass http://wagtail-site; + } +} + +# TODO: parameterize +server { + listen 80; listen 443 ssl; + server_name conference.public-health.ch; + location /fr { + return 301 $scheme://sph17.organizers-congress.org/frontend/index.php?sub=89; + } + location / { + return 301 $scheme://sph17.organizers-congress.org; + } +} + +# TODO: parameterize +server { + listen 80; listen 443 ssl; + server_name sphc.ch; + return 301 $scheme://sph17.organizers-congress.org; +} diff --git a/ansible/site.yaml b/ansible/site.yaml new file mode 100644 index 0000000..90860b5 --- /dev/null +++ b/ansible/site.yaml @@ -0,0 +1,12 @@ +- hosts: webservers + become: True + gather_facts: True + vars: + release_dir: /opt/publichealth + django_log_dir: /var/log/publichealth + ipv4_addresses: "{{ ansible_all_ipv4_addresses }}" + environment: + COMPOSE_FILE: "./docker-compose.yml" + COMPOSE_PROJECT_NAME: publichealth + roles: + - web diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index 7040ba5..0000000 --- a/docker-compose.yml +++ /dev/null @@ -1,36 +0,0 @@ -web: - build: . - dockerfile: .dockerfile - command: gunicorn publichealth.wsgi:application -b 0.0.0.0:5000 -w 5 --log-file=- - volumes: - - ./:/usr/src/app - - /var/log/publichealth:/var/log/publichealth - links: - - redis - - postgres - - elasticsearch - ports: - - "5000:5000" - environment: - - SECRET_KEY=${SECRET_KEY} - - ALLOWED_HOSTS=${ALLOWED_HOSTS} - - DJANGO_SETTINGS_MODULE=${DJANGO_SETTINGS_MODULE} - - MAILGUN_KEY=${MAILGUN_KEY} - - MAILGUN_FROM=${MAILGUN_FROM} - - MAILGUN_DOMAIN=${MAILGUN_DOMAIN} - - DATABASE_URL=postgres://postgres:@postgres:5432/postgres - - ELASTICSEARCH_URL=http://elasticsearch:9200 - - REDIS_URL=redis://redis:6379 - - LOG_DIR=/var/log/publichealth -redis: - image: redis - ports: - - "6379:6379" -postgres: - image: postgres - ports: - - "5432:5432" -elasticsearch: - image: orchardup/elasticsearch - ports: - - "9200:9200"