├── .editorconfig ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ └── release.yml ├── .gitignore ├── .kitchen.yml ├── .scrutinizer.yml ├── .yamllint ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Gemfile ├── README.md ├── Rakefile ├── ansible.cfg ├── default.yml ├── defaults └── main.yml ├── group_vars └── windows-servers.yml ├── hosts ├── meta └── main.yml ├── tasks ├── access.yml ├── account.yml ├── audit.yml ├── ie.yml ├── main.yml ├── misc.yml ├── password_policy.yml ├── powershell.yml ├── rdp.yml ├── security_policy.yml └── user_rights.yml ├── templates └── security_policy.inf.yml ├── test └── integration │ └── default │ └── inspec │ ├── controls │ └── tests.rb │ └── inspec.yml └── vagrant_windows_target.rb /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | end_of_line = lf 9 | insert_final_newline = true 10 | 11 | # Matches the exact files either package.json or .travis.yml 12 | [*.yml] 13 | indent_style = space 14 | indent_size = 2 15 | trim_trailing_whitespace = true 16 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | **Describe the bug** 8 | A clear and concise description of what the bug is. 9 | 10 | **Expected behavior** 11 | A clear and concise description of what you expected to happen. 12 | 13 | **Actual behavior** 14 | 15 | ```paste below 16 | 17 | ``` 18 | **Example Playbook** 19 | 20 | ```paste below 21 | 22 | ``` 23 | 24 | **OS / Environment** 25 | 26 | 27 | **Ansible Version** 28 | 29 | ```paste below 30 | 31 | ``` 32 | 33 | **Role Version** 34 | 35 | ```paste below 36 | 37 | ``` 38 | 39 | **Additional context** 40 | Add any other context about the problem here. 41 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | **Is your feature request related to a problem? Please describe.** 8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 9 | 10 | **Describe the solution you'd like** 11 | A clear and concise description of what you want to happen. 12 | 13 | **Describe alternatives you've considered** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: New release 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | generate_changelog: 10 | runs-on: ubuntu-latest 11 | name: create release draft 12 | steps: 13 | - uses: actions/checkout@v1 14 | 15 | - name: 'Get Previous tag' 16 | id: previoustag 17 | uses: "WyriHaximus/github-action-get-previous-tag@master" 18 | env: 19 | GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" 20 | 21 | - name: calculate next version 22 | id: version 23 | uses: patrickjahns/version-drafter-action@v1 24 | env: 25 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 26 | 27 | - name: Generate changelog 28 | uses: charmixer/auto-changelog-action@8095796 29 | with: 30 | token: ${{ secrets.GITHUB_TOKEN }} 31 | future_release: ${{ steps.version.outputs.next-version }} 32 | 33 | - name: Generate changelog for the release 34 | uses: charmixer/auto-changelog-action@8095796 35 | with: 36 | token: ${{ secrets.GITHUB_TOKEN }} 37 | since_tag: ${{ steps.previoustag.outputs.tag }} 38 | future_release: ${{ steps.version.outputs.next-version }} 39 | output: CHANGELOGRELEASE.md 40 | 41 | - name: push changelog 42 | uses: github-actions-x/commit@v2.6 43 | with: 44 | github-token: ${{ secrets.GITHUB_TOKEN }} 45 | push-branch: 'master' 46 | commit-message: 'update changelog' 47 | force-add: 'true' 48 | files: CHANGELOG.md 49 | name: dev-sec CI 50 | email: hello@dev-sec.io 51 | 52 | - name: Read CHANGELOG.md 53 | id: package 54 | uses: juliangruber/read-file-action@v1 55 | with: 56 | path: ./CHANGELOGRELEASE.md 57 | 58 | - name: Create Release draft 59 | id: create_release 60 | uses: actions/create-release@v1 61 | env: 62 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token 63 | with: 64 | release_name: ${{ steps.version.outputs.next-version }} 65 | tag_name: ${{ steps.version.outputs.next-version }} 66 | body: | 67 | ${{ steps.package.outputs.content }} 68 | draft: true 69 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | kitchen/.kitchen 2 | .kitchen 3 | -------------------------------------------------------------------------------- /.kitchen.yml: -------------------------------------------------------------------------------- 1 | --- 2 | driver: 3 | name: vagrant 4 | gui: false 5 | linked_clone: true 6 | 7 | platforms: 8 | - name: ansibleserver 9 | driver_plugin: docker 10 | driver_config: 11 | image: rndmh3ro/docker-centos7-ansible:latest 12 | platform: centos 13 | network: 14 | - [ 'private_network', { ip: '172.28.128.10' } ] 15 | transport: 16 | max_ssh_sessions: 1 17 | provisioner: 18 | name: ansible_playbook 19 | roles_path: ../ansible-windows-hardening 20 | ansible_connection: winrm 21 | ansible_inventory: hosts 22 | require_windows_support: true 23 | require_chef_for_busser: false 24 | ansible_host_key_checking: false 25 | ansible_diff: true 26 | ansible_verbose: false 27 | ansible_verbosity: 3 28 | playbook: default.yml 29 | verifier: 30 | name: shell 31 | command: exit 0 32 | 33 | - name: windows-2012R2 34 | driver_config: 35 | box: mwrock/Windows2012R2 36 | communicator: winrm 37 | network: 38 | - [ 'private_network', { ip: '172.28.128.11' } ] 39 | # needed to force provision of the vagrantfile 40 | provision: true 41 | # needed to run the ConfigureRemotingForAnsible.ps1 42 | vagrantfiles: 43 | - vagrant_windows_target.rb 44 | transport: 45 | name: winrm 46 | verifier: 47 | name: inspec 48 | provisioner: 49 | name: shell 50 | 51 | - name: windows-2016 52 | driver_config: 53 | box: mwrock/Windows2016 54 | communicator: winrm 55 | network: 56 | - [ 'private_network', { ip: '172.28.128.12' } ] 57 | # needed to force provision of the vagrantfile 58 | provision: true 59 | # needed to run the ConfigureRemotingForAnsible.ps1 60 | vagrantfiles: 61 | - vagrant_windows_target.rb 62 | transport: 63 | name: winrm 64 | verifier: 65 | name: inspec 66 | provisioner: 67 | name: shell 68 | 69 | suites: 70 | - name: default 71 | -------------------------------------------------------------------------------- /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | --- 2 | build: 3 | dependencies: 4 | before: 5 | - "pip2 install ansible-lint yamllint" 6 | environment: 7 | python: "2.7.7" 8 | tests: 9 | override: 10 | - "yamllint $(find . -name '*.yml' | grep -v ./kitchen | grep -v ./.scrutinizer.yml | grep -v ./lib/ | grep -v vault)" 11 | - "ansible-lint -p $(find . -name '*.yml' | grep -v ./kitchen | grep -v ./.scrutinizer.yml | grep -v ./lib/ | grep -v vault) -v" 12 | -------------------------------------------------------------------------------- /.yamllint: -------------------------------------------------------------------------------- 1 | --- 2 | extends: default 3 | 4 | rules: 5 | line-length: disable 6 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased](https://github.com/dev-sec/ansible-windows-hardening/tree/HEAD) 4 | 5 | [Full Changelog](https://github.com/dev-sec/ansible-windows-hardening/compare/f1bf94c4a49d1ed1cc81062cf47b6b6719d48d96...HEAD) 6 | 7 | **Implemented enhancements:** 8 | 9 | - add changelog and release workflow [\#9](https://github.com/dev-sec/ansible-windows-hardening/pull/9) ([rndmh3ro](https://github.com/rndmh3ro)) 10 | 11 | **Fixed bugs:** 12 | 13 | - Removes " escaping [\#5](https://github.com/dev-sec/ansible-windows-hardening/pull/5) ([jmsmkn](https://github.com/jmsmkn)) 14 | 15 | **Closed issues:** 16 | 17 | - Publish to Ansible Galaxy [\#7](https://github.com/dev-sec/ansible-windows-hardening/issues/7) 18 | - Windows 2008 R2 Support [\#4](https://github.com/dev-sec/ansible-windows-hardening/issues/4) 19 | - Update readme to include baselines [\#1](https://github.com/dev-sec/ansible-windows-hardening/issues/1) 20 | 21 | **Merged pull requests:** 22 | 23 | - add task for windows-base-105: Disable SMB1 to Windows Shares [\#2](https://github.com/dev-sec/ansible-windows-hardening/pull/2) ([chrisfowles](https://github.com/chrisfowles)) 24 | 25 | 26 | 27 | \* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)* 28 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributor Guideline 2 | 3 | This document provides an overview of how you can participate in improving this project or extending it. We are grateful for all your help: bug reports and fixes, code contributions, documentation or ideas. Feel free to join, we appreciate your support!! 4 | 5 | ## Communication 6 | 7 | ### GitHub repositories 8 | 9 | Much of the issues, goals and ideas are tracked in the respective projects in GitHub. Please use this channel to report bugs and post ideas. 10 | 11 | ## git and GitHub 12 | 13 | In order to contribute code please: 14 | 15 | 1. Fork the project on GitHub 16 | 2. Clone the project 17 | 3. Add changes (and tests) 18 | 4. Commit and push 19 | 5. Create a merge-request 20 | 21 | To have your code merged, see the expectations listed below. 22 | 23 | You can find a well-written guide [here](https://help.github.com/articles/fork-a-repo). 24 | 25 | Please follow common commit best-practices. Be explicit, have a short summary, a well-written description and references. This is especially important for the merge-request. 26 | 27 | Some great guidelines can be found [here](https://wiki.openstack.org/wiki/GitCommitMessages) and [here](http://robots.thoughtbot.com/5-useful-tips-for-a-better-commit-message). 28 | 29 | 30 | ## Expectations 31 | 32 | ### Don't reinvent the wheel 33 | 34 | This hardening project doesn't intend to reinvent the configuration stack for services. Aim to use official configuration projects first and provide hardening as a layer on top. The goal is remove the need for a user to configure all aspects of services and maintain security configuration. This way, the user can still configure a service using the interface provided by the official project. 35 | 36 | * For Chef refer to the official [opscode community cookbooks](http://community.opscode.com/cookbooks). 37 | * For Puppet head to the [Puppet Forge](https://forge.puppetlabs.com/) and take a node of the Puppet supported modules. 38 | * For Ansible check the [Ansible Module Index](http://docs.ansible.com/list_of_all_modules.html) 39 | 40 | These projects are generally hosted on GitHub as well. 41 | 42 | In some cases, we in fact create the full rollout stack, but this is generally the exception ([os-hardening](https://github.com/TelekomLabs/chef-os-hardening), [nginx-hardening](https://github.com/TelekomLabs/chef-nginx-hardening)). 43 | 44 | 45 | ### Be explicit 46 | 47 | * Please avoid using nonsensical property and variable names. 48 | * Use self-describing attribute names for user configuration. 49 | * In case of failures, communicate what happened and why a failure occurs to the user. Make it easy to track the code or action that produced the error. Try to catch and handle errors if possible to provide improved failure messages. 50 | 51 | 52 | ### Add tests 53 | 54 | The security review of this project is done using integration tests. 55 | 56 | Whenever you add a new security configuration, please start by writing a test that checks for this configuration. For example: If you want to set a new attribute in a configuration file, write a test that expects the value to be set first. Then implement your change. 57 | 58 | You may add a new feature request by creating a test for whatever value you need. 59 | 60 | All tests will be reviewed internally for their validity and overall project direction. 61 | 62 | 63 | ### Document your code 64 | 65 | As code is more often read than written, please provide documentation in all projects. 66 | 67 | Adhere to the respective guidelines for documentation: 68 | 69 | * Chef generally documents code based explicit readme files. For code documentation please use [yard-chef](https://github.com/rightscale/yard-chef) 70 | * [Puppet module documentation](http://docs.puppetlabs.com/puppet/latest/reference/modules_documentation.html) 71 | 72 | 73 | ### Follow coding styles 74 | 75 | We generally include test for coding guidelines: 76 | 77 | * Chef follows [Foodcritic](http://acrmp.github.io/foodcritic/) 78 | * Puppet is checked with [puppet-lint](http://puppet-lint.com/checks/) 79 | * Ansible is checked by running the playbook with the syntax-check option, e.g. `ansible-playbook foo.yml --syntax-check` 80 | 81 | Remember: Code is generally read much more often than written. 82 | 83 | ### Use Markdown 84 | 85 | Wherever possible, please refrain from any other formats and stick to simple markdown. 86 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' do 2 | gem 'kitchen-ansible' 3 | gem 'kitchen-docker' 4 | gem 'kitchen-vagrant' 5 | gem 'kitchen-inspec' 6 | gem 'winrm' 7 | gem 'winrm-fs' 8 | gem 'kitchen-pester' 9 | gem 'vagrant-winrm' 10 | end 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # windows-hardening (Ansible Role) 2 | 3 | 4 | **Attention:** This role has been migrated to our hardening-collection: 5 | * https://github.com/dev-sec/ansible-os-hardening/ 6 | * https://galaxy.ansible.com/devsec/hardening 7 | 8 | Please open any issues and pull requests there! 9 | 10 | ## Requirements 11 | 12 | * Ansible 2.3.0 13 | 14 | ## Variables 15 | 16 | | Name | Default Value | Description | 17 | | ---------------------------------------------- | --------------- | ----------------------------------- | 18 | | `win_security_PasswordComplexity` | `1` | Flag that indicates whether the operating system MUST require that passwords meet complexity requirements. Default: True | 19 | | `win_security_LockoutBadCount` | `4` | Number of failed logon attempts after which a user account MUST be locked out. Default: 4 | 20 | | `win_security_ResetLockoutCount` | `15` | Number of minutes after a failed logon attempt that the account MUST be locked out. Default: 15 minutes| 21 | | `win_security_LockoutDuration` | `15` | The number of minutes that a locked-out account MUST remain locked out before automatically becoming unlocked. Default: 15 minutes | 22 | | `win_security_SeRemoteInteractiveLogonRight` | `*S-1-5-32-544` | Determines which users or groups can access the logon screen of a remote computer through a RDP connection. Default: Administrators | 23 | | `win_security_SeTcbPrivilege` | `*S-1-0-0` | Allows a process to authenticate like a user and thus gain access to the same resources as a user. Default: Nobody | 24 | | `win_security_SeMachineAccountPrivilege` | `*S-1-5-32-544` | Allows the user to add a computer to a specific domain. Default: Administrators | 25 | | `win_security_SeTrustedCredManAccessPrivilege` | `` | Access Credential Manager as a trusted caller policy setting is used by Credential Manager during backup and restore. Default: No One | 26 | | `win_security_SeNetworkLogonRight` | `*S-1-0-0` | Required for an account to log on using the network logon type. Default: Nobody | 27 | 28 | ## Example Playbook 29 | 30 | ``` 31 | - hosts: localhost 32 | roles: 33 | - dev-sec.windows-hardening 34 | ``` 35 | 36 | ## Local Testing 37 | 38 | 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). 39 | 40 | We create multiple hosts - one linux host where Ansible runs on and the Windows hosts. 41 | 42 | Next install test-kitchen: 43 | 44 | ```bash 45 | # Install dependencies 46 | gem install bundler 47 | bundle install 48 | ``` 49 | 50 | Then you can run the playbook and tests: 51 | ``` 52 | # create the ansible and windows hosts 53 | bundle exec kitchen create 54 | 55 | # run ansible playbook on windows host 56 | bundle exec kitchen converge default-ansibleserver 57 | 58 | # verify windows machines 59 | bundle exec kitchen verify windows 60 | 61 | ``` 62 | 63 | ## Contributing 64 | 65 | See [contributor guideline](CONTRIBUTING.md). 66 | 67 | ## License and Author 68 | 69 | * Author:: Sebastian Gumprich 70 | 71 | Licensed under the Apache License, Version 2.0 (the "License"); 72 | you may not use this file except in compliance with the License. 73 | You may obtain a copy of the License at 74 | 75 | http://www.apache.org/licenses/LICENSE-2.0 76 | 77 | Unless required by applicable law or agreed to in writing, software 78 | distributed under the License is distributed on an "AS IS" BASIS, 79 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 80 | See the License for the specific language governing permissions and 81 | limitations under the License. 82 | 83 | 84 | [1]: http://travis-ci.org/dev-sec/ansible-os-hardening 85 | [2]: https://gitter.im/dev-sec/general 86 | [3]: https://galaxy.ansible.com/dev-sec/os-hardening 87 | 88 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env rake 2 | # encoding: utf-8 3 | 4 | # Automatically generate a changelog for this project. Only loaded if 5 | # the necessary gem is installed. 6 | begin 7 | require 'github_changelog_generator/task' 8 | GitHubChangelogGenerator::RakeTask.new :changelog 9 | rescue LoadError 10 | puts '>>>>> GitHub Changelog Generator not loaded, omitting tasks' 11 | end 12 | -------------------------------------------------------------------------------- /ansible.cfg: -------------------------------------------------------------------------------- 1 | # config file for ansible -- http://ansible.com/ 2 | # ============================================== 3 | 4 | # nearly all parameters can be overridden in ansible-playbook 5 | # or with command line flags. ansible will read ANSIBLE_CONFIG, 6 | # ansible.cfg in the current working directory, .ansible.cfg in 7 | # the home directory or /etc/ansible/ansible.cfg, whichever it 8 | # finds first 9 | 10 | [defaults] 11 | ansible_managed = Ansible managed: {file} modified on %Y-%m-%d by {uid} on {host} 12 | 13 | # additional paths to search for roles in, colon separated 14 | roles_path = ../ 15 | 16 | -------------------------------------------------------------------------------- /default.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: kitchen_test_role 3 | hosts: windows-servers 4 | strategy: free 5 | roles: 6 | - ansible-windows-hardening 7 | vars: 8 | win_security_SeRemoteInteractiveLogonRight: '*S-1-1-0, *S-1-5-32-544, *S-1-5-32-545, *S-1-5-32-551' 9 | win_security_SeTcbPrivilege: '*S-1-0-0' 10 | win_security_SeMachineAccountPrivilege: '*S-1-5-32-544' 11 | win_security_SeTrustedCredManAccessPrivilege: '*S-1-0-0' 12 | win_security_SeNetworkLogonRight: '*S-1-1-0, *S-1-5-32-544, *S-1-5-32-545, *S-1-5-32-551' 13 | -------------------------------------------------------------------------------- /defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | win_security_policy_template_location: 'C:\Windows\security\templates' 3 | win_security_policy_database_location: 'C:\Windows\security\database' 4 | win_security_policy_log_location: 'C:\Windows\security\logs' 5 | win_security_policy_database_name: 'hardening.sdb' 6 | 7 | # System access settings 8 | win_security_PasswordComplexity: 1 9 | win_security_LockoutBadCount: 4 10 | win_security_ResetLockoutCount: 15 11 | win_security_LockoutDuration: 15 12 | 13 | # Security policy rights / privileges settings. 14 | win_security_SeRemoteInteractiveLogonRight: '*S-1-5-32-544' 15 | win_security_SeTcbPrivilege: '*S-1-0-0' 16 | win_security_SeMachineAccountPrivilege: '*S-1-5-32-544' 17 | win_security_SeTrustedCredManAccessPrivilege: '' 18 | win_security_SeNetworkLogonRight: '*S-1-0-0' 19 | -------------------------------------------------------------------------------- /group_vars/windows-servers.yml: -------------------------------------------------------------------------------- 1 | ansible_user: Administrator 2 | ansible_password: vagrant 3 | ansible_port: 5986 4 | ansible_connection: winrm 5 | ansible_winrm_transport: ssl 6 | ansible_winrm_server_cert_validation: ignore 7 | -------------------------------------------------------------------------------- /hosts: -------------------------------------------------------------------------------- 1 | [windows-servers] 2 | 172.28.128.11 3 | 172.28.128.12 4 | -------------------------------------------------------------------------------- /meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | galaxy_info: 3 | author: "Sebastian Gumprich" 4 | description: 'This Ansible role provides numerous security-related configurations, providing all-round base protection.' 5 | company: Hardening Framework Team 6 | license: Apache License 2.0 7 | min_ansible_version: '2.2.1' 8 | platforms: 9 | - name: EL 10 | versions: 11 | - 6 12 | - 7 13 | - name: Ubuntu 14 | versions: 15 | - precise 16 | - trusty 17 | - xenial 18 | - name: Debian 19 | versions: 20 | - wheezy 21 | - jessie 22 | galaxy_tags: 23 | - system 24 | - security 25 | - hardening 26 | dependencies: [] 27 | -------------------------------------------------------------------------------- /tasks/access.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Verify the Windows folder permissions are properly set | windows-base-100 3 | win_file: 4 | path: C:\windows 5 | state: directory 6 | 7 | - name: Safe DLL Search Mode is Enabled | windows-base-101 8 | win_regedit: 9 | path: HKLM:\System\CurrentControlSet\Control\Session Manager 10 | name: "SafeDllSearchMode" 11 | data: "0" 12 | type: dword 13 | 14 | - name: Anonymous Access to Windows Shares and Named Pipes is Disallowed | windows-base-102 15 | win_regedit: 16 | path: HKLM:\System\CurrentControlSet\Services\LanManServer\Parameters 17 | name: "RestrictNullSessAccess" 18 | data: "1" 19 | type: dword 20 | 21 | - name: All Shares are Configured to Prevent Anonymous Access | windows-base-103 22 | win_regedit: 23 | path: HKLM:\System\CurrentControlSet\Services\LanManServer\Parameters 24 | name: "NullSessionShares" 25 | data: "" 26 | type: multistring 27 | 28 | - name: Force Encrypted Windows Network Passwords | windows-base-104 29 | win_regedit: 30 | path: HKLM:\System\CurrentControlSet\Services\LanmanWorkstation\Parameters 31 | name: "EnablePlainTextPassword" 32 | data: "0" 33 | type: dword 34 | 35 | - name: Disable SMB1 to Windows Shares | windows-base-105 36 | win_regedit: 37 | path: HKLM:\System\CurrentControlSet\Services\LanManServer\Parameters 38 | name: "SMB1" 39 | data: "0" 40 | type: dword 41 | 42 | - name: Strong Windows NTLMv2 Authentication Enabled; Weak LM Disabled | windows-base-201 43 | win_regedit: 44 | path: HKLM:\System\CurrentControlSet\Control\Lsa 45 | name: "LmCompatibilityLevel" 46 | data: "4" 47 | type: dword 48 | 49 | - name: Enable Strong Encryption for Windows Network Sessions on Clients | windows-base-202 50 | win_regedit: 51 | path: HKLM:\System\CurrentControlSet\Control\Lsa\MSV1_0 52 | name: "NtlmMinClientSec" 53 | data: "537395200" 54 | type: dword 55 | 56 | - name: Enable Strong Encryption for Windows Network Sessions on Servers | windows-base-203 57 | win_regedit: 58 | path: HKLM:\System\CurrentControlSet\Control\Lsa\MSV1_0 59 | name: "NtlmMinServerSec" 60 | data: "537395200" 61 | type: dword 62 | -------------------------------------------------------------------------------- /tasks/account.yml: -------------------------------------------------------------------------------- 1 | --- 2 | #- name: Windows Default Guest Account is Disabled | windows-account-101 3 | # Security Policy EnableGuestAccount should eq 0 4 | 5 | #- name: Windows Password Complexity is Enabled | windows-account-102 6 | # Security Policy PasswordComplexity should eq 1 7 | 8 | #- name: Minimum Windows Password Length Configured to be at Least 8 Characters | windows-account-103 9 | # Security Policy MinimumPasswordLength should not eq 0 10 | 11 | #- name: Set Windows Account lockout threshold | windows-account-104 12 | # Security Policy LockoutBadCount should not eq 0 13 | 14 | #- name: Windows Account Lockout Counter Configured to Wait at Least 30 Minutes Before Reset | windows-account-105 15 | # Security Policy ResetLockoutCount should not eq 0 16 | 17 | #- name: Windows Account Lockout Duration Configured to at Least 30 Minutes | windows-account-106 18 | # Security Policy LockoutDuration should not eq 0 19 | -------------------------------------------------------------------------------- /tasks/audit.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Configure System Event Log (Application) | windows-audit-100 3 | win_regedit: 4 | path: HKLM:\Software\Policies\Microsoft\Windows\EventLog\Application 5 | name: "MaxSize" 6 | data: "1" 7 | type: dword 8 | 9 | - name: Configure System Event Log (Security) | windows-audit-101 10 | win_regedit: 11 | path: HKLM:\Software\Policies\Microsoft\Windows\EventLog\Security 12 | name: "MaxSize" 13 | data: "1" 14 | type: dword 15 | 16 | - name: Configure System Event Log (Setup) | windows-audit-102 17 | win_regedit: 18 | path: HKLM:\Software\Policies\Microsoft\Windows\EventLog\Setup 19 | name: "MaxSize" 20 | data: "1" 21 | type: dword 22 | 23 | - name: Configure System Event Log (System) | windows-audit-103 24 | win_regedit: 25 | path: HKLM:\Software\Policies\Microsoft\Windows\EventLog\System 26 | name: "MaxSize" 27 | data: "1" 28 | type: dword 29 | 30 | - name: Account Logon Audit Log | windows-audit-203 31 | win_command: AuditPol /Set /Category:"Account Logon" /Failure:Enable /Success:Enable 32 | register: accountLogonAudit 33 | args: 34 | creates: C:\accountLogonAudit.lock 35 | 36 | - name: Create accountLogonAudit.lock if account audit log was activated 37 | win_copy: 38 | dest: C:\accountLogonAudit.lock 39 | content: "" 40 | force: no 41 | when: accountLogonAudit 42 | 43 | - name: Audit Application Group Management | windows-audit-204 44 | win_command: AuditPol /Set /SubCategory:"Application Group Management" /Failure:Enable /Success:Enable 45 | register: appGroupMngmtAudit 46 | args: 47 | creates: C:\appGroupMngmtAudit.lock 48 | 49 | - name: Create appGroupMngmtAudit.lock if group manangement audit log was activated 50 | win_copy: 51 | dest: C:\appGroupMngmtAudit.lock 52 | content: "" 53 | force: no 54 | when: appGroupMngmtAudit 55 | 56 | - name: Audit Computer Account Management | windows-audit-205 57 | win_command: AuditPol /Set /SubCategory:"Computer Account Management" /Failure:Enable /Success:Enable 58 | register: appAccountMngmtAudit 59 | args: 60 | creates: C:\appAccountMngmtAudit.lock 61 | 62 | - name: Create appAccountMngmtAudit.lock if computer account manangement audit log was activated 63 | win_copy: 64 | dest: C:\appAccountMngmtAudit.lock 65 | content: "" 66 | force: no 67 | when: appAccountMngmtAudit 68 | 69 | 70 | - name: Audit Application Group Management | windows-audit-206 71 | win_command: AuditPol /Set /SubCategory:"Distribution Group Management" /Failure:Enable /Success:Enable 72 | register: distGroupMngmtAudit 73 | args: 74 | creates: C:\distGroupMngmtAudit.lock 75 | 76 | - name: Create distGroupMngmtAudit.lock if dist group manangement audit log was activated 77 | win_copy: 78 | dest: C:\distGroupMngmtAudit.lock 79 | content: "" 80 | force: no 81 | when: distGroupMngmtAudit 82 | -------------------------------------------------------------------------------- /tasks/ie.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: IE 64-bit tab | windows-ie-101 3 | win_regedit: 4 | path: HKLM:\Software\Policies\Microsoft\Internet Explorer\Main 5 | name: "Isolation64Bit" 6 | data: "1" 7 | type: dword 8 | 9 | - name: Run antimalware programs against ActiveX controls | windows-ie-102 10 | win_regedit: 11 | path: HKLM:\Software\Policies\Microsoft\Windows\CurrentVersion\Internet Settings\Zones\3 12 | name: "270C" 13 | data: "0" 14 | type: dword 15 | -------------------------------------------------------------------------------- /tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - include: access.yml 3 | tags: access 4 | 5 | - include: account.yml 6 | tags: account 7 | 8 | - include: audit.yml 9 | tags: audit 10 | 11 | - include: ie.yml 12 | tags: ie 13 | 14 | - include: misc.yml 15 | tags: misc 16 | 17 | - include: password_policy.yml 18 | tags: password_policy 19 | 20 | - include: powershell.yml 21 | tags: powershell 22 | 23 | - include: rdp.yml 24 | tags: rdp 25 | 26 | - include: security_policy.yml 27 | tags: security_policy 28 | 29 | - include: user_rights.yml 30 | tags: user_rights 31 | -------------------------------------------------------------------------------- /tasks/misc.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Microsoft Online Accounts | microsoft-online-accounts 3 | win_regedit: 4 | path: HKLM:\SOFTWARE\Microsoft\PolicyManager\default\Settings\AllowYourAccount 5 | name: "value" 6 | data: "0" 7 | type: dword 8 | 9 | - name: Disable Windows Store | disable-windows-store 10 | win_regedit: 11 | path: HKLM:\SOFTWARE\Policies\Microsoft\WindowsStore 12 | name: "AutoDownload" 13 | data: "4" 14 | type: dword 15 | 16 | - name: Ensure Turn off Automatic Download and Install of Updates is set to Disabled 17 | win_regedit: 18 | path: HKLM:\SOFTWARE\Policies\Microsoft\WindowsStore 19 | name: "DisableOSUpgrade" 20 | data: "1" 21 | type: dword 22 | 23 | - name: Disable indexing encrypted files | disable-index-encrypted-files 24 | win_regedit: 25 | path: HKLM:\SOFTWARE\Policies\Microsoft\Windows\Windows Search 26 | name: "AllowIndexingEncryptedStoresOrItems" 27 | data: "0" 28 | type: dword 29 | -------------------------------------------------------------------------------- /tasks/password_policy.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Set Enforce password history to 24 or more passwords | cis enforce-password-history 1.1.1 3 | win_command: net accounts /uniquepw:24 4 | register: passHistory 5 | args: 6 | creates: C:\passHistory.lock 7 | 8 | - name: Create passHistory.lock if password history is enforced 9 | win_copy: 10 | dest: C:\passHistory.lock 11 | content: "" 12 | force: no 13 | when: passHistory 14 | 15 | - name: Set Maximum password age to 60 or more days | cis maximum-password-age 1.1.2 16 | win_command: net accounts /maxpwage:60 17 | register: maxPassAge 18 | args: 19 | creates: C:\maxPassAge.lock 20 | 21 | - name: Create maxPassAge.lock if password history is enforced 22 | win_copy: 23 | dest: C:\maxPassAge.lock 24 | content: "" 25 | force: no 26 | when: maxPassAge 27 | 28 | - name: Set Minimum password age to 1 or more days | cis minimum-password-age 1.1.3 29 | win_command: net accounts /minpwage:1 30 | register: minPassAge 31 | args: 32 | creates: C:\minPassAge.lock 33 | 34 | - name: Create minPassAge.lock if password history is enforced 35 | win_copy: 36 | dest: C:\minPassAge.lock 37 | content: "" 38 | force: no 39 | when: minPassAge 40 | 41 | - name: Set Minimum password length to 14 or more characters | cis minimum-password-length 1.1.4 42 | win_command: net accounts /minpwlen:14 43 | register: minPassLength 44 | args: 45 | creates: C:\minPassLength.lock 46 | 47 | - name: Create minPassLength.lock if password history is enforced 48 | win_copy: 49 | dest: C:\minPassLength.lock 50 | content: "" 51 | force: no 52 | when: minPassLength 53 | -------------------------------------------------------------------------------- /tasks/powershell.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Enabling PowerShell script block logging will record detailed information from the processing of PowerShell commands and scripts 3 | - name: Powershell ScriptBlock Logging | powershell-script-blocklogging 4 | win_regedit: 5 | path: HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging 6 | name: "EnableScriptBlockLogging" 7 | data: "0" 8 | type: dword 9 | 10 | # Transcription creates a unique record of every PowerShell session, including all input and output, exactly as it appears in the session. 11 | - name: Powershell Transcription | powershell-transcription 12 | win_regedit: 13 | path: HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription 14 | name: "EnableTranscripting" 15 | data: "0" 16 | type: dword 17 | -------------------------------------------------------------------------------- /tasks/rdp.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Windows Remote Desktop Configured to Always Prompt for Password | windows-rdp-100 3 | win_regedit: 4 | path: HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services 5 | name: "fPromptForPassword" 6 | data: "1" 7 | type: dword 8 | 9 | - name: Strong Encryption for Windows Remote Desktop Required | windows-rdp-101 10 | win_regedit: 11 | path: HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services 12 | name: "MinEncryptionLevel" 13 | data: "3" 14 | type: dword 15 | -------------------------------------------------------------------------------- /tasks/security_policy.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: create security policy directories 3 | win_file: 4 | path: "{{item}}" 5 | state: directory 6 | with_items: 7 | - "{{win_security_policy_template_location}}" 8 | - "{{win_security_policy_database_location}}" 9 | - "{{win_security_policy_log_location}}" 10 | 11 | - name: create security policy on host 12 | win_template: 13 | src: security_policy.inf.yml 14 | dest: "{{win_security_policy_template_location}}\\ansible_windows_hardening_security_policy.inf" 15 | register: security_policy 16 | 17 | - name: load gpo configuration locally 18 | raw: "secedit /configure /cfg {{win_security_policy_template_location}}\\ansible_windows_hardening_security_policy.inf /db {{ win_security_policy_database_location }}\\ansible_windows_hardening.db /quiet" 19 | when: security_policy.changed 20 | -------------------------------------------------------------------------------- /tasks/user_rights.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | -------------------------------------------------------------------------------- /templates/security_policy.inf.yml: -------------------------------------------------------------------------------- 1 | [Unicode] 2 | Unicode=yes 3 | 4 | [Version] 5 | signature="$CHICAGO$" 6 | Revision=1 7 | 8 | [System Access] 9 | PasswordComplexity = {{win_security_PasswordComplexity}} 10 | LockoutBadCount = {{win_security_LockoutBadCount}} 11 | ResetLockoutCount = {{win_security_ResetLockoutCount}} 12 | LockoutDuration = {{win_security_LockoutDuration}} 13 | 14 | [Privilege Rights] 15 | SeRemoteInteractiveLogonRight = {{win_security_SeRemoteInteractiveLogonRight}} 16 | SeTcbPrivilege = {{win_security_SeTcbPrivilege}} 17 | SeMachineAccountPrivilege = {{win_security_SeMachineAccountPrivilege}} 18 | SeTrustedCredManAccessPrivilege = {{win_security_SeTrustedCredManAccessPrivilege}} 19 | SeNetworkLogonRight = {{win_security_SeNetworkLogonRight}} 20 | 21 | -------------------------------------------------------------------------------- /test/integration/default/inspec/controls/tests.rb: -------------------------------------------------------------------------------- 1 | include_controls 'windows-baseline' do 2 | # we need to skip the test to ensure we can connect with non-administrator 3 | # winrm user for our tests 4 | skip_control 'cis-network-access-2.2.2' 5 | skip_control 'windows-account-100' 6 | end 7 | -------------------------------------------------------------------------------- /test/integration/default/inspec/inspec.yml: -------------------------------------------------------------------------------- 1 | name: windows-hardening-integration-tests 2 | depends: 3 | - name: windows-baseline 4 | url: https://github.com/dev-sec/windows-baseline 5 | -------------------------------------------------------------------------------- /vagrant_windows_target.rb: -------------------------------------------------------------------------------- 1 | Vagrant.configure("2") do |c| 2 | c.vm.provision :shell do |shell| 3 | shell.path = "https://raw.githubusercontent.com/ansible/ansible/devel/examples/scripts/ConfigureRemotingForAnsible.ps1" 4 | end 5 | end 6 | --------------------------------------------------------------------------------