├── .foodcritic ├── .gitignore ├── .kitchen.yml ├── .ruby-version ├── .travis.yml ├── Berksfile ├── CHANGELOG.md ├── LICENSE ├── README.md ├── attributes └── default.rb ├── files └── default │ └── vulns │ └── ssh_config1011+ ├── libraries ├── config_macos.rb ├── harden_macos.rb ├── helpers.rb ├── matchers.rb ├── os_helpers.rb └── read_write_plist.rb ├── metadata.rb ├── providers └── atypical_plist.rb ├── recipes └── default.rb ├── resources ├── atypical_plist.rb └── default.rb ├── spec ├── spec_helper.rb └── unit │ └── recipes │ └── default_spec.rb └── test └── integration ├── default └── serverspec │ ├── config_spec.rb │ └── hardening_spec.rb └── helpers └── serverspec └── spec_helper.rb /.foodcritic: -------------------------------------------------------------------------------- 1 | ~FC044 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vagrant 2 | Berksfile.lock 3 | *~ 4 | *# 5 | .#* 6 | \#*# 7 | .*.sw[a-z] 8 | *.un~ 9 | 10 | # Bundler 11 | Gemfile.lock 12 | bin/* 13 | .bundle/* 14 | 15 | .kitchen/ 16 | .kitchen.local.yml 17 | 18 | .DS_Store 19 | -------------------------------------------------------------------------------- /.kitchen.yml: -------------------------------------------------------------------------------- 1 | --- 2 | driver: 3 | name: vagrant 4 | 5 | provisioner: 6 | name: chef_solo 7 | # The following is option and can be set as auto (which is warn by default), 8 | # info, debug, or warn. 9 | #log_level: debug 10 | 11 | platforms: 12 | - name: macosx1011 13 | driver: 14 | synced_folders: 15 | - [".", "/vagrant", "owner: :vagrant, group: :wheel, create: true, type: :rsync"] 16 | - ["~/.kitchen/cache", "/tmp/omnibus/cache", "owner: :vagrant, group: :wheel, create: true, type: :rsync"] 17 | gui: true 18 | - name: macos-10.12 19 | driver: 20 | synced_folders: 21 | - [".", "/vagrant", "owner: :vagrant, group: :wheel, create: true, type: :rsync"] 22 | - ["~/.kitchen/cache", "/tmp/omnibus/cache", "owner: :vagrant, group: :wheel, create: true, type: :rsync"] 23 | gui: true 24 | 25 | suites: 26 | - name: default 27 | run_list: 28 | - recipe[harden_macos::default] 29 | attributes: 30 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 2.2 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Use Travis's container-based infrastructure 2 | sudo: false 3 | addons: 4 | apt: 5 | sources: 6 | - chef-stable-precise 7 | packages: 8 | - chefdk 9 | 10 | # Don't `bundle install` 11 | install: echo "skip bundle install" 12 | 13 | branches: 14 | only: 15 | - master 16 | 17 | # Ensure we make ChefDK's Ruby the default 18 | before_script: 19 | - eval "$(/opt/chefdk/bin/chef shell-init bash)" 20 | # We have to install chef-sugar for ChefSpec 21 | - /opt/chefdk/embedded/bin/chef gem install chef-sugar 22 | # TODO: Remove this work-around for a Hashie::Mash WARNing 23 | # details: https://github.com/chef/chef-dk/issues/1157 24 | - /opt/chefdk/embedded/bin/chef gem install berkshelf 25 | script: 26 | - /opt/chefdk/embedded/bin/chef --version 27 | - /opt/chefdk/embedded/bin/foodcritic --version 28 | - /opt/chefdk/embedded/bin/foodcritic . 29 | - /opt/chefdk/bin/chef exec rspec 30 | -------------------------------------------------------------------------------- /Berksfile: -------------------------------------------------------------------------------- 1 | source 'https://supermarket.chef.io' 2 | 3 | metadata 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # harden_macos cookbook CHANGELOG 2 | 3 | This file is used to list changes made in each version of the harden_macos cookbook. 4 | 5 | 1.0.1 6 | ----- 7 | - Fixes to spacing, as well as a note in readme about chef 13 compatibility 8 | 9 | 10 | 1.0.0 11 | ----- 12 | - Initial release of harden_macos cookbook to open source. 13 | 14 | 15 | - - - 16 | Check the [Markdown Syntax Guide](http://daringfireball.net/projects/markdown/syntax) for help with Markdown. 17 | 18 | The [Github Flavored Markdown page](http://github.github.com/github-flavored-markdown/) describes the differences between markdown on github and standard markdown. 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # harden_macos Cookbook 2 | 3 | [![Build Status](https://travis-ci.org/NunaInc/harden_macos.svg?branch=master)](https://travis-ci.org/NunaInc/harden_macos) 4 | 5 | This cookbook covers several areas of hardening for macOS and has generally 6 | only been tested for OSX version 10.11.x and 10.12.x. 7 | 8 | ## Requirements 9 | ### platforms 10 | - `mac_os_x` 11 | 12 | ### platform_version 13 | - `10.11.x` 14 | - `10.12.x` 15 | 16 | ### cookbooks 17 | - `mac_os_x` - For userdefaults custom resource to do Library defaults write commands. NOTE: In order to work with Chef 13, you must have the change from this PR in your mac_os_x cookbook https://github.com/sous-chefs/mac_os_x/pull/25 18 | 19 | ## Attributes 20 | Descriptions for all attributes are located in attributes/default.rb 21 | 22 | ## References 23 | ### GOV.UK 24 | - https://www.gov.uk/government/uploads/system/uploads/attachment_data/file/470580/osx-provisioning-script.sh.txt - Specific 10.11 commands 25 | - https://www.gov.uk/government/uploads/system/uploads/attachment_data/file/471620/End_User_Devices_Security_Guidance_-_Apple_OS_X_10_11.pdf - Security explanations 26 | 27 | ### Apple 28 | - https://support.apple.com/en-us/HT201159 (links to gov.uk provisioning script) 29 | 30 | ### Other 31 | - https://github.com/cagerton/dots/blob/master/macos.sh - Specific MacOS commands 32 | - https://cipherli.st/ - ssh_config 33 | 34 | ## License and Authors 35 | Author: Meg Cassidy (meg@nuna.com) 36 | Author: Craig Anderson (craig@nuna.com) 37 | Author: Alan Berman (alan@nuna.com) 38 | 39 | Copyright:: 2016-2017, Nuna, Inc. 40 | 41 | Licensed under the Apache License, Version 2.0 (the "License"); 42 | you may not use this file except in compliance with the License. 43 | You may obtain a copy of the License at 44 | 45 | http://www.apache.org/licenses/LICENSE-2.0 46 | 47 | Unless required by applicable law or agreed to in writing, software 48 | distributed under the License is distributed on an "AS IS" BASIS, 49 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 50 | See the License for the specific language governing permissions and 51 | limitations under the License. 52 | -------------------------------------------------------------------------------- /attributes/default.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook Name:: harden_macos 3 | # Attribute:: default 4 | # Author:: Meg Cassidy () 5 | # Author:: Craig Anderson () 6 | # 7 | # Copyright:: 2016-2017, Nuna, Inc. 8 | # 9 | # Licensed under the Apache License, Version 2.0 (the "License"); 10 | # you may not use this file except in compliance with the License. 11 | # You may obtain a copy of the License at 12 | # 13 | # http://www.apache.org/licenses/LICENSE-2.0 14 | # 15 | # Unless required by applicable law or agreed to in writing, software 16 | # distributed under the License is distributed on an "AS IS" BASIS, 17 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | # See the License for the specific language governing permissions and 19 | # limitations under the License. 20 | 21 | # Security changes needed. 22 | default['harden_os']['spotlight_plist_changelist'] = { 23 | '"MENU_SPOTLIGHT_SUGGESTIONS"' => '0', 24 | '"MENU_WEBSEARCH"' => '0' 25 | } 26 | 27 | # Call the current_user method in libraries/helpers.rb 28 | # Returns the username of the currently logged in user (if applicable) 29 | default['harden_os']['user'] = current_user 30 | 31 | # Call the valid_current_user method in libraries/helpers.rb 32 | # Returns true or false 33 | default['harden_os']['user_check'] = valid_current_user 34 | 35 | # This list denotes which methods are run from the config_macos and 36 | # harden_macos libraries 37 | default['harden_os']['harden_tasks'] = \ 38 | ['firewall', 'safari', 'macos mail', 'devices', 'finder', 'icloud',\ 39 | 'network', 'remote services', 'sharing', 'utilities', 'ssh configs'] 40 | 41 | default['harden_os']['config_tasks'] = \ 42 | ['updates', 'user preferences', 'user permissions', 'login window', 'sleep',\ 43 | 'privacy'] 44 | -------------------------------------------------------------------------------- /files/default/vulns/ssh_config1011+: -------------------------------------------------------------------------------- 1 | HashKnownHosts yes 2 | Host * 3 | UseRoaming no 4 | SendEnv LANG LC_* 5 | ConnectTimeout 30 6 | KexAlgorithms curve25519-sha256@libssh.org,diffie-hellman-group-exchange-sha256 7 | MACs 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,umac-128@openssh.com 8 | Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr 9 | ServerAliveInterval 10 10 | -------------------------------------------------------------------------------- /libraries/config_macos.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook Name:: harden_macos 3 | # Library:: config_macos 4 | # Author:: Meg Cassidy () 5 | # 6 | # Copyright:: 2016-2017, Nuna, Inc. 7 | # 8 | # Licensed under the Apache License, Version 2.0 (the "License"); 9 | # you may not use this file except in compliance with the License. 10 | # You may obtain a copy of the License at 11 | # 12 | # http://www.apache.org/licenses/LICENSE-2.0 13 | # 14 | # Unless required by applicable law or agreed to in writing, software 15 | # distributed under the License is distributed on an "AS IS" BASIS, 16 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | # See the License for the specific language governing permissions and 18 | # limitations under the License. 19 | 20 | module OSHardeningCookbookMacOS 21 | module ConfigurationHelpers 22 | 23 | def configure_macos_updates 24 | puts("\nConfigurations Library: " + __method__.to_s) 25 | 26 | # Software Update function on the system in question must 27 | # be set to automatically check for updates. Without the automatic checks, 28 | # app and OS X updates will not automatically install. 29 | execute 'softwareupdate on' do 30 | command 'softwareupdate --schedule on' 31 | only_if 'softwareupdate --schedule | grep "Automatic check is off"' 32 | end 33 | 34 | # Each sub-array contains domain, key name and description of change 35 | supd_d = '/Library/Preferences/com.apple.SoftwareUpdate' 36 | comm_d = '/Library/Preferences/com.apple.commerce.plist' 37 | updates_dkd = \ 38 | [[supd_d, 'AutomaticCheckEnabled', 'Auto Check for Updates'],\ 39 | [supd_d, 'AutomaticDownload', 'Auto Download Updates'], 40 | [comm_d, 'AutoUpdateRestartRequired', 'AutoUpdate Restart Required'],\ 41 | [comm_d, 'AutoUpdate', 'Auto Update']] 42 | updates_dkd.each do |dkd| 43 | mac_os_x_userdefaults "Enable SysPref - #{dkd[2]}" do 44 | domain dkd[0] 45 | key dkd[1] 46 | value 1 47 | sudo true 48 | end 49 | end 50 | end 51 | 52 | def configure_macos_user_prefs 53 | puts("\nConfigurations Library: " + __method__.to_s) 54 | 55 | # Takes effect after logout/login 56 | mac_os_x_userdefaults 'Disable fast user switching' do 57 | domain '/Library/Preferences/.GlobalPreferences' 58 | key 'MultipleSessionEnabled' 59 | value 0 60 | end 61 | end 62 | 63 | def configure_macos_user_permissions 64 | puts("\nConfigurations Library: " + __method__.to_s) 65 | 66 | mac_os_x_userdefaults 'Disable guest account login' do 67 | domain '/Library/Preferences/com.apple.loginwindow' 68 | key 'GuestEnabled' 69 | value 0 70 | end 71 | mac_os_x_userdefaults 'Disable guest access to shared folders (AFP sharing)' do 72 | domain '/Library/Preferences/com.apple.AppleFileServer' 73 | key 'guestAccess' 74 | value 0 75 | end 76 | mac_os_x_userdefaults 'Disable guest access to shared folders (SMB sharing)' do 77 | domain '/Library/Preferences/SystemConfiguration/com.apple.smb.server' 78 | key 'AllowGuestAccess' 79 | value 0 80 | end 81 | 82 | end 83 | 84 | def configure_macos_login_window_prefs # Topic: 'login window' 85 | puts("\nConfigurations Library: " + __method__.to_s) 86 | 87 | # Enabling FileVault2 also automatically deletes this key and it 88 | # cannot be enabled while FileVault2 is enabled. 89 | execute 'Disable automatic login' do 90 | command 'defaults delete /Library/Preferences/com.apple.loginwindow '\ 91 | 'autoLoginUser' 92 | only_if 'defaults read /Library/Preferences/com.apple.loginwindow | '\ 93 | 'grep "autoLoginUser"' 94 | end 95 | 96 | # Clean up kcpassword file 97 | # If automatic login is ever enabled, an obfuscated copy of the user pw 98 | # is stored at /etc/kcpassword. 99 | file '/etc/kcpassword' do 100 | action :delete 101 | end 102 | 103 | # Disable password hints on lock screen 104 | # Note: Does not uncheck Login Options -> Show password hints, but still sets 105 | # the retries until hint at zero (disabled). 106 | # 107 | mac_os_x_userdefaults 'Disable password hints on lock screen' do 108 | domain 'com.apple.loginwindow' 109 | key 'RetriesUntilHint' 110 | value '0' 111 | type 'int' 112 | sudo true 113 | end 114 | end 115 | 116 | def configure_macos_sleep_prefs 117 | puts("\nConfigurations Library: " + __method__.to_s) 118 | 119 | # These prefs can be accessed in the GUI at System Preferences -> 120 | # Security and Privacy 121 | # NOTE: These require computer restart to take effect. 122 | sleep_kdv = \ 123 | [['askForPassword', 'Require password on screensaver', 1],\ 124 | ['askForPasswordDelay', 'Require password immediately', 0]] 125 | sleep_kdv.each do |kdv| 126 | mac_os_x_userdefaults kdv[1] do 127 | domain 'com.apple.screensaver' 128 | key kdv[0] 129 | user new_resource.user 130 | value kdv[2] 131 | end 132 | end 133 | 134 | execute 'Idle to screensaver time' do 135 | user new_resource.user 136 | command 'defaults -currentHost write com.apple.screensaver idleTime 300' 137 | not_if 'defaults -currentHost read com.apple.screensaver idleTime | '\ 138 | 'grep ^300$', user => new_resource.user 139 | end 140 | end 141 | 142 | def configure_privacy_prefs 143 | puts("\nConfigurations Library: " + __method__.to_s) 144 | 145 | # This is the same as: 146 | # Sys Preferences -> Security & Privacy => Privacy => Location Services 147 | # Select "System Services" and click "Details...". Check "Show location 148 | # icon in the menu bar when System Services request your location". 149 | mac_os_x_userdefaults 'Show icon in toolbar when localization is used' do 150 | domain '/Library/Preferences/com.apple.locationmenu' 151 | key 'ShowSystemServices' 152 | value 1 153 | end 154 | end 155 | 156 | end 157 | end 158 | 159 | Chef::Recipe.send(:include, OSHardeningCookbookMacOS::ConfigurationHelpers) 160 | Chef::Resource.send(:include, OSHardeningCookbookMacOS::ConfigurationHelpers) 161 | Chef::Provider.send(:include, OSHardeningCookbookMacOS::ConfigurationHelpers) 162 | -------------------------------------------------------------------------------- /libraries/harden_macos.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook Name:: harden_macos 3 | # Library:: harden_macos 4 | # Author:: Meg Cassidy () 5 | # 6 | # Copyright:: 2016-2017, Nuna, Inc. 7 | # 8 | # 9 | # Licensed under the Apache License, Version 2.0 (the "License"); 10 | # you may not use this file except in compliance with the License. 11 | # You may obtain a copy of the License at 12 | # 13 | # http://www.apache.org/licenses/LICENSE-2.0 14 | # 15 | # Unless required by applicable law or agreed to in writing, software 16 | # distributed under the License is distributed on an "AS IS" BASIS, 17 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | # See the License for the specific language governing permissions and 19 | # limitations under the License. 20 | 21 | module OSHardeningCookbookMacOS 22 | module HardeningHelpers 23 | 24 | def harden_macos_firewall 25 | puts("\nHardening Library: " + __method__.to_s) 26 | # Enable the macOS System Firewall 27 | # 1 = enable firewall and disables "Block All Incoming Connections" 28 | # 2 = enable firewall and enable the setting "Block All Incoming 29 | # Connections." 30 | mac_os_x_userdefaults 'Enable OS X system firewall' do 31 | domain '/Library/Preferences/com.apple.alf' 32 | key 'globalstate' 33 | value 1 34 | only_if { File.exist? '/Library/Preferences/com.apple.alf.plist' } 35 | end 36 | 37 | execute 'Enable firewall stealthmode' do 38 | command '/usr/libexec/ApplicationFirewall/socketfilterfw --setstealthmode on' 39 | not_if '/usr/libexec/ApplicationFirewall/socketfilterfw '\ 40 | '--getstealthmode | grep "Stealth mode enabled"' 41 | end 42 | end 43 | 44 | def harden_macos_app_safari 45 | puts("\nHardening Library: " + __method__.to_s) 46 | mac_os_x_userdefaults 'Safari disable opening files after downloading' do 47 | domain 'com.apple.Safari' 48 | key 'AutoOpenSafeDownloads' 49 | value 0 50 | user new_resource.user 51 | end 52 | 53 | # Safari's separate setting (separate from Spotlight) for search metrics 54 | mac_os_x_userdefaults 'Disable Safari Spotlight Suggestions' do 55 | domain 'com.apple.Safari' 56 | key 'UniversalSearchEnabled' 57 | value 0 58 | user new_resource.user 59 | end 60 | end 61 | 62 | def harden_macos_app_mail 63 | puts("\nHardening Library: " + __method__.to_s) 64 | # com.apple.mail-shared only exists setup of Mail.app was initiated 65 | mac_os_x_userdefaults 'Disable autoload remote content in Mail.app' do 66 | domain 'com.apple.mail-shared' 67 | key 'DisableURLLoading' 68 | value 1 69 | only_if { File.exist? "/Users/#{node['harden_os']['user']}/Library"\ 70 | "/Preferences/com.apple.mail-shared.plist" } 71 | end 72 | end 73 | 74 | def harden_macos_devices 75 | puts("\nHardening Library: " + __method__.to_s) 76 | 77 | mac_os_x_userdefaults 'Disable the IR controller (Apple Remote)' do 78 | domain '/Library/Preferences/com.apple.driver.AppleIRController' 79 | key 'DeviceEnabled' 80 | value 0 81 | only_if { File.exist? '/Library/Preferences/com.apple.driver.AppleIRController.plist' } 82 | end 83 | 84 | bluetooth_powerstate = Mixlib::ShellOut.new('defaults read /Library/Preferences/com.apple.Bluetooth ControllerPowerState | grep 1') 85 | if bluetooth_powerstate.run_command 86 | # Turn off Bluetooth, but only if no paired devices exist 87 | mac_os_x_userdefaults 'Turn off Bluetooth' do 88 | domain '/Library/Preferences/com.apple.Bluetooth' 89 | key 'ControllerPowerState' 90 | value 0 91 | notifies :run, 'execute[Kill bluetooth server process]', :delayed 92 | not_if 'system_profiler SPBluetoothDataType | grep "^Bluetooth:" -A 20 | grep "Connectable: Yes"' 93 | end 94 | end 95 | 96 | # This will not work if ControllerPowerState is not set to 0, because 97 | # the bluetooth process will just restart itself. 98 | execute 'Kill bluetooth server process' do 99 | command 'killall -HUP blued' 100 | action :nothing 101 | end 102 | 103 | # Disables the ability to login via Smartcard (macOS 10.12) 104 | mac_os_x_userdefaults 'disable Sierra smartcard support' do 105 | domain '/Library/Preferences/com.apple.security.smartcard' 106 | key 'DisabledTokens' 107 | value 'com.apple.CryptoTokenKit.pivtoken' 108 | type 'array' 109 | only_if { node['platform_version'].split('.')[1].to_i >= 12 } 110 | end 111 | end 112 | 113 | def harden_macos_finder 114 | puts("\nHardening Library: " + __method__.to_s) 115 | 116 | harden_macos_atypical_plist 'Change spotlight enabled categories' do 117 | identifier 'Spotlight' 118 | changelist node['harden_os']['spotlight_plist_changelist'] 119 | action :update 120 | end 121 | end 122 | 123 | def harden_macos_icloud 124 | puts("\nHardening Library: " + __method__.to_s) 125 | # This is reserved for future use 126 | end 127 | 128 | def harden_macos_network 129 | puts("\nHardening Library: " + __method__.to_s) 130 | 131 | get_wo_network = Mixlib::ShellOut.new('sudo systemsetup getwakeonnetworkaccess') 132 | unless get_wo_network.run_command == \ 133 | 'Wake On Network Access: Not supported on this machine.' 134 | execute 'Disable wake on network access' do 135 | command 'systemsetup -setwakeonnetworkaccess off' 136 | only_if { get_wo_network.run_command == 'Wake On Network Access: On' } 137 | end 138 | end 139 | 140 | # This does not currently work. Seems like things changed in 10.11 141 | # execute 'Require admin to create wifi computer-to-computer networks' do 142 | # command '/System/Library/PrivateFrameworks/Apple80211.framework/'\ 143 | # 'Versions/Current/Resources/airport en1 prefs '\ 144 | # 'RequireAdminIBSS=YES' 145 | # only_if '/System/Library/PrivateFrameworks/Apple80211.framework/'\ 146 | # 'Versions/Current/Resources/airport en1 prefs RequireAdminIBSS'\ 147 | # ' | grep "RequireAdminIBSS=NO\|RequireAdminIBSS=Unknown"' 148 | # end 149 | 150 | execute 'Set Clock Using Network Time' do 151 | command 'systemsetup setusingnetworktime on' 152 | only_if 'systemsetup getusingnetworktime | grep "Network Time: Off"' 153 | end 154 | end 155 | 156 | def harden_macos_remote_services 157 | puts("\nHardening Library: " + __method__.to_s) 158 | 159 | # Disable Apple Remote Events 160 | # "Apple events are the message-based interprocess communication 161 | # mechanism in Mac OS, first appearing in System 7 and supported by 162 | # every version since then, including Mac OS X. Apple events describe 163 | # "high-level" events such as "open document" or "print file," whereas 164 | # earlier OSs had supported much more basic events, namely "click" 165 | # and "keypress". Apple events form the basis of the Mac OS scripting 166 | # system, AppleScript." 167 | execute 'Disable Apple Remote Events' do 168 | command 'systemsetup -setremoteappleevents off' 169 | only_if 'systemsetup -getremoteappleevents | grep '\ 170 | '"Remote Apple Events: On"' 171 | end 172 | 173 | # Note: the agent does not start by default on 10.11 174 | execute 'Disable remote management' do 175 | command '/System/Library/CoreServices/RemoteManagement/ARDAgent.app'\ 176 | '/Contents/Resources/kickstart -deactivate -stop -configure '\ 177 | '-access -off' 178 | only_if 'ps -ef | egrep "/System/Library/CoreServices/Remote'\ 179 | 'Management/ARDAgent.app/Contents/MacOS/[A]RDAgent"' 180 | end 181 | end 182 | 183 | def harden_macos_sysprefs_sharing 184 | puts("\nHardening Library: " + __method__.to_s) 185 | 186 | # Most below are in GUI at System Preferences -> Sharing 187 | execute 'Disable remote login (SSH)' do 188 | command '/usr/sbin/systemsetup -f -setremotelogin off' 189 | only_if 'systemsetup -getremotelogin | grep "Remote Login: On"' 190 | end 191 | 192 | execute 'Disable printer sharing' do 193 | command 'cupsctl --no-share-printers' 194 | only_if 'cupsctl | egrep "_share_printers=1"' 195 | end 196 | 197 | macosx_service 'Disable screen sharing' do 198 | service_name 'com.apple.screensharing' 199 | plist '/System/Library/LaunchDaemons/com.apple.screensharing.plist' 200 | action :disable 201 | end 202 | 203 | # Disabled by default on MacOS >10.8 204 | macosx_service 'Disable ftp daemon' do 205 | service_name 'ftp' 206 | plist '/System/Library/LaunchDaemons/ftp.plist' 207 | action :disable 208 | end 209 | 210 | macosx_service 'Disable Apple File Server (File Sharing)' do 211 | service_name 'AppleFileServer' 212 | plist '/System/Library/LaunchDaemons/com.apple.AppleFileServer.plist' 213 | action :disable 214 | end 215 | 216 | macosx_service 'Disable SMB File Server (File Sharing)' do 217 | service_name 'smbd' 218 | plist '/System/Library/LaunchDaemons/com.apple.smbd.plist' 219 | action :disable 220 | end 221 | end 222 | 223 | def harden_macos_utilities 224 | puts("\nHardening Library: " + __method__.to_s) 225 | # This is reserved for future use. 226 | end 227 | 228 | def harden_macos_ssh_configs 229 | puts("\nHardening Library: " + __method__.to_s) 230 | 231 | # openSSH vulnerability fix - January 2016 232 | # CVE-2016-0777 and CVE-2016-0778) 233 | # 234 | # Note: The values of these variables changed in 10.11. 235 | # Version 10.10 is not supported. 236 | current_ssh_config = '/etc/ssh/ssh_config' 237 | ssh_config_version = '/vulns/ssh_config1011+' 238 | 239 | require 'FileUtils' 240 | require 'tmpdir' 241 | 242 | # Create directory to hold new ssh config. This must be done outside 243 | # a ruby_block so that it can be used in the cookbook_file below 244 | temp_dir = Dir.mktmpdir 245 | puts(__method__.to_s + ': temporary directory is: ' + temp_dir) 246 | new_ssh_config_path = temp_dir + '/ssh_config' 247 | 248 | # Copy ssh_config from the cookbook and place into temporary directory 249 | cookbook_file 'Secure SSH config file' do 250 | source ssh_config_version 251 | path new_ssh_config_path 252 | owner 'root' 253 | group 'wheel' 254 | action :create 255 | end 256 | 257 | # Rename existing ssh_config and then copy over the new config 258 | # The existing (original) ssh_config has the day, time, and .bak 259 | # appended to the file name. Only then does it copy over the new 260 | # ssh_config file 261 | # 262 | ruby_block 'rename original ssh_config and copy over new config' do 263 | block do 264 | # puts(__method__.to_s + ": Swapping in new ssh config" + temp_dir) 265 | File.rename(current_ssh_config, current_ssh_config + Time.now.strftime('%Y-%m-%d_%H%M') + '.bak') unless current_ssh_config.empty? 266 | FileUtils.mv new_ssh_config_path, current_ssh_config unless current_ssh_config.empty? 267 | end 268 | not_if { FileUtils.compare_file(new_ssh_config_path, current_ssh_config) } 269 | end # ruby block 270 | 271 | # Clean up! 272 | ruby_block 'Clean up temp directory' do 273 | block do 274 | FileUtils.rm_r temp_dir 275 | end 276 | end 277 | end # harden_macos_ssh_configs 278 | 279 | end # module HardeningHelpers 280 | end # module OSHardeningCookbookMacOS 281 | 282 | Chef::Recipe.send(:include, OSHardeningCookbookMacOS::HardeningHelpers) 283 | Chef::Resource.send(:include, OSHardeningCookbookMacOS::HardeningHelpers) 284 | Chef::Provider.send(:include, OSHardeningCookbookMacOS::HardeningHelpers) 285 | -------------------------------------------------------------------------------- /libraries/helpers.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook Name:: harden_macos 3 | # Library:: helpers 4 | # Author:: Meg Cassidy () 5 | # 6 | # Copyright:: 2016-2017, Nuna, Inc. 7 | # 8 | # Licensed under the Apache License, Version 2.0 (the "License"); 9 | # you may not use this file except in compliance with the License. 10 | # You may obtain a copy of the License at 11 | # 12 | # http://www.apache.org/licenses/LICENSE-2.0 13 | # 14 | # Unless required by applicable law or agreed to in writing, software 15 | # distributed under the License is distributed on an "AS IS" BASIS, 16 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | # See the License for the specific language governing permissions and 18 | # limitations under the License. 19 | 20 | # If a user is logged in, /dev/console is always owned by them. Returns the 21 | # username. 22 | def uid_user 23 | if node['platform'] == 'mac_os_x' 24 | user_uid = File.stat('/dev/console').uid 25 | require 'etc' 26 | Etc.getpwuid(user_uid).name 27 | else # Required to allow testing in Travis CI (Linux). Otherwise not used 28 | id_output = Mixlib::ShellOut.new('id -un') 29 | id_output.run_command 30 | unless id_output.error? # Will only return stdout if no error 31 | id_output.stdout.strip 32 | else 33 | '' 34 | end 35 | end 36 | end 37 | 38 | # Checks against the string returned by uid_user. Returns nil if uid_user is 39 | # root or a system user (which start with an underscore). 40 | def current_user 41 | if node['platform'] == 'mac_os_x' 42 | if uid_user == 'root' 43 | nil 44 | elsif /^_\w*\z/.match(uid_user) 45 | nil 46 | else 47 | uid_user 48 | end 49 | else # Required to allow testing in Travis CI (Linux). Otherwise not used 50 | uid_user 51 | end 52 | end 53 | 54 | # Similar checks as the current_user method, but with boolean output 55 | def valid_current_user 56 | if uid_user == 'root' 57 | false 58 | elsif /^_\w*\z/.match(uid_user) 59 | false 60 | else 61 | true 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /libraries/matchers.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook Name:: harden_macos 3 | # Library:: matchers 4 | # Author:: Meg Cassidy () 5 | # 6 | # Copyright:: 2016-2017, Nuna, Inc. 7 | # 8 | # Licensed under the Apache License, Version 2.0 (the "License"); 9 | # you may not use this file except in compliance with the License. 10 | # You may obtain a copy of the License at 11 | # 12 | # http://www.apache.org/licenses/LICENSE-2.0 13 | # 14 | # Unless required by applicable law or agreed to in writing, software 15 | # distributed under the License is distributed on an "AS IS" BASIS, 16 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | # See the License for the specific language governing permissions and 18 | # limitations under the License. 19 | 20 | # https://github.com/sethvargo/chefspec#packaging-custom-matchers 21 | if defined?(ChefSpec) 22 | def secure_harden_macos(harden_macos) 23 | ChefSpec::Matchers::ResourceMatcher.new(:harden_macos, :secure, harden_macos) 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /libraries/os_helpers.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook Name:: harden_macos 3 | # Library:: os_helpers 4 | # Author:: Meg Cassidy () 5 | # 6 | # Copyright:: 2016-2017, Nuna, Inc. 7 | # 8 | # Licensed under the Apache License, Version 2.0 (the "License"); 9 | # you may not use this file except in compliance with the License. 10 | # You may obtain a copy of the License at 11 | # 12 | # http://www.apache.org/licenses/LICENSE-2.0 13 | # 14 | # Unless required by applicable law or agreed to in writing, software 15 | # distributed under the License is distributed on an "AS IS" BASIS, 16 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | # See the License for the specific language governing permissions and 18 | # limitations under the License. 19 | 20 | # Helper methods for general OS related case statements 21 | module OperatingSystemHelpers 22 | 23 | def os_version 24 | # Takes the second integer from the node['platform_version'] value to run 25 | # checks against, since the cookbook only deals with macOS and that is used 26 | # as the major version. 27 | node['platform_version'].split('.')[1].to_i 28 | end 29 | 30 | def supported 31 | # Key/value array used in 'supported_platform_version' and 32 | # 'is_supported_platform?' methods against the node['platform'] value. 33 | { 34 | mac_os_x: %w(10.11 10.12) 35 | } 36 | end 37 | 38 | def supported_platform_version 39 | # Search the 'supported' method array contents for node['platform_version'] 40 | # values and returns true or false 41 | current_version = Chef::Version.new(node['platform_version']) 42 | sp = false 43 | supported[node['platform'].to_sym].each do |version| 44 | required_version = Chef::Version.new(version) 45 | # Move to next item if the major versions do not match. 46 | next unless required_version.major == current_version.major 47 | # Change sp value if the minor version matches 48 | # Example: Minor version of 10.12 is 12. Minor version of 10.11.3 is 11. 49 | sp = true if required_version.minor == current_version.minor 50 | end 51 | sp 52 | end 53 | 54 | def is_supported_platform? 55 | # Returns true or false if node['platform'] is contained in the 'supported' 56 | # method array AND the platform version is a value for that key 57 | supported.keys.include?(node['platform'].to_sym) && 58 | supported_platform_version 59 | end 60 | end 61 | 62 | Chef::Recipe.send(:include, OperatingSystemHelpers) 63 | Chef::Resource.send(:include, OperatingSystemHelpers) 64 | Chef::Provider.send(:include, OperatingSystemHelpers) 65 | -------------------------------------------------------------------------------- /libraries/read_write_plist.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook Name:: harden_macos 3 | # Library:: read_write_plist 4 | # Author:: Craig Anderson () 5 | # 6 | # Copyright:: 2016-2017, Nuna, Inc. 7 | # 8 | # Licensed under the Apache License, Version 2.0 (the "License"); 9 | # you may not use this file except in compliance with the License. 10 | # You may obtain a copy of the License at 11 | # 12 | # http://www.apache.org/licenses/LICENSE-2.0 13 | # 14 | # Unless required by applicable law or agreed to in writing, software 15 | # distributed under the License is distributed on an "AS IS" BASIS, 16 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | # See the License for the specific language governing permissions and 18 | # limitations under the License. 19 | 20 | # Routines to read and write complex plist file values. 21 | # Currently just reads and writes an ordered array of elements. 22 | 23 | module ReadWritePlist 24 | # Returns a Ruby Hash corresponding to an ordered array 25 | # of key value pairs in string 'str'. 26 | # 27 | # The format of 'str' is the output of the 28 | # macOs 'defaults read' command. 29 | # 30 | # It looks for array items of the form: 31 | # { enabled = X; name = VALUE;} 32 | # or 33 | # { enabled = X; name = "VALUE";} 34 | # where X is 0 or 1, and VALUE is something like CONTACT or IMAGES 35 | # 36 | # NOTE! Assumes Hash maintains insert order. This is 37 | # true for Ruby 1.9+. 38 | def key_value_str_to_hash(str) 39 | result = {} 40 | regexp = Regexp.new('\s*{\s*enabled\s*=\s*([\d]);\s*name\s*=\s*(["_\w]+);') 41 | 42 | output = str.gsub("\n", " ").squeeze(' ') 43 | output = output.gsub("(", "") 44 | output = output.gsub(")", "") 45 | o = output.split(',') 46 | o.each { |item| 47 | m = regexp.match(item) 48 | if m != nil then 49 | result[m[2]] = m[1] 50 | end 51 | } 52 | return result 53 | end 54 | 55 | # Convert a hash to a string suitable for handing off 56 | # to the 'defaults' program 57 | def key_value_hash_to_str(hash) 58 | result_str = "" 59 | hash.each do |param, enabled| 60 | result_str += " '{ enabled = #{enabled}; name = #{param}; }' " 61 | end 62 | return result_str 63 | end 64 | 65 | # Get the value corresponding to the key in 'hash'. 66 | # This handles the weirdness 67 | # where some keys are double quoted and some are not. 68 | def get_hash_value(hash, key) 69 | if hash[key] 70 | return hash[key] 71 | else 72 | new_key = '"' + key + '"' 73 | if hash[new_key] 74 | return hash[new_key] 75 | end 76 | end 77 | return nil 78 | end 79 | 80 | # Set the hash value corresponding to the key. 81 | # This handles the weirdness 82 | # where some keys are double quoted and some are not. 83 | def set_hash_value(hash, key, value) 84 | if hash[key] 85 | hash[key] = value 86 | return true 87 | else 88 | new_key = '"' + key + '"' 89 | if hash[new_key] 90 | hash[new_key] = value 91 | return true 92 | end 93 | end 94 | return nil 95 | end 96 | end 97 | -------------------------------------------------------------------------------- /metadata.rb: -------------------------------------------------------------------------------- 1 | name 'harden_macos' 2 | maintainer 'Nuna, Inc.' 3 | maintainer_email 'cookbooks@nuna.com' 4 | license 'Apache 2.0' 5 | description 'macOS hardening tasks' 6 | long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) 7 | version '1.0.1' 8 | issues_url 'https://github.com/NunaInc/harden_macos/issues' 9 | source_url 'https://github.com/NunaInc/harden_macos' 10 | depends 'mac_os_x' 11 | chef_version '>= 12.5' if respond_to?(:chef_version) 12 | supports 'mac_os_x' 13 | -------------------------------------------------------------------------------- /providers/atypical_plist.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook Name:: harden_macos 3 | # Provider:: atypical_plist 4 | # Author:: Craig Anderson () 5 | # 6 | # Copyright:: 2016-2017, Nuna, Inc. 7 | # 8 | # Licensed under the Apache License, Version 2.0 (the "License"); 9 | # you may not use this file except in compliance with the License. 10 | # You may obtain a copy of the License at 11 | # 12 | # http://www.apache.org/licenses/LICENSE-2.0 13 | # 14 | # Unless required by applicable law or agreed to in writing, software 15 | # distributed under the License is distributed on an "AS IS" BASIS, 16 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | # See the License for the specific language governing permissions and 18 | # limitations under the License. 19 | 20 | include ReadWritePlist 21 | 22 | $spotlight_key = "orderedItems" 23 | $spotlight_plist_version = "7" 24 | 25 | # These are the starting values when a new user 26 | # is created. Order is important, since the underlying 27 | # resource is an array and Ruby hashes preserve order. 28 | # 29 | # Double quoted values are also important! 30 | default_spotlight_prefs = { 31 | 'APPLICATIONS' => '1', 32 | '"MENU_SPOTLIGHT_SUGGESTIONS"' => '1', 33 | '"MENU_CONVERSION"' => '1', 34 | '"MENU_EXPRESSION"' => '1', 35 | '"MENU_DEFINITION"' => '1', 36 | '"SYSTEM_PREFS"' => '1', 37 | 'DOCUMENTS' => '1', 38 | 'DIRECTORIES' => '1', 39 | 'PRESENTATIONS' => '1', 40 | 'SPREADSHEETS' => '1', 41 | 'PDF' => '1', 42 | 'MESSAGES' => '1', 43 | 'CONTACT' => '1', 44 | '"EVENT_TODO"' => '1', 45 | 'IMAGES' => '1', 46 | 'BOOKMARKS' => '1', 47 | 'MUSIC' => '1', 48 | 'MOVIES' => '1', 49 | 'FONTS' => '1', 50 | '"MENU_OTHER"' => '1', 51 | '"MENU_WEBSEARCH"' => '1' 52 | } 53 | 54 | use_inline_resources 55 | action :update do 56 | case new_resource.identifier 57 | when 'Spotlight' 58 | do_log("Running secure_Spotlight\n") 59 | (generate_items, do_update, write_version, items_hash) = 60 | read_spotlight_config(new_resource.changelist) 61 | if generate_items 62 | do_log("Doing Spotlight generate") 63 | write_spotlight_version($spotlight_plist_version) 64 | write_spotlight_items(default_spotlight_prefs) 65 | 66 | items_hash = default_spotlight_prefs 67 | do_log("@@@ Item hash is #{items_hash}\n") 68 | end 69 | if do_update 70 | do_log("Doing Spotlight update") 71 | apply_spotlight_changelist(items_hash, new_resource.changelist) 72 | end 73 | else 74 | raise "Unhandled application: #{new_resource.application}" 75 | end 76 | end 77 | 78 | 79 | # Attempt to read the orderedItems from the Spotlight plist config file. 80 | # Verify that the version matches the expected value. 81 | # If it exists, check to see if it needs to be updated to match the 82 | # changelist passed in. 83 | # 84 | # Note that while orderedItems may kinda sorta look like JSON, it is not. 85 | # 86 | # Params: 87 | # changelist (Hash) - Key value pairs of attributes that caller wants 88 | # to have specified values 89 | # Returns: 90 | # generate_times: True if orderedItems needs to be created 91 | # update_spotlight: True if orderedItems needs updating to the 'changelist' 92 | # values 93 | # write_version: write version attribute 94 | # items_hash: hash of ordredItems read (if any) 95 | def read_spotlight_config(changelist) 96 | items_hash = nil 97 | write_version = false 98 | update_spotlight = false 99 | generate_items = false 100 | 101 | filename = get_spotlight_plist_filename() 102 | 103 | if filename 104 | do_log("File is: " + filename) 105 | else 106 | do_log("File name is not known") 107 | end 108 | 109 | if filename && ::File.file?(filename) 110 | do_log("File exists\n") 111 | # Make sure version matches. 112 | version_cmd = "defaults read #{filename} version" 113 | 114 | version_output = Mixlib::ShellOut.new(version_cmd) 115 | version_output.run_command 116 | unless version_output.error? # Will only return stdout if no error 117 | if version_output.stdout.strip != $spotlight_plist_version 118 | raise "Spotlight pref version mismatch: saw" \ 119 | "#{version_output}, expected #{$spotlight_plist_version}" 120 | end 121 | else 122 | generate_items = true 123 | write_version = true 124 | end 125 | key_cmd = "defaults read #{filename} #{$spotlight_key}" 126 | key_output = Mixlib::ShellOut.new(key_cmd) 127 | key_output.run_command 128 | unless key_output.error? # Will only return stdout if no error 129 | do_log("OrderedItems found\n") 130 | items_hash = key_value_str_to_hash(key_output.stdout) 131 | update_spotlight = false 132 | changelist.each do | key, value | 133 | if items_hash[key] != value 134 | update_spotlight = true 135 | break 136 | end 137 | end 138 | else 139 | # If user hasn't changed Spotlight prefs, orderedItems 140 | # may not exist 141 | do_log("OrderedItems not found\n") 142 | update_spotlight = true 143 | end 144 | elsif filename 145 | do_log("Plist file does not exist\n") 146 | update_spotlight = true 147 | end 148 | 149 | do_log(("Update: #{update_spotlight}; Write version #{write_version}\n")) 150 | return generate_items, update_spotlight, write_version, items_hash 151 | end 152 | 153 | 154 | # Apply the changelist to the items_hash hash and write it 155 | # out to the Spotlight plist file. Assumes that the ordered 156 | # Params: 157 | # items_hash (Hash) 158 | # changelist (Hash) 159 | def apply_spotlight_changelist(items_hash, changelist) 160 | 161 | do_log("apply_spotlight_items: items_hash is: #{items_hash}\n") 162 | changelist.each do | key, value | 163 | do_log("Changing key #{key} to #{value}\n") 164 | set_hash_value(items_hash, key, value) 165 | end 166 | do_log("apply_spotlight_items: items_hash is: #{items_hash}\n") 167 | write_spotlight_items(items_hash) 168 | end 169 | 170 | 171 | # Write the items_hash to the spotlight plist files key. 172 | # Params: 173 | # items_hash (Hash): 174 | def write_spotlight_items(items_hash) 175 | the_user = get_user_name() 176 | filename = get_spotlight_plist_filename() 177 | str_out = key_value_hash_to_str(items_hash) 178 | 179 | the_command = "defaults write #{filename} #{$spotlight_key}" \ 180 | " -array #{str_out}" 181 | do_log("write_spotlight_items: command is: #{the_command}\n") 182 | 183 | # This will show as running twice if orderedItems did not exist prior to 184 | # run. The first run is to set everything defined in default, and the 185 | # second is to apply the changes from the changelist. 186 | execute "Writing ordered list" do 187 | user the_user 188 | command the_command 189 | end 190 | end 191 | 192 | 193 | # Write out the version to the Spotlight plist file 194 | # Params: 195 | # version_num (string) 196 | def write_spotlight_version(version_num) 197 | filename = get_spotlight_plist_filename() 198 | the_user = get_user_name() 199 | if (filename.nil? || the_user.nil?) 200 | raise "write_spotlight_version: filename or user is nil" 201 | end 202 | 203 | the_command = "defaults write #{filename} version -int #{version_num}" 204 | do_log("write_spotlight_version: command is: #{the_command}\n") 205 | 206 | execute "Writing spotlight version" do 207 | user the_user 208 | command the_command 209 | end 210 | end 211 | 212 | 213 | # Get the current logged in user. Return nil if the 214 | # current logged in user is not valid. 215 | def get_user_name() 216 | if node['harden_os']['user_check'] == false 217 | do_log("Current user is not valid end user\n") 218 | return nil 219 | else 220 | return(node['harden_os']['user']) 221 | end 222 | end 223 | 224 | 225 | # Get the name of the spotlight filename. 226 | # Return nil if it can't be determined. 227 | def get_spotlight_plist_filename() 228 | user_name = get_user_name() 229 | if user_name 230 | filename = "/Users/#{user_name}/Library/Preferences/com.apple.Spotlight.plist" 231 | else 232 | filename = nil 233 | end 234 | return filename 235 | end 236 | 237 | # This is used to quickly turn verbose debugging on and off, 238 | # since it cannot be done in Chef kitchen. 239 | def do_log(string) 240 | Chef::Log.debug(string) 241 | end 242 | -------------------------------------------------------------------------------- /recipes/default.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook Name:: harden_macos 3 | # Recipe:: default 4 | # Author:: Meg Cassidy () 5 | # 6 | # Copyright:: 2016-2017, Nuna, Inc. 7 | # 8 | # Licensed under the Apache License, Version 2.0 (the "License"); 9 | # you may not use this file except in compliance with the License. 10 | # You may obtain a copy of the License at 11 | # 12 | # http://www.apache.org/licenses/LICENSE-2.0 13 | # 14 | # Unless required by applicable law or agreed to in writing, software 15 | # distributed under the License is distributed on an "AS IS" BASIS, 16 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | # See the License for the specific language governing permissions and 18 | # limitations under the License. 19 | 20 | 21 | # Call the is_supported_platform? method in libraries/os_helpers.rb 22 | # Takes the node's platform and compares to list of supported platforms 23 | unless is_supported_platform? 24 | raise "Node platform (#{node['platform']}) is not supported at this time" 25 | end 26 | 27 | node['harden_os']['harden_tasks'].each do |topic_name| 28 | harden_macos topic_name do 29 | user node['harden_os']['user'] 30 | end 31 | end 32 | 33 | node['harden_os']['config_tasks'].each do |topic_name| 34 | harden_macos topic_name do 35 | user node['harden_os']['user'] 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /resources/atypical_plist.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook:: harden_macos 3 | # Author:: Craig Anderson () 4 | # Provider:: atypical_plist 5 | # 6 | # Copyright:: 2016-2017, Nuna, Inc. 7 | # 8 | # 9 | # Licensed under the Apache License, Version 2.0 (the "License"); 10 | # you may not use this file except in compliance with the License. 11 | # You may obtain a copy of the License at 12 | # 13 | # http://www.apache.org/licenses/LICENSE-2.0 14 | # 15 | # Unless required by applicable law or agreed to in writing, software 16 | # distributed under the License is distributed on an "AS IS" BASIS, 17 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | # See the License for the specific language governing permissions and 19 | # limitations under the License. 20 | 21 | # Include the plist library file 22 | include ReadWritePlist 23 | 24 | actions :update 25 | default_action :update 26 | 27 | property :identifier, :kind_of => String, :name_attribute => true, :required => true 28 | property :values, :kind_of => [Hash], :default => nil, :required => true 29 | property :changelist, :kind_of => [Hash], :default => nil, :required => false 30 | -------------------------------------------------------------------------------- /resources/default.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook Name:: harden_macos 3 | # Author:: Meg Cassidy () 4 | # Resources:: default 5 | # 6 | # Copyright:: 2016-2017, Nuna, Inc. 7 | # 8 | # Licensed under the Apache License, Version 2.0 (the "License"); 9 | # you may not use this file except in compliance with the License. 10 | # You may obtain a copy of the License at 11 | # 12 | # http://www.apache.org/licenses/LICENSE-2.0 13 | # 14 | # Unless required by applicable law or agreed to in writing, software 15 | # distributed under the License is distributed on an "AS IS" BASIS, 16 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | # See the License for the specific language governing permissions and 18 | # limitations under the License. 19 | 20 | resource_name :harden_macos 21 | 22 | actions :secure 23 | default_action :secure 24 | 25 | # A topic is a named group of hardening tasks 26 | attribute :topic, 27 | kind_of: String, 28 | name_attribute: true, 29 | required: true 30 | 31 | # Task specific options, such as setting the screensaver to 5 minutes, etc. 32 | attribute :options, 33 | kind_of: Hash, 34 | required: false 35 | 36 | # Run as a specific user, otherwise run as node's current user 37 | attribute :user, 38 | kind_of: String, 39 | default: node['harden_os']['user'], 40 | required: true 41 | 42 | action :secure do 43 | unless node['harden_os']['user'].nil? 44 | case node['platform'] 45 | when 'mac_os_x' 46 | case new_resource.topic.downcase 47 | # Call specific methods for each topic 48 | when 'firewall' then harden_macos_firewall 49 | when 'safari' then harden_macos_app_safari 50 | when 'macos mail' then harden_macos_app_mail 51 | when 'devices' then harden_macos_devices 52 | when 'finder' then harden_macos_finder 53 | when 'icloud' then harden_macos_icloud 54 | when 'network' then harden_macos_network 55 | when 'remote services' then harden_macos_remote_services 56 | when 'sharing' then harden_macos_sysprefs_sharing 57 | when 'utilities' then harden_macos_utilities 58 | when 'ssh configs' then harden_macos_ssh_configs 59 | when 'updates' then configure_macos_updates 60 | when 'user preferences' then configure_macos_user_prefs 61 | when 'user permissions' then configure_macos_user_permissions 62 | when 'login window' then configure_macos_login_window_prefs 63 | when 'sleep' then configure_macos_sleep_prefs 64 | when 'hotcorners' then configure_macos_hotcorners 65 | when 'privacy' then configure_privacy_prefs 66 | end 67 | else 68 | # Do not extend this functionality to support other operating systems 69 | raise "Node platform (#{node['platform']}) is not supported at this time" 70 | end 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook Name:: harden_macos 3 | # Author:: Meg Cassidy () 4 | # Spec:: helper 5 | # 6 | # Copyright:: 2016-2017, Nuna, Inc. 7 | # 8 | # Licensed under the Apache License, Version 2.0 (the "License"); 9 | # you may not use this file except in compliance with the License. 10 | # You may obtain a copy of the License at 11 | # 12 | # http://www.apache.org/licenses/LICENSE-2.0 13 | # 14 | # Unless required by applicable law or agreed to in writing, software 15 | # distributed under the License is distributed on an "AS IS" BASIS, 16 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | # See the License for the specific language governing permissions and 18 | # limitations under the License. 19 | 20 | require 'chefspec' 21 | require 'chefspec/berkshelf' 22 | 23 | at_exit { ChefSpec::Coverage.report! } 24 | 25 | RSpec.configure do |config| 26 | config.color = true 27 | config.alias_example_group_to :describe_recipe, type: :recipe 28 | end 29 | 30 | shared_context 'converged recipe', type: :recipe do 31 | let(:chef_run) do 32 | runner = ChefSpec::SoloRunner.new(node_attributes) 33 | runner.converge(described_recipe) 34 | end 35 | 36 | let(:node_attributes) do 37 | {} 38 | end 39 | 40 | let(:context_node_attributes) do 41 | {} 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /spec/unit/recipes/default_spec.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook Name:: harden_macos 3 | # Author:: Meg Cassidy () 4 | # Spec:: default 5 | # 6 | # Copyright:: 2016-2017, Nuna, Inc. 7 | # 8 | # Licensed under the Apache License, Version 2.0 (the "License"); 9 | # you may not use this file except in compliance with the License. 10 | # You may obtain a copy of the License at 11 | # 12 | # http://www.apache.org/licenses/LICENSE-2.0 13 | # 14 | # Unless required by applicable law or agreed to in writing, software 15 | # distributed under the License is distributed on an "AS IS" BASIS, 16 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | # See the License for the specific language governing permissions and 18 | # limitations under the License. 19 | 20 | require 'spec_helper' 21 | 22 | # describe_recipe 'harden_macos::default' do 23 | # context 'When running on mac_os_x 10.11' do 24 | # let(:node_attributes) do 25 | # { platform: 'mac_os_x', version: '10.11.1' } 26 | # end 27 | 28 | # # let(:context_node_attributes) do 29 | # # os_version == 11 30 | # # end 31 | 32 | # it 'converges successfully' do 33 | # expect { chef_run }.to_not raise_error 34 | # end 35 | 36 | # it 'runs firewall harden task with the default action' do 37 | # expect(chef_run).to secure_harden_macos('firewall') 38 | # end 39 | 40 | # it 'runs safari harden task with the default action' do 41 | # expect(chef_run).to secure_harden_macos('safari') 42 | # end 43 | 44 | # it 'runs macos mail harden task with the default action' do 45 | # expect(chef_run).to secure_harden_macos('macos mail') 46 | # end 47 | 48 | # it 'runs devices harden task with the default action' do 49 | # expect(chef_run).to secure_harden_macos('devices') 50 | # end 51 | 52 | # it 'runs finder harden task with the default action' do 53 | # expect(chef_run).to secure_harden_macos('finder') 54 | # end 55 | 56 | # it 'runs icloud harden task with the default action' do 57 | # expect(chef_run).to secure_harden_macos('icloud') 58 | # end 59 | 60 | # it 'runs network harden task with the default action' do 61 | # expect(chef_run).to secure_harden_macos('network') 62 | # end 63 | 64 | # it 'runs remote services harden task with the default action' do 65 | # expect(chef_run).to secure_harden_macos('remote services') 66 | # end 67 | 68 | # it 'runs sharing harden task with the default action' do 69 | # expect(chef_run).to secure_harden_macos('sharing') 70 | # end 71 | 72 | # it 'runs utilities harden task with the default action' do 73 | # expect(chef_run).to secure_harden_macos('utilities') 74 | # end 75 | 76 | # it 'runs ssh configs harden task with the default action' do 77 | # expect(chef_run).to secure_harden_macos('ssh configs') 78 | # end 79 | 80 | # it 'runs updates harden task with the default action' do 81 | # expect(chef_run).to secure_harden_macos('updates') 82 | # end 83 | 84 | # it 'runs user preferences harden task with the default action' do 85 | # expect(chef_run).to secure_harden_macos('user preferences') 86 | # end 87 | 88 | # it 'runs user permissions harden task with the default action' do 89 | # expect(chef_run).to secure_harden_macos('user permissions') 90 | # end 91 | 92 | # it 'runs login window harden task with the default action' do 93 | # expect(chef_run).to secure_harden_macos('login window') 94 | # end 95 | 96 | # it 'runs sleep harden task with the default action' do 97 | # expect(chef_run).to secure_harden_macos('sleep') 98 | # end 99 | 100 | # it 'runs privacy harden task with the default action' do 101 | # expect(chef_run).to secure_harden_macos('privacy') 102 | # end 103 | # end 104 | # end 105 | -------------------------------------------------------------------------------- /test/integration/default/serverspec/config_spec.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook Name:: harden_macos 3 | # Author:: Meg Cassidy () 4 | # 5 | # Copyright:: 2016-2017, Nuna, Inc. 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | 19 | require 'spec_helper' 20 | 21 | describe 'harden_macos::config_spec' do 22 | # Serverspec examples can be found at 23 | # http://serverspec.org/resource_types.html 24 | describe 'Software Update on' do 25 | describe command('softwareupdate --schedule') do 26 | its(:stdout) { should match /^Automatic check is on$/ } 27 | end 28 | end 29 | describe 'Automatic Check Enabled' do 30 | describe command('defaults read /Library/Preferences/com.apple.SoftwareUpdate AutomaticCheckEnabled') do 31 | its(:stdout) { should match /^1$/ } 32 | end 33 | end 34 | describe 'Automatic Download Enabled' do 35 | describe command('defaults read /Library/Preferences/com.apple.SoftwareUpdate AutomaticDownload') do 36 | its(:stdout) { should match /^1$/ } 37 | end 38 | end 39 | describe 'Automatic Restart Required' do 40 | describe command('defaults read /Library/Preferences/com.apple.commerce.plist AutoUpdateRestartRequired') do 41 | its(:stdout) { should match /^1$/ } 42 | end 43 | end 44 | describe 'Autoupdate' do 45 | describe command('defaults read /Library/Preferences/com.apple.commerce.plist AutoUpdate') do 46 | its(:stdout) { should match /^1$/ } 47 | end 48 | end 49 | 50 | describe 'Fast User Switching Off' do 51 | describe command('defaults read /Library/Preferences/.GlobalPreferences MultipleSessionEnabled') do 52 | its(:stdout) { should match /^0$/ } 53 | end 54 | end 55 | 56 | describe 'Disable guest account login' do 57 | describe command('defaults read /Library/Preferences/com.apple.loginwindow GuestEnabled') do 58 | its(:stdout) { should match /^0$/ } 59 | end 60 | end 61 | 62 | describe 'Disable guest account to shared AFP folders' do 63 | describe command('defaults read /Library/Preferences/com.apple.AppleFileServer guestAccess') do 64 | its(:stdout) { should match /^0$/ } 65 | end 66 | end 67 | 68 | describe 'Disable guest account to shared SMB folders' do 69 | describe command('defaults read /Library/Preferences/SystemConfiguration/com.apple.smb.server AllowGuestAccess') do 70 | its(:stdout) { should match /^0$/ } 71 | end 72 | end 73 | 74 | describe 'Disable automatic login' do 75 | describe command('defaults read /Library/Preferences/com.apple.loginwindow autoLoginUser') do 76 | its(:stderr) { should match /does not exist\n$/ } 77 | end 78 | end 79 | 80 | describe 'Cleanup /etc/kcpasswd' do 81 | describe file('/etc/kcpasswd') do 82 | it { should_not exist } 83 | end 84 | end 85 | 86 | # Password hints 87 | describe 'Disable password hints on lock screen' do 88 | describe command('defaults read com.apple.loginwindow') do 89 | its(:stdout) { should match /RetriesUntilHint = 0;/ } 90 | end 91 | end 92 | 93 | # Sleep prefs - ask for password 94 | describe 'Ask for password' do 95 | describe command("su vagrant -c 'defaults read com.apple.screensaver askForPassword'") do 96 | its(:stdout) { should match /^1$/ } 97 | end 98 | end 99 | 100 | # Sleep prefs - require password immediately 101 | describe 'Ask for password immediately' do 102 | describe command("su vagrant -c 'defaults read com.apple.screensaver askForPasswordDelay'") do 103 | its(:stdout) { should match /^0$/ } 104 | end 105 | end 106 | 107 | # Idle to screensaver time 108 | describe 'Idle to Screensaver time' do 109 | describe command("su vagrant -c 'defaults -currentHost read com.apple.screensaver idleTime'") do 110 | its(:stdout) { should match /^300$/ } 111 | end 112 | end 113 | 114 | # Privacy prefs 115 | describe 'Show System Services' do 116 | describe command('defaults read /Library/Preferences/com.apple.locationmenu ShowSystemServices') do 117 | its(:stdout) { should match /^1$/ } 118 | end 119 | end 120 | 121 | end 122 | -------------------------------------------------------------------------------- /test/integration/default/serverspec/hardening_spec.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook Name:: harden_macos 3 | # Author:: Meg Cassidy () 4 | # 5 | # Copyright:: 2016-2017, Nuna, Inc. 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | 19 | require 'spec_helper' 20 | 21 | RSpec.configure do |c| 22 | c.filter_run_excluding :bluetooth_is_paired => lambda { 23 | connectable_cmd = 'system_profiler SPBluetoothDataType' + 24 | '| grep "^Bluetooth:" -A 20' + 25 | '| grep "Connectable: Yes"' 26 | output = `#{connectable_cmd}` 27 | the_match = output.match("Connectable: Yes") 28 | the_match != nil 29 | } 30 | end 31 | 32 | describe 'harden_macos::default' do 33 | # Serverspec examples can be found at 34 | # http://serverspec.org/resource_types.html 35 | describe 'Harden macOS firewall' do 36 | describe 'enable firewall' do 37 | describe command('defaults read /Library/Preferences/com.apple.alf globalstate') do 38 | its(:stdout) { should match /^1$/ } 39 | end 40 | end 41 | 42 | describe 'enable firewall stealthmode' do 43 | describe command('/usr/libexec/ApplicationFirewall/socketfilterfw --getstealthmode') do 44 | its(:stdout) { should match /Stealth mode enabled/ } 45 | end 46 | end 47 | end # Harden macOS firewall 48 | 49 | describe 'Harden Safari' do 50 | describe 'Safari disable opening files after downloading' do 51 | describe command('defaults read /Users/vagrant/Library/Preferences/com.apple.Safari AutoOpenSafeDownloads') do 52 | its(:stdout) { should match /^0$/ } 53 | end 54 | end 55 | 56 | describe 'Disable Safari Spotlight Suggestions' do 57 | describe command('defaults read' + 58 | ' /Users/vagrant/Library/Preferences/com.apple.Safari' + 59 | ' UniversalSearchEnabled') do 60 | its(:stdout) { should match /^0$/ } 61 | end 62 | end 63 | end # Harden Safari 64 | 65 | describe 'Disable autoload remote content in Mail.app' do 66 | describe file('/Users/vagrant/Library/Preferences/com.apple.mail-shared.plist') do 67 | it { should_not exist } 68 | end 69 | end 70 | 71 | describe 'Harden macOS Devices' do 72 | describe 'Diable IR controller' do 73 | describe command('defaults read' + 74 | ' /Library/Preferences/com.apple.driver.AppleIRController' + 75 | ' DeviceEnabled') do 76 | its(:stdout) { should match /0/ } 77 | end 78 | end 79 | 80 | describe 'Kill bluetooth server process', :bluetooth_is_paired => false do 81 | describe command('ps -elf | grep /usr/sbin/blued') do 82 | its(:stdout) { should_not match /\/usr\/sbin\/blued/ } 83 | end 84 | describe command('defaults read /Library/Preferences/com.apple.Bluetooth ControllerPowerState') do 85 | its(:stdout) { should match /0/ } 86 | end 87 | end 88 | 89 | # Note: Even on a new stock 10.12 kitchen image, there is no com.apple.security.smartcard 90 | # When there is, fix/change this. 91 | describe 'Disable login via Smartcard' do 92 | describe command('defaults read' + 93 | ' /Library/Preferences/com.apple.security.smartcard') do 94 | its(:stderr) { should match /Domain \/Library\/Preferences\/com.apple.security.smartcard does not exist/ } 95 | end 96 | end 97 | end # Harden macOS devices 98 | 99 | 100 | describe 'Harden Spotlight' do 101 | # Recipe turns off a couple of settings. Check for it. 102 | describe command('defaults read' + 103 | ' /Users/vagrant/Library/Preferences/com.apple.Spotlight.plist') do 104 | its(:stdout) { should match /enabled = 0;\s*name = "MENU_WEBSEARCH"/ } 105 | end 106 | describe command('defaults read' + 107 | ' /Users/vagrant/Library/Preferences/com.apple.Spotlight.plist') do 108 | its(:stdout) { should match /enabled = 0;\s*name = "MENU_SPOTLIGHT_SUGGESTIONS"/ } 109 | end 110 | end 111 | 112 | describe 'Harden macOS Networking' do 113 | describe 'Disable Wake on Network Access' do 114 | describe command ("systemsetup getwakeonnetworkaccess") do 115 | its(:stdout) { should match /Not supported on this machine|Off/ } 116 | end 117 | end 118 | 119 | # Fix this when corresponding command works. 120 | #describe 'Require admin to create wifi network' do 121 | #describe command ('ps -ef | egrep "/System/Library/CoreServices/Remote'\ 122 | #'Management/ARDAgent.app/Contents/MacOS/[A]RDAgent') do 123 | #its(:stdout) { should match // } 124 | #end 125 | #end 126 | 127 | describe 'Set Clock Using Network time' do 128 | describe command ("systemsetup getusingnetworktime") do 129 | its(:stdout) { should match /Network Time: On/ } 130 | end 131 | end 132 | end 133 | 134 | describe 'Harden macOS Remote Services' do 135 | describe 'Disable Apple Remote Events' do 136 | describe command ("systemsetup -getremoteappleevents | grep 'Remote Apple Events'") do 137 | its(:stdout) { should match /Remote Apple Events: Off/ } 138 | end 139 | end 140 | 141 | describe 'Disable remote mangement' do 142 | describe command ('ps -ef | egrep "/System/Library/CoreServices/Remote'\ 143 | 'Management/ARDAgent.app/Contents/MacOS/[A]RDAgent') do 144 | its(:stdout) { should match // } 145 | end 146 | end 147 | end 148 | 149 | 150 | describe 'Harden macOS System Preferences -> Sharing:' do 151 | describe 'Disable remote login' do 152 | describe command ("systemsetup -getremotelogin | grep 'Remote Login'") do 153 | its(:stdout) { should match /Remote Login: Off/ } 154 | end 155 | end 156 | 157 | describe 'Disable Printer Sharing' do 158 | describe command ('cupsctl | grep _share_printers') do 159 | its(:stdout) { should match /_share_printers=0/ } 160 | end 161 | end 162 | 163 | describe 'Disable Screen Sharing' do 164 | describe command ('defaults read' + 165 | ' /System/Library/LaunchDaemons/com.apple.screensharing.plist' + 166 | ' Disabled') do 167 | its(:stdout) { should match /1/ } 168 | end 169 | end 170 | 171 | describe 'Disable FTP daemon' do 172 | describe command ('defaults read /System/Library/LaunchDaemons/ftp.plist Disabled') do 173 | its(:stdout) { should match /1/ } 174 | end 175 | end 176 | 177 | describe 'Disable AppleFileServer (Sharing)' do 178 | describe command ('defaults read' + 179 | ' /System/Library/LaunchDaemons/com.apple.AppleFileServer.plist' + 180 | ' Disabled') do 181 | its(:stdout) { should match /1/ } 182 | end 183 | end 184 | 185 | describe 'Disable SMB File Server (Sharing)' do 186 | describe command ('defaults read' + 187 | ' /System/Library/LaunchDaemons/com.apple.smbd.plist' + 188 | ' Disabled') do 189 | its(:stdout) { should match /1/ } 190 | end 191 | end 192 | end # Harden macOS System Preferences -> Sharing: 193 | 194 | end # harden_macos::default 195 | -------------------------------------------------------------------------------- /test/integration/helpers/serverspec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook Name:: harden_macos 3 | # Author:: Meg Cassidy () 4 | # 5 | # Copyright:: 2016-2017, Nuna, Inc. 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | 19 | require 'serverspec' 20 | 21 | if (/cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM).nil? 22 | set :backend, :exec 23 | else 24 | set :backend, :cmd 25 | set :os, family: 'windows' 26 | end 27 | --------------------------------------------------------------------------------