├── .editorconfig ├── .envrc ├── .gitattributes ├── .github ├── CODEOWNERS └── workflows │ ├── ci.yml │ └── stale.yml ├── .gitignore ├── .markdownlint-cli2.yaml ├── .mdlrc ├── .overcommit.yml ├── .vscode └── extensions.json ├── .yamllint ├── Berksfile ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Dangerfile ├── LICENSE ├── README.md ├── TESTING.md ├── attributes └── default.rb ├── chefignore ├── documentation └── .gitkeep ├── kitchen.dokken.yml ├── kitchen.exec.yml ├── kitchen.global.yml ├── kitchen.yml ├── libraries └── helpers.rb ├── metadata.rb ├── mlc_config.json ├── recipes ├── default.rb └── iptables.rb ├── renovate.json ├── spec ├── spec_helper.rb └── unit │ └── recipes │ ├── default_spec.rb │ ├── default_windows_spec.rb │ └── iptables_spec.rb ├── templates ├── ca_keys.erb ├── port_ssh.erb ├── revoked_keys.erb ├── ssh_config.erb └── sshd_config.erb └── test └── integration ├── default └── default_spec.rb ├── iptables └── default_spec.rb └── windows-default └── default_spec.rb /.editorconfig: -------------------------------------------------------------------------------- 1 | # https://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 | # 2 space indentation 12 | indent_style = space 13 | indent_size = 2 14 | 15 | # Avoid issues parsing cookbook files later 16 | charset = utf-8 17 | 18 | # Avoid cookstyle warnings 19 | trim_trailing_whitespace = true 20 | -------------------------------------------------------------------------------- /.envrc: -------------------------------------------------------------------------------- 1 | use chefworkstation 2 | export KITCHEN_GLOBAL_YAML=kitchen.global.yml 3 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @sous-chefs/maintainers 2 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: ci 3 | 4 | "on": 5 | pull_request: 6 | push: 7 | branches: 8 | - main 9 | 10 | jobs: 11 | lint-unit: 12 | uses: sous-chefs/.github/.github/workflows/lint-unit.yml@3.1.1 13 | permissions: 14 | actions: write 15 | checks: write 16 | pull-requests: write 17 | statuses: write 18 | issues: write 19 | 20 | integration: 21 | needs: lint-unit 22 | runs-on: ubuntu-latest 23 | strategy: 24 | matrix: 25 | os: 26 | - "almalinux-8" 27 | - "amazonlinux-2" 28 | - "centos-7" 29 | - "centos-stream-8" 30 | - "debian-10" 31 | - "debian-11" 32 | - "fedora-latest" 33 | - "opensuse-leap-15" 34 | - "ubuntu-1804" 35 | - "ubuntu-2004" 36 | - "rockylinux-8" 37 | suite: 38 | - "default" 39 | - "iptables" 40 | exclude: 41 | - os: "almalinux-8" 42 | suite: "iptables" 43 | - os: "centos-7" 44 | suite: "iptables" 45 | - os: "centos-stream-8" 46 | suite: "iptables" 47 | - os: "opensuse-leap-15" 48 | suite: "iptables" 49 | - os: "rockylinux-8" 50 | suite: "iptables" 51 | fail-fast: false 52 | 53 | steps: 54 | - name: Check out code 55 | uses: actions/checkout@v4 56 | - name: Install Chef 57 | uses: actionshub/chef-install@3.0.0 58 | - name: Dokken 59 | uses: actionshub/test-kitchen@3.0.0 60 | env: 61 | CHEF_LICENSE: accept-no-persist 62 | KITCHEN_LOCAL_YAML: kitchen.dokken.yml 63 | with: 64 | suite: ${{ matrix.suite }} 65 | os: ${{ matrix.os }} 66 | 67 | integration-windows: 68 | needs: lint-unit 69 | runs-on: windows-latest 70 | timeout-minutes: 20 71 | strategy: 72 | matrix: 73 | os: 74 | - "windows-latest" 75 | suite: 76 | - "windows-default" 77 | fail-fast: false 78 | 79 | steps: 80 | - name: Check out code 81 | uses: actions/checkout@v4 82 | - name: Install Chef 83 | uses: actionshub/chef-install@3.0.0 84 | - name: Download Openssh Installer 85 | uses: suisei-cn/actions-download-file@v1.6.0 86 | with: 87 | url: https://github.com/PowerShell/Win32-OpenSSH/releases/download/v9.2.2.0p1-Beta/OpenSSH-Win64-v9.2.2.0.msi 88 | target: installer/ 89 | - name: Install Openssh 90 | run: | 91 | echo %cd% 92 | dir installer 93 | $file = "installer\\OpenSSH-Win64-v9.2.2.0.msi" 94 | $log = "installer\\install.log" 95 | $procMain = Start-Process "msiexec" "/i `"$file`" /qn /l*! `"$log`"" -NoNewWindow -PassThru 96 | $procLog = Start-Process "powershell" "Get-Content -Path `"$log`" -Wait" -NoNewWindow -PassThru 97 | $procMain.WaitForExit() 98 | $procLog.Kill() 99 | 100 | - name: Kitchen Converge 101 | uses: actionshub/test-kitchen@3.0.0 102 | env: 103 | CHEF_LICENSE: accept-no-persist 104 | KITCHEN_LOCAL_YAML: kitchen.exec.yml 105 | with: 106 | suite: ${{ matrix.suite }} 107 | os: ${{ matrix.os }} 108 | action: converge 109 | - name: Kitchen Verify 110 | uses: actionshub/test-kitchen@3.0.0 111 | env: 112 | CHEF_LICENSE: accept-no-persist 113 | KITCHEN_LOCAL_YAML: kitchen.exec.yml 114 | with: 115 | suite: ${{ matrix.suite }} 116 | os: ${{ matrix.os }} 117 | action: verify 118 | 119 | # unable to get SSH service to start 120 | # integration-macos: 121 | # needs: [mdl, yamllint, delivery] 122 | # runs-on: macos-latest 123 | # strategy: 124 | # matrix: 125 | # os: 126 | # - 'macos-latest' 127 | # suite: 128 | # - 'default' 129 | # fail-fast: false 130 | # 131 | # steps: 132 | # - name: Check out code 133 | # uses: actions/checkout@v2 134 | # - name: Install Chef 135 | # uses: actionshub/chef-install@2.0.4 136 | # - name: test-kitchen 137 | # uses: actionshub/test-kitchen@2.1.0 138 | # env: 139 | # CHEF_LICENSE: accept-no-persist 140 | # KITCHEN_LOCAL_YAML: kitchen.exec.yml 141 | # with: 142 | # suite: ${{ matrix.suite }} 143 | # os: ${{ matrix.os }} 144 | # - name: Print debug output on failure 145 | # if: failure() 146 | # run: | 147 | # sudo syslog 148 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Mark stale issues and pull requests 3 | 4 | "on": 5 | schedule: [cron: "0 0 * * *"] 6 | 7 | jobs: 8 | stale: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/stale@v9 12 | with: 13 | repo-token: ${{ secrets.GITHUB_TOKEN }} 14 | close-issue-message: > 15 | Closing due to inactivity. 16 | If this is still an issue please reopen or open another issue. 17 | Alternatively drop by the #sous-chefs channel on the [Chef Community Slack](http://community-slack.chef.io/) and we'll be happy to help! 18 | Thanks, Sous-Chefs. 19 | days-before-close: 7 20 | days-before-stale: 365 21 | stale-issue-message: > 22 | Marking stale due to inactivity. 23 | Remove stale label or comment or this will be closed in 7 days. 24 | Alternatively drop by the #sous-chefs channel on the [Chef Community Slack](http://community-slack.chef.io/) and we'll be happy to help! 25 | Thanks, Sous-Chefs. 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.rbc 2 | .config 3 | InstalledFiles 4 | pkg 5 | test/tmp 6 | test/version_tmp 7 | tmp 8 | _Store 9 | *~ 10 | *# 11 | .#* 12 | \#*# 13 | *.un~ 14 | *.tmp 15 | *.bk 16 | *.bkup 17 | 18 | # editor files 19 | .idea 20 | .*.sw[a-z] 21 | 22 | # ruby/bundler/rspec files 23 | .ruby-version 24 | .ruby-gemset 25 | .rvmrc 26 | Gemfile.lock 27 | .bundle 28 | *.gem 29 | coverage 30 | spec/reports 31 | 32 | # YARD / rdoc artifacts 33 | .yardoc 34 | _yardoc 35 | doc/ 36 | rdoc 37 | 38 | # chef infra stuff 39 | Berksfile.lock 40 | .kitchen 41 | kitchen.local.yml 42 | vendor/ 43 | .coverage/ 44 | .zero-knife.rb 45 | Policyfile.lock.json 46 | 47 | # vagrant stuff 48 | .vagrant/ 49 | .vagrant.d/ 50 | -------------------------------------------------------------------------------- /.markdownlint-cli2.yaml: -------------------------------------------------------------------------------- 1 | config: 2 | ul-indent: false # MD007 3 | line-length: false # MD013 4 | no-duplicate-heading: false # MD024 5 | reference-links-images: false # MD052 6 | ignores: 7 | - .github/copilot-instructions.md 8 | -------------------------------------------------------------------------------- /.mdlrc: -------------------------------------------------------------------------------- 1 | rules "~MD013", "~MD024", "~MD025" 2 | -------------------------------------------------------------------------------- /.overcommit.yml: -------------------------------------------------------------------------------- 1 | --- 2 | PreCommit: 3 | TrailingWhitespace: 4 | enabled: true 5 | YamlLint: 6 | enabled: true 7 | required_executable: "yamllint" 8 | ChefSpec: 9 | enabled: true 10 | required_executable: "chef" 11 | command: ["chef", "exec", "rspec"] 12 | Cookstyle: 13 | enabled: true 14 | required_executable: "cookstyle" 15 | command: ["cookstyle"] 16 | MarkdownLint: 17 | enabled: false 18 | required_executable: "npx" 19 | command: ["npx", "markdownlint-cli2", "'**/*.md'"] 20 | include: ["**/*.md"] 21 | 22 | CommitMsg: 23 | HardTabs: 24 | enabled: true 25 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "chef-software.chef", 4 | "rebornix.ruby", 5 | "editorconfig.editorconfig", 6 | "DavidAnson.vscode-markdownlint" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /.yamllint: -------------------------------------------------------------------------------- 1 | --- 2 | extends: default 3 | rules: 4 | line-length: 5 | max: 256 6 | level: warning 7 | document-start: disable 8 | braces: 9 | forbid: false 10 | min-spaces-inside: 0 11 | max-spaces-inside: 1 12 | min-spaces-inside-empty: -1 13 | max-spaces-inside-empty: -1 14 | comments: 15 | min-spaces-from-content: 1 16 | -------------------------------------------------------------------------------- /Berksfile: -------------------------------------------------------------------------------- 1 | source 'https://supermarket.chef.io' 2 | 3 | metadata 4 | 5 | group :integration do 6 | cookbook 'apt' 7 | cookbook 'homebrew' 8 | end 9 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # openssh Cookbook CHANGELOG 2 | 3 | This file is used to list changes made in each version of the openssh cookbook. 4 | 5 | ## Unreleased 6 | 7 | ## 2.11.15 - *2024-11-27* 8 | 9 | - In more recent enterprise linux (rhel, oel, etc) systems system policies are added at end of sshd config and therefor any match block needs to be closed 10 | 11 | ## 2.11.14 - *2024-11-18* 12 | 13 | Standardise files with files in sous-chefs/repo-management 14 | 15 | Standardise files with files in sous-chefs/repo-management 16 | 17 | ## 2.11.13 - *2024-07-15* 18 | 19 | Standardise files with files in sous-chefs/repo-management 20 | 21 | ## 2.11.12 - *2024-05-23* 22 | 23 | Standardise files with files in sous-chefs/repo-management 24 | 25 | Standardise files with files in sous-chefs/repo-management 26 | 27 | ## 2.11.11 - *2024-05-03* 28 | 29 | ## 2.11.10 - *2024-05-03* 30 | 31 | ## 2.11.9 - *2024-03-18* 32 | 33 | ## 2.11.8 - *2024-03-17* 34 | 35 | ## 2.11.7 - *2024-03-12* 36 | 37 | - fix sshd-keygen command location for Amazon Linux 2023 38 | 39 | ## 2.11.6 - *2023-12-21* 40 | 41 | ## 2.11.5 - *2023-09-29* 42 | 43 | ## 2.11.4 - *2023-09-11* 44 | 45 | ## 2.11.3 - *2023-08-05* 46 | 47 | - resolved cookstyle error: attributes/default.rb:120:58 refactor: `Chef/RedundantCode/MultiplePlatformChecks` 48 | 49 | ## 2.11.2 - *2023-08-04* 50 | 51 | Standardise files with files in sous-chefs/repo-management 52 | 53 | ## 2.11.1 - *2023-08-04* 54 | 55 | - Disable PAM option on Windows 56 | 57 | ## 2.11.0 - *2023-08-01* 58 | 59 | - Add Windows Support 60 | 61 | ## 2.10.18 - *2023-07-10* 62 | 63 | ## 2.10.17 - *2023-05-16* 64 | 65 | ## 2.10.16 - *2023-04-17* 66 | 67 | ## 2.10.15 - *2023-04-07* 68 | 69 | Standardise files with files in sous-chefs/repo-management 70 | 71 | ## 2.10.14 - *2023-04-01* 72 | 73 | ## 2.10.13 - *2023-04-01* 74 | 75 | ## 2.10.12 - *2023-04-01* 76 | 77 | Standardise files with files in sous-chefs/repo-management 78 | 79 | ## 2.10.11 - *2023-03-20* 80 | 81 | Standardise files with files in sous-chefs/repo-management 82 | 83 | ## 2.10.10 - *2023-03-15* 84 | 85 | Standardise files with files in sous-chefs/repo-management 86 | 87 | Standardise files with files in sous-chefs/repo-management 88 | 89 | ## 2.10.9 - *2023-02-23* 90 | 91 | Standardise files with files in sous-chefs/repo-management 92 | 93 | ## 2.10.8 - *2023-02-23* 94 | 95 | Remove delivery 96 | 97 | ## 2.10.7 - *2023-02-16* 98 | 99 | Standardise files with files in sous-chefs/repo-management 100 | 101 | ## 2.10.6 - *2023-02-14* 102 | 103 | ## 2.10.5 - *2023-02-14* 104 | 105 | Standardise files with files in sous-chefs/repo-management 106 | 107 | ## 2.10.4 - *2022-12-08* 108 | 109 | Standardise files with files in sous-chefs/repo-management 110 | 111 | Standardise files with files in sous-chefs/repo-management 112 | 113 | ## 2.10.3 - *2022-02-10* 114 | 115 | - Standardise files with files in sous-chefs/repo-management 116 | 117 | ## 2.10.2 - *2022-02-08* 118 | 119 | - Remove delivery folder 120 | 121 | ## 2.10.1 - *2022-02-01* 122 | 123 | - Update tested platforms 124 | 125 | ## 2.10.0 - *2022-01-24* 126 | 127 | - Improved sorting of Match objects in sshd_config 128 | 129 | ## 2.9.2 - *2021-08-30* 130 | 131 | - Standardise files with files in sous-chefs/repo-management 132 | 133 | ## 2.9.1 - *2021-06-01* 134 | 135 | - Standardise files with files in sous-chefs/repo-management 136 | 137 | ## 2.9.0 - *2021-02-25* 138 | 139 | - Sous Chefs Adoption 140 | - Cookstyle fixes 141 | 142 | ## 2.8.1 (2019-10-03) 143 | 144 | - Expand platform testing to the latest platforms - [@tas50](https://github.com/tas50) 145 | - Move template files out of the default directory - [@tas50](https://github.com/tas50) 146 | - Remove deprecated recipe and long_description metadata - [@tas50](https://github.com/tas50) 147 | - Remove EOL opensuse platform from the metadata - [@tas50](https://github.com/tas50) 148 | - Cookstyle fixes in the library - [@tas50](https://github.com/tas50) 149 | - Add RHEL 8 docker container support - [@tas50](https://github.com/tas50) 150 | 151 | ## 2.8.0 (2019-05-06) 152 | 153 | - This release greatly improves the default attributes on openSUSE/SLES systems 154 | - Update test kitchen config - [@tas50](https://github.com/tas50) 155 | - Add code owners file - [@tas50](https://github.com/tas50) 156 | - Cookstyle fixes - [@tas50](https://github.com/tas50) 157 | - Remove testing of EOL Ubuntu 14.04 - [@tas50](https://github.com/tas50) 158 | - Move the service name to a helper - [@tas50](https://github.com/tas50) 159 | - Fix the roaming test for Amazon Linux 2 - [@tas50](https://github.com/tas50) 160 | - Add opensuseleap 15 testing and Chef 14 testing - [@tas50](https://github.com/tas50) 161 | - Disable roaming on SLES 15 as well - [@tas50](https://github.com/tas50) 162 | - Support generating ssh keys in opensuse 15 containers - [@tas50](https://github.com/tas50) 163 | - Move use_roaming attribute default logic to a helper - [@tas50](https://github.com/tas50) 164 | - Configure the subystem properly on SUSE platform family - [@tas50](https://github.com/tas50) 165 | - Move ssh host key determination to a helper with SLES 15 support - [@tas50](https://github.com/tas50) 166 | - Make sure we return true when supported - [@tas50](https://github.com/tas50) 167 | - Modernize the specs for the new ChefSpec release - [@tas50](https://github.com/tas50) 168 | - Add platform version helpers for readability - [@tas50](https://github.com/tas50) 169 | - Default specs to 18.04 - [@tas50](https://github.com/tas50) 170 | - Avoid FC warning - [@tas50](https://github.com/tas50) 171 | - Disable opensuse 15 testing for now - [@tas50](https://github.com/tas50) 172 | 173 | ## 2.7.1 (2018-11-01) 174 | 175 | - Add support for multiple subsystems 176 | - Use template verify property instead of notify to handle configuration verification so we don't ever template out a non-functional config 177 | 178 | ## 2.7.0 (2018-07-24) 179 | 180 | - Add support for array values under a host hash and added indentation for host values 181 | 182 | ## 2.6.3 (2018-03-19) 183 | 184 | - Support Amazon Linux 2 in containers 185 | 186 | ## 2.6.2 (2018-03-02) 187 | 188 | - Swap Chef 12 testing for Chef 14 testing 189 | - Create the privilege separation directory on debian/ubuntu, which is not always there on Docker images 190 | - Add Ubuntu 18.04 testing 191 | 192 | ## 2.6.1 (2017-11-30) 193 | 194 | - Generate missing ssh keys on amazon linux as well. This impacts containers where ssh keys have not already been generated 195 | 196 | ## 2.6.0 (2017-10-18) 197 | 198 | - Fixed trusted user CA key documentation 199 | - Collapse the smartos hostkey attributes into the centos 6 attributes since they were the same values 200 | - Make sure the hostkey attribute works when RHEL 8 comes out by not constraining the version check too much 201 | - Run sshd-keygen on Fedora / CentOS 7 when host keys are missing. Why would keys be missing? Well if you've never run sshd then you don't have keys on RHEL/Fedora. This happens primarily when you try to Chef a container 202 | - Add Testing on Chef 12 to Travis so we test both 12 and 13 203 | - Move the flat helper methods into an actual library that is properly loaded 204 | 205 | ## 2.5.0 (2017-09-16) 206 | 207 | - Added TrustedUserCAKeys and RevokedKeys support 208 | - Enabled Foodcritic FC024 again 209 | - Generate keys on systemd boxes before validating configs by starting sshd-keygen service if it exists. This prevents failures in docker 210 | - Use multipackage installs to install client/server packages to speed up the chef run 211 | - Add Debian 9 testing in Travis 212 | - Add more platforms to Chefspecs, avoid deprecation warnings, and greatly speed up specs 213 | 214 | ## 2.4.1 (2017-05-22) 215 | 216 | - Fix a bug that resulted in RHEL 6 cert paths being incorrect and expanded testing to check ssh login behavior not just config validation. 217 | 218 | ## 2.4.0 (2017-05-11) 219 | 220 | - Config fixes for the sshd config on Amazon Linux 221 | - Use the correct ssh host keys on RHEL 6 222 | - Use the right sftp subsystem on Debian and Fedora 223 | - Make sure the hostkeys are set on Debian/Ubuntu 224 | 225 | ## 2.3.1 (2017-04-20) 226 | 227 | - Fix AIX service to skip enable since AIX does not support enable 228 | 229 | ## 2.3.0 (2017-04-19) 230 | 231 | - Add basic AIX support 232 | 233 | ## 2.2.0 (2017-04-03) 234 | 235 | - Test with Local Delivery instead of Rake 236 | - Initial Amazon Linux support for Chef 13 237 | 238 | ## 2.1.1 (2017-01-03) 239 | 240 | - Fix for sftp on rhel 241 | - Add all supported SUSE releases to the readme and metadata 242 | 243 | ## 2.1.0 (2016-09-18) 244 | 245 | - Add support for multiple sshd ports. 246 | - Switch to kitchen-dokken for integration testing in Travis CI 247 | - EL7 intentionally lacks of auto-gen'd DSA key 248 | - Fix commented default for ciphers and macs 249 | - Add chef_version metadata 250 | - Remove hostnames from the templates 251 | - Basic Mac OS support 252 | - Avoid node.set deprecation warnings 253 | - Require Chef 12.1+ 254 | - Fix inspec tests 255 | - Remove the service provider logic that isn't necessary in Chef 12 256 | - Set the sftp subsystem on Ubuntu 257 | 258 | ## 2.0.0 (2016-03-18) 259 | 260 | - Don't set the Roaming No directive on RHEL systems before 7.0 as they ship with a sshd release which does not handle this directive 261 | - Depend on the newer iptables cookbook, which bumps the required Chef release for this cookbook to 12.0+ 262 | 263 | ## 1.6.1 (2016-01-20) 264 | 265 | - Restored sshd restarting post config change 266 | 267 | ## 1.6.0 (2016-01-14) 268 | 269 | - Removed the default['openssh']['rootgroup'] attribute and instead use root_group which was introduced in Chef 11.6.0 270 | - UseRoaming no is now set in the client config to resolve CVE-2016-0777 and CVE-2016-0778 271 | - Converted bats integration test to 2 suites of Inspec tests 272 | - Added a libary to sort sshd_config entries while keeping port at the top to prevent sshd from failing to start 273 | 274 | ## 1.5.2 (2015-06-29) 275 | 276 | - Use the complete path to sshd when verifying the config file since sbin may not be in the path 277 | 278 | ## 1.5.0 (2015-06-24) 279 | 280 | - Perform a config syntax check before restarting the sshd so we don't break remote access to hosts 281 | - Add support for Ubuntu 15.04+ with systemd 282 | - Added a chefignore file 283 | - Added Gitter badge for asking questions in a Gitter chat room 284 | 285 | ## 1.4.0 (2015-05-01) 286 | 287 | - 42 - Fixed support for SmartOS 288 | - 46 - Correct ArchLinux service name 289 | - 43 - Correct OpenSSH server package name on RHEL, Fedora 290 | - 31 - Allow included iptables rule to use the same port number if defined in attributes. 291 | - 41 - Fix default recipe order 292 | - 47 - Fix up iptables rule 293 | - 49 - Fixed the print_last_log attribute in the Readme 294 | - Updated Test Kitchen config with all supported platforms 295 | - Updated Test Kitchen / Foodcritic / Rubocop / Berkshelf depedencies in the Gemfile 296 | - Replaced Travis Ruby 1.9.3/2.0.0 testing with 2.1.5/2.2.0 297 | - Resolved all Rubocop warnings 298 | 299 | ## v1.3.4 (2014-04-23) 300 | 301 | - [COOK-4576] - No way to override `AuthorizedKeysFile` 302 | - [COOK-4584] - Use Upstart on Ubuntu 12.04 303 | - [COOK-4585] - skip match block in template if empty or unset 304 | - [COOK-4586] OpenSSH Gentoo support 305 | 306 | ## v1.3.2 307 | 308 | ### Bug 309 | 310 | - **[COOK-3995](https://tickets.chef.io/browse/COOK-3995)** - sshd_config template needs ordering 311 | - **[COOK-3910](https://tickets.chef.io/browse/COOK-3910)** - ssh fails to start in Ubuntu 13.10 312 | - **[COOK-2073](https://tickets.chef.io/browse/COOK-2073)** - Add support for Match block 313 | 314 | ## v1.3.0 315 | 316 | ### Improvement 317 | 318 | - **[COOK-3644](https://tickets.chef.io/browse/COOK-3644)** - Add FreeBSD support 319 | - **[COOK-2517](https://tickets.chef.io/browse/COOK-2517)** - Add hash support 320 | - **[COOK-2000](https://tickets.chef.io/browse/COOK-2000)** - Make mode of sshd_config a configurable option 321 | 322 | ### Bug 323 | 324 | - **[COOK-3558](https://tickets.chef.io/browse/COOK-3558)** - Fix RSA Server Config Options 325 | - **[COOK-3557](https://tickets.chef.io/browse/COOK-3557)** - Fix PubkeyAuthentication option 326 | 327 | ## v1.2.2 328 | 329 | ### Bug 330 | 331 | - **[COOK-3304](https://tickets.chef.io/browse/COOK-3304)** - Fix error setting Dynamic `ListenAddresses` 332 | 333 | ## v1.2.0 334 | 335 | ### Improvement 336 | 337 | - [COOK-2647]: `port_ssh` iptables template has no corresponding recipe 338 | 339 | ## v1.1.4 340 | 341 | - [COOK-2225] - Add platform_family suse 342 | 343 | ## v1.1.2 344 | 345 | - [COOK-1954] - Fix attribute camel case to match `man sshd_config` 346 | - [COOK-1889] - SSH restarting on each chef run due to template changes 347 | 348 | ## v1.1.0 349 | 350 | - [COOK-1663] - Configurable ListenAddress based off list of interface names 351 | - [COOK-1685] - Make default sshd_config value more robust 352 | 353 | ## v1.0.0 354 | 355 | - [COOK-1014] - Templates for ssh(d).conf files. 356 | 357 | ## v0.8.1 358 | 359 | - Current public release 360 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Community Guidelines 2 | 3 | This project follows the Chef Community Guidelines 4 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Please refer to 4 | [https://github.com/chef-cookbooks/community_cookbook_documentation/blob/main/CONTRIBUTING.MD](https://github.com/chef-cookbooks/community_cookbook_documentation/blob/main/CONTRIBUTING.MD) 5 | -------------------------------------------------------------------------------- /Dangerfile: -------------------------------------------------------------------------------- 1 | # Reference: http://danger.systems/reference.html 2 | 3 | # A pull request summary is required. Add a description of the pull request purpose. 4 | # Changelog must be updated for each pull request that changes code. 5 | # Warnings will be issued for: 6 | # Pull request with more than 400 lines of code changed 7 | # Pull reqest that change more than 5 lines without test changes 8 | # Failures will be issued for: 9 | # Pull request without summary 10 | # Pull requests with code changes without changelog entry 11 | 12 | def code_changes? 13 | code = %w(libraries attributes recipes resources files templates) 14 | code.each do |location| 15 | return true unless git.modified_files.grep(/#{location}/).empty? 16 | end 17 | false 18 | end 19 | 20 | def test_changes? 21 | tests = %w(spec test kitchen.yml kitchen.dokken.yml) 22 | tests.each do |location| 23 | return true unless git.modified_files.grep(/#{location}/).empty? 24 | end 25 | false 26 | end 27 | 28 | failure 'Please provide a summary of your Pull Request.' if github.pr_body.length < 10 29 | 30 | warn 'This is a big Pull Request.' if git.lines_of_code > 400 31 | 32 | warn 'This is a Table Flip.' if git.lines_of_code > 2000 33 | 34 | # Require a CHANGELOG entry for non-test changes. 35 | if !git.modified_files.include?('CHANGELOG.md') && code_changes? 36 | failure 'Please include a CHANGELOG entry.' 37 | end 38 | 39 | # Require Major Minor Patch version labels 40 | unless github.pr_labels.grep /minor|major|patch/i 41 | warn 'Please add a release label to this pull request' 42 | end 43 | 44 | # A sanity check for tests. 45 | if git.lines_of_code > 5 && code_changes? && !test_changes? 46 | warn 'This Pull Request is probably missing tests.' 47 | end 48 | -------------------------------------------------------------------------------- /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 | 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # openssh Cookbook 2 | 3 | [![Cookbook Version](https://img.shields.io/cookbook/v/openssh.svg)](https://supermarket.chef.io/cookbooks/openssh) 4 | [![CI State](https://github.com/sous-chefs/openssh/workflows/ci/badge.svg)](https://github.com/sous-chefs/openssh/actions?query=workflow%3Aci) 5 | [![OpenCollective](https://opencollective.com/sous-chefs/backers/badge.svg)](#backers) 6 | [![OpenCollective](https://opencollective.com/sous-chefs/sponsors/badge.svg)](#sponsors) 7 | [![License](https://img.shields.io/badge/License-Apache%202.0-green.svg)](https://opensource.org/licenses/Apache-2.0) 8 | 9 | Installs and configures OpenSSH client and daemon. 10 | 11 | ## Maintainers 12 | 13 | This cookbook is maintained by the Sous Chefs. The Sous Chefs are a community of Chef cookbook maintainers working together to maintain important cookbooks. If you’d like to know more please visit [sous-chefs.org](https://sous-chefs.org/) or come chat with us on the Chef Community Slack in [#sous-chefs](https://chefcommunity.slack.com/messages/C2V7B88SF). 14 | 15 | ## Requirements 16 | 17 | ### Platforms 18 | 19 | - Debian/Ubuntu 20 | - RHEL/CentOS/Scientific/Oracle 21 | - Fedora 22 | - FreeBSD 23 | - Suse Enterprise Linux 24 | - openSUSE / openSUSE leap 25 | - AIX 7.1 26 | - Windows 27 | 28 | ### Chef 29 | 30 | - Chef 12.1+ 31 | 32 | ### Cookbooks 33 | 34 | - iptables 35 | 36 | ## Recipes 37 | 38 | ### default 39 | 40 | Installs openssh packages, manages the sshd config file, configure trusted ca keys, configure revoked keys, and starts/enables the sshd service. 41 | 42 | ### iptables 43 | 44 | Creates an iptables firewall rule to allow inbound SSH connections. 45 | 46 | ## Usage 47 | 48 | Apply the default recipe to the node's run_list to ensure that the openssh packages are installed, sshd is configured, and the service is started and enabled 49 | 50 | ## Attributes List 51 | 52 | The attributes list is dynamically generated, and lines up with the default openssh configs. 53 | 54 | This means anything located in [sshd_config](http://www.openbsd.org/cgi-bin/man.cgi?query=sshd_config&sektion=5) or [ssh_config](http://www.openbsd.org/cgi-bin/man.cgi?query=sshd_config&sektion=5) can be used in your node attributes. 55 | 56 | - If the option can be entered more then once, use an _Array_, otherwise, use a _String_. If the option is host-specific use a `Hash` (please see below for more details). 57 | - Each attribute is stored as ruby case, and converted to camel case for the config file on the fly. 58 | - The current default attributes match the stock `ssh_config` and `sshd_config` provided by openssh. 59 | - The namespace for `sshd_config` is `node['openssh']['server']`. 60 | - Likewise, the namespace for `ssh_config` is `node['openssh']['client']`. 61 | - An attribute can be an `Array`, a `Hash` or a `String`. 62 | - If it is an `Array`, each item in the array will get it's own line in the config file. 63 | - `Hash` attributes are meant to used with `ssh_config` namespace to create host-specific configurations. The keys of the `Hash` will be used as the `Host` entries and their associated entries as the configuration values. 64 | - All the values in openssh are commented out in the `attributes/default.rb` file for a base starting point. 65 | - There is one special attribute name, which is `match`. This is not included in the default template like the others. `node['openssh']['server']['match']` must be a Hash, where the key is the match pattern criteria and the value should be a Hash of normal keywords and values. The same transformations listed above apply to these keywords. To get improved sorting of match items, you can prefix the key with a number. See examples below. 66 | 67 | ## Dynamic ListenAddress 68 | 69 | Pass in a `Hash` of interface names, and IP address type(s) to bind sshd to. This will expand to a list of IP addresses which override the default `node['openssh']['server']['listen_address']` value. 70 | 71 | ## Examples and Common usage 72 | 73 | These can be mixed and matched in roles and attributes. Please note, it is possible to get sshd into a state that it will not run. If this is the case, you will need to login via an alternate method and debug sshd like normal. 74 | 75 | ### No Password logins 76 | 77 | This requires use of identity files to connect 78 | 79 | ```json 80 | "openssh": { 81 | "server": { 82 | "password_authentication": "no" 83 | } 84 | } 85 | ``` 86 | 87 | ### Change sshd Port 88 | 89 | ```json 90 | "openssh": { 91 | "server": { 92 | "port": "14188" 93 | } 94 | } 95 | ``` 96 | 97 | ### Match 98 | 99 | ```json 100 | "openssh": { 101 | "server": { 102 | "match": { 103 | "Address 192.168.1.0/24": { 104 | "password_authentication": "yes" 105 | }, 106 | "Group admins": { 107 | "permit_tunnel": "yes", 108 | "max_sessions": "20" 109 | } 110 | } 111 | } 112 | } 113 | ``` 114 | 115 | ### Match with sorting 116 | 117 | ```json 118 | "openssh": { 119 | "server": { 120 | "match": { 121 | "0 User foobar": { 122 | "force_command": "internal-sftp -d /home/%u -l VERBOSE" 123 | }, 124 | "Group admins": { 125 | "force_command": "internal-sftp -d /home/admins -l VERBOSE" 126 | } 127 | } 128 | } 129 | } 130 | ``` 131 | 132 | ### Enable X Forwarding 133 | 134 | ```json 135 | "openssh": { 136 | "server": { 137 | "x11_forwarding": "yes" 138 | } 139 | } 140 | ``` 141 | 142 | ### Bind to a specific set of address (this example actually binds to all) 143 | 144 | Not to be used with `node['openssh']['listen_interfaces']`. 145 | 146 | ```json 147 | "openssh": { 148 | "server": { 149 | "address_family": "any", 150 | "listen_address": [ "192.168.0.1", "::" ] 151 | } 152 | } 153 | } 154 | ``` 155 | 156 | ### Bind to the addresses tied to a set of interfaces 157 | 158 | ```json 159 | "openssh": { 160 | "listen_interfaces": { 161 | "eth0": "inet", 162 | "eth1": "inet6" 163 | } 164 | } 165 | ``` 166 | 167 | ### Configure Trusted User CA Keys 168 | 169 | ```json 170 | "openssh": { 171 | "ca_keys": [ 172 | "ssh-rsa key... ca_id_1", 173 | "ssh-rsa key... ca_id_2" 174 | ] 175 | } 176 | ``` 177 | 178 | ### Configure Revoked Keys 179 | 180 | ```json 181 | "openssh": { 182 | "server": { 183 | "revoked_keys": [ 184 | "ssh-rsa key... user_key_1", 185 | "ssh-rsa key... user_key_2" 186 | ] 187 | } 188 | } 189 | ``` 190 | 191 | ### Host-specific configurations with hashes 192 | 193 | You can use a `Hash` with `node['openssh']['client']` to configure different values for different hosts. 194 | 195 | ```json 196 | "client": { 197 | "*": { 198 | "g_s_s_a_p_i_authentication": "yes", 199 | "send_env": "LANG LC_*", 200 | "hash_known_hosts": "yes" 201 | }, 202 | "localhost": { 203 | "user_known_hosts_file": "/dev/null", 204 | "strict_host_key_checking": "no" 205 | }, 206 | "127.0.0.1": { 207 | "user_known_hosts_file": "/dev/null", 208 | "strict_host_key_checking": "no" 209 | }, 210 | "other*": { 211 | "user_known_hosts_file": "/dev/null", 212 | "strict_host_key_checking": "no" 213 | } 214 | } 215 | ``` 216 | 217 | The keys are used as values with the `Host` entries. So, the configuration fragment shown above generates: 218 | 219 | ```text 220 | Host * 221 | SendEnv LANG LC_* 222 | HashKnownHosts yes 223 | GSSAPIAuthentication yes 224 | Host localhost 225 | StrictHostKeyChecking no 226 | UserKnownHostsFile /dev/null 227 | Host 127.0.0.1 228 | StrictHostKeyChecking no 229 | UserKnownHostsFile /dev/null 230 | Host other* 231 | StrictHostKeyChecking no 232 | UserKnownHostsFile /dev/null 233 | ``` 234 | 235 | ### SSH Subsystems 236 | 237 | Configure multiple SSH subsystems (e.g. sftp, netconf): 238 | 239 | ```json 240 | "openssh": { 241 | "server": { 242 | "subsystem": { 243 | "sftp": "/usr/lib/openssh/sftp-server", 244 | "appX": "/usr/sbin/appX" 245 | } 246 | } 247 | } 248 | ``` 249 | 250 | Former declaration of single subsystem: 251 | 252 | ```json 253 | "openssh": { 254 | "server": { 255 | "subsystem": "sftp /usr/lib/openssh/sftp-server" 256 | } 257 | } 258 | ``` 259 | 260 | ## Contributors 261 | 262 | This project exists thanks to all the people who [contribute.](https://opencollective.com/sous-chefs/contributors.svg?width=890&button=false) 263 | 264 | ### Backers 265 | 266 | Thank you to all our backers! 267 | 268 | ![https://opencollective.com/sous-chefs#backers](https://opencollective.com/sous-chefs/backers.svg?width=600&avatarHeight=40) 269 | 270 | ### Sponsors 271 | 272 | Support this project by becoming a sponsor. Your logo will show up here with a link to your website. 273 | 274 | ![https://opencollective.com/sous-chefs/sponsor/0/website](https://opencollective.com/sous-chefs/sponsor/0/avatar.svg?avatarHeight=100) 275 | ![https://opencollective.com/sous-chefs/sponsor/1/website](https://opencollective.com/sous-chefs/sponsor/1/avatar.svg?avatarHeight=100) 276 | ![https://opencollective.com/sous-chefs/sponsor/2/website](https://opencollective.com/sous-chefs/sponsor/2/avatar.svg?avatarHeight=100) 277 | ![https://opencollective.com/sous-chefs/sponsor/3/website](https://opencollective.com/sous-chefs/sponsor/3/avatar.svg?avatarHeight=100) 278 | ![https://opencollective.com/sous-chefs/sponsor/4/website](https://opencollective.com/sous-chefs/sponsor/4/avatar.svg?avatarHeight=100) 279 | ![https://opencollective.com/sous-chefs/sponsor/5/website](https://opencollective.com/sous-chefs/sponsor/5/avatar.svg?avatarHeight=100) 280 | ![https://opencollective.com/sous-chefs/sponsor/6/website](https://opencollective.com/sous-chefs/sponsor/6/avatar.svg?avatarHeight=100) 281 | ![https://opencollective.com/sous-chefs/sponsor/7/website](https://opencollective.com/sous-chefs/sponsor/7/avatar.svg?avatarHeight=100) 282 | ![https://opencollective.com/sous-chefs/sponsor/8/website](https://opencollective.com/sous-chefs/sponsor/8/avatar.svg?avatarHeight=100) 283 | ![https://opencollective.com/sous-chefs/sponsor/9/website](https://opencollective.com/sous-chefs/sponsor/9/avatar.svg?avatarHeight=100) 284 | -------------------------------------------------------------------------------- /TESTING.md: -------------------------------------------------------------------------------- 1 | # Testing 2 | 3 | Please refer to [the community cookbook documentation on testing](https://github.com/chef-cookbooks/community_cookbook_documentation/blob/main/TESTING.MD). 4 | -------------------------------------------------------------------------------- /attributes/default.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook:: openssh 3 | # Attributes:: default 4 | # 5 | # Author:: Ernie Brodeur 6 | # Copyright:: 2008-2019, Chef Software, 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 | # Attributes are commented out using the default config file values. 21 | # Uncomment the ones you need, or set attributes in a role. 22 | # 23 | 24 | default['openssh']['package_name'] = case node['platform_family'] 25 | when 'rhel', 'fedora', 'amazon' 26 | %w(openssh-clients openssh-server) 27 | when 'arch', 'suse', 'gentoo' 28 | %w(openssh) 29 | when 'freebsd', 'smartos', 'mac_os_x', 'aix', 'windows' 30 | %w() 31 | else 32 | %w(openssh-client openssh-server) 33 | end 34 | 35 | default['openssh']['config_mode'] = if platform_family?('rhel', 'fedora', 'amazon', 'aix') 36 | '0600' 37 | else 38 | '0644' 39 | end 40 | 41 | # trusted ca keys 42 | default['openssh']['ca_keys'] = %w() 43 | # revoked keys 44 | default['openssh']['revoked_keys'] = %w() 45 | 46 | # ssh config group 47 | default['openssh']['client']['host'] = '*' 48 | 49 | # Workaround for CVE-2016-0777 and CVE-2016-0778. 50 | # Older versions of RHEL should not receive this directive 51 | default['openssh']['client']['use_roaming'] = 'no' 52 | # default['openssh']['client']['forward_agent'] = 'no' 53 | # default['openssh']['client']['forward_x11'] = 'no' 54 | # default['openssh']['client']['rhosts_rsa_authentication'] = 'no' 55 | # default['openssh']['client']['rsa_authentication'] = 'yes' 56 | # default['openssh']['client']['password_authentication'] = 'yes' 57 | # default['openssh']['client']['host_based_authentication'] = 'no' 58 | # default['openssh']['client']['gssapi_authentication'] = 'no' 59 | # default['openssh']['client']['gssapi_delegate_credentials'] = 'no' 60 | # default['openssh']['client']['batch_mode'] = 'no' 61 | # default['openssh']['client']['check_host_ip'] = 'yes' 62 | # default['openssh']['client']['address_family'] = 'any' 63 | # default['openssh']['client']['connect_timeout'] = '0' 64 | # default['openssh']['client']['strict_host_key_checking'] = 'ask' 65 | # default['openssh']['client']['identity_file'] = '~/.ssh/identity' 66 | # default['openssh']['client']['identity_file_rsa'] = '~/.ssh/id_rsa' 67 | # default['openssh']['client']['identity_file_dsa'] = '~/.ssh/id_dsa' 68 | # default['openssh']['client']['port'] = '22' 69 | # default['openssh']['client']['protocol'] = [ '2 1' ] 70 | # default['openssh']['client']['cipher'] = '3des' 71 | # default['openssh']['client']['ciphers'] = 'aes128-ctr,aes192-ctr,aes256-ctr,arcfour256,arcfour128,aes128-cbc,3des-cbc' 72 | # default['openssh']['client']['macs'] = 'hmac-md5,hmac-sha1,umac-64@openssh.com,hmac-ripemd160' 73 | # default['openssh']['client']['escape_char'] = '~' 74 | # default['openssh']['client']['tunnel'] = 'no' 75 | # default['openssh']['client']['tunnel_device'] = 'any:any' 76 | # default['openssh']['client']['permit_local_command'] = 'no' 77 | # default['openssh']['client']['visual_host_key'] = 'no' 78 | # default['openssh']['client']['proxy_command'] = 'ssh -q -W %h:%p gateway.example.com' 79 | # sshd config group 80 | # default['openssh']['server']['port'] = '22' 81 | # default['openssh']['server']['address_family'] = 'any' 82 | # default['openssh']['server']['listen_address'] = [ '0.0.0.0 ::' ] 83 | # default['openssh']['server']['protocol'] = '2' 84 | # default['openssh']['server']['host_key_v1'] = '/etc/ssh/ssh_host_key' 85 | # default['openssh']['server']['host_key_rsa'] = '/etc/ssh/ssh_host_rsa_key' 86 | # default['openssh']['server']['host_key_dsa'] = '/etc/ssh/ssh_host_dsa_key' 87 | 88 | # modern platforms don't generate DSA keys by default and older platforms don't support 89 | # ed25519 keys, but if you tell sshd to look for all of them it will spam syslog with 90 | # whatever it can't find so choose a sane set of supported keys based on version using 91 | # a helper method. 92 | default['openssh']['server']['host_key'] = supported_ssh_host_keys 93 | 94 | # default['openssh']['server']['host_key_ecdsa'] = '/etc/ssh/ssh_host_ecdsa_key' 95 | # default['openssh']['server']['key_regeneration_interval'] = '1h' 96 | # default['openssh']['server']['server_key_bits'] = '1024' 97 | # default['openssh']['server']['syslog_facility'] = 'AUTH' 98 | # default['openssh']['server']['log_level'] = 'INFO' 99 | # default['openssh']['server']['login_grace_time'] = '2m' 100 | # default['openssh']['server']['permit_root_login'] = 'yes' 101 | # default['openssh']['server']['strict_modes'] = 'yes' 102 | # default['openssh']['server']['max_auth_tries'] = '6' 103 | # default['openssh']['server']['max_sessions'] = '10' 104 | # default['openssh']['server']['r_s_a_authentication'] = 'yes' 105 | # default['openssh']['server']['pubkey_authentication'] = 'yes' 106 | # default['openssh']['server']['authorized_keys_file'] = '%h/.ssh/authorized_keys' 107 | # default['openssh']['server']['rhosts_r_s_a_authentication'] = 'no' 108 | # default['openssh']['server']['host_based_authentication'] = 'no' 109 | # default['openssh']['server']['ignore_user_known_hosts'] = 'no' 110 | # default['openssh']['server']['ignore_rhosts'] = 'yes' 111 | default['openssh']['server']['password_authentication'] = 'no' if platform?('amazon') 112 | # default['openssh']['server']['permit_empty_passwords'] = 'no' 113 | default['openssh']['server']['challenge_response_authentication'] = 'no' 114 | # default['openssh']['server']['kerberos_authentication'] = 'no' 115 | # default['openssh']['server']['kerberos_or_localpasswd'] = 'yes' 116 | # default['openssh']['server']['kerberos_ticket_cleanup'] = 'yes' 117 | # default['openssh']['server']['kerberos_get_afs_token'] = 'no' 118 | # default['openssh']['server']['gssapi_authentication'] = 'no' 119 | # default['openssh']['server']['gssapi_clean_up_credentials'] = 'yes' 120 | default['openssh']['server']['use_p_a_m'] = 'yes' unless platform_family?('smartos', 'windows') 121 | # default['openssh']['server']['allow_agent_forwarding'] = 'yes' 122 | # default['openssh']['server']['allow_tcp_forwarding'] = 'yes' 123 | # default['openssh']['server']['gateway_ports'] = 'no' 124 | # default['openssh']['server']['x11_forwarding'] = 'no' 125 | # default['openssh']['server']['x11_display_offset'] = '10' 126 | # default['openssh']['server']['x11_use_localhost'] = 'yes' 127 | # default['openssh']['server']['print_motd'] = 'yes' 128 | # default['openssh']['server']['print_last_log'] = 'yes' 129 | # default['openssh']['server']['t_c_p_keep_alive'] = 'yes' 130 | # default['openssh']['server']['use_login'] = 'no' 131 | # default['openssh']['server']['use_privilege_separation'] = 'yes' 132 | # default['openssh']['server']['permit_user_environment'] = 'no' 133 | # default['openssh']['server']['compression'] = 'delayed' 134 | # default['openssh']['server']['client_alive_interval'] = '0' 135 | # default['openssh']['server']['client_alive_count_max'] = '3' 136 | # default['openssh']['server']['use_dns'] = 'yes' 137 | # default['openssh']['server']['pid_file'] = '/var/run/sshd.pid' 138 | # default['openssh']['server']['max_startups'] = '10' 139 | # default['openssh']['server']['permit_tunnel'] = 'no' 140 | # default['openssh']['server']['chroot_directory'] = 'none' 141 | # default['openssh']['server']['banner'] = 'none' 142 | # default['openssh']['server']['subsystem'] = 'sftp /usr/libexec/sftp-server' 143 | default['openssh']['server']['trusted_user_c_a_keys'] = if platform_family?('windows') 144 | join_path(base_ssh_config_dir, 'ca_userkeys.pub') 145 | else 146 | '/etc/ssh/ca_keys' 147 | end 148 | default['openssh']['server']['revoked_keys'] = if platform_family?('windows') 149 | join_path(base_ssh_config_dir, 'revoked_keys') 150 | else 151 | '/etc/ssh/revoked_keys' 152 | end 153 | default['openssh']['server']['subsystem'] = 'sftp /usr/libexec/openssh/sftp-server' if platform_family?('rhel', 'amazon', 'fedora') 154 | default['openssh']['server']['subsystem'] = 'sftp /usr/lib/openssh/sftp-server' if platform_family?('debian') 155 | default['openssh']['server']['subsystem'] = 'sftp /usr/lib/ssh/sftp-server' if platform_family?('suse') 156 | default['openssh']['server']['match'] = {} 157 | -------------------------------------------------------------------------------- /chefignore: -------------------------------------------------------------------------------- 1 | # Put files/directories that should be ignored in this file when uploading 2 | # to a Chef Infra Server or Supermarket. 3 | # Lines that start with '# ' are comments. 4 | 5 | # OS generated files # 6 | ###################### 7 | .DS_Store 8 | ehthumbs.db 9 | Icon? 10 | nohup.out 11 | Thumbs.db 12 | .envrc 13 | 14 | # EDITORS # 15 | ########### 16 | .#* 17 | .project 18 | .settings 19 | *_flymake 20 | *_flymake.* 21 | *.bak 22 | *.sw[a-z] 23 | *.tmproj 24 | *~ 25 | \#* 26 | REVISION 27 | TAGS* 28 | tmtags 29 | .vscode 30 | .editorconfig 31 | 32 | ## COMPILED ## 33 | ############## 34 | *.class 35 | *.com 36 | *.dll 37 | *.exe 38 | *.o 39 | *.pyc 40 | *.so 41 | */rdoc/ 42 | a.out 43 | mkmf.log 44 | 45 | # Testing # 46 | ########### 47 | .circleci/* 48 | .codeclimate.yml 49 | .delivery/* 50 | .foodcritic 51 | .kitchen* 52 | .mdlrc 53 | .overcommit.yml 54 | .rspec 55 | .rubocop.yml 56 | .travis.yml 57 | .watchr 58 | .yamllint 59 | azure-pipelines.yml 60 | Dangerfile 61 | examples/* 62 | features/* 63 | Guardfile 64 | kitchen*.yml 65 | mlc_config.json 66 | Procfile 67 | Rakefile 68 | spec/* 69 | test/* 70 | 71 | # SCM # 72 | ####### 73 | .git 74 | .gitattributes 75 | .gitconfig 76 | .github/* 77 | .gitignore 78 | .gitkeep 79 | .gitmodules 80 | .svn 81 | */.bzr/* 82 | */.git 83 | */.hg/* 84 | */.svn/* 85 | 86 | # Berkshelf # 87 | ############# 88 | Berksfile 89 | Berksfile.lock 90 | cookbooks/* 91 | tmp 92 | 93 | # Bundler # 94 | ########### 95 | vendor/* 96 | Gemfile 97 | Gemfile.lock 98 | 99 | # Policyfile # 100 | ############## 101 | Policyfile.rb 102 | Policyfile.lock.json 103 | 104 | # Documentation # 105 | ############# 106 | CODE_OF_CONDUCT* 107 | CONTRIBUTING* 108 | documentation/* 109 | TESTING* 110 | UPGRADING* 111 | 112 | # Vagrant # 113 | ########### 114 | .vagrant 115 | Vagrantfile 116 | -------------------------------------------------------------------------------- /documentation/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sous-chefs/openssh/a28f97855fac76ccfc3e2eb9a94f43aa69d0d025/documentation/.gitkeep -------------------------------------------------------------------------------- /kitchen.dokken.yml: -------------------------------------------------------------------------------- 1 | driver: 2 | name: dokken 3 | privileged: true 4 | chef_version: <%= ENV['CHEF_VERSION'] || 'current' %> 5 | 6 | transport: { name: dokken } 7 | provisioner: { name: dokken } 8 | 9 | platforms: 10 | - name: almalinux-8 11 | driver: 12 | image: dokken/almalinux-8 13 | pid_one_command: /usr/lib/systemd/systemd 14 | 15 | - name: almalinux-9 16 | driver: 17 | image: dokken/almalinux-9 18 | pid_one_command: /usr/lib/systemd/systemd 19 | 20 | - name: almalinux-10 21 | driver: 22 | image: dokken/almalinux-10 23 | pid_one_command: /usr/lib/systemd/systemd 24 | 25 | - name: amazonlinux-2023 26 | driver: 27 | image: dokken/amazonlinux-2023 28 | pid_one_command: /usr/lib/systemd/systemd 29 | 30 | - name: centos-stream-9 31 | driver: 32 | image: dokken/centos-stream-9 33 | pid_one_command: /usr/lib/systemd/systemd 34 | 35 | - name: centos-stream-10 36 | driver: 37 | image: dokken/centos-stream-10 38 | pid_one_command: /usr/lib/systemd/systemd 39 | 40 | - name: debian-11 41 | driver: 42 | image: dokken/debian-11 43 | pid_one_command: /bin/systemd 44 | 45 | - name: debian-12 46 | driver: 47 | image: dokken/debian-12 48 | pid_one_command: /bin/systemd 49 | 50 | - name: fedora-latest 51 | driver: 52 | image: dokken/fedora-latest 53 | pid_one_command: /usr/lib/systemd/systemd 54 | 55 | - name: opensuse-leap-15 56 | driver: 57 | image: dokken/opensuse-leap-15 58 | pid_one_command: /usr/lib/systemd/systemd 59 | 60 | - name: oraclelinux-8 61 | driver: 62 | image: dokken/oraclelinux-8 63 | pid_one_command: /usr/lib/systemd/systemd 64 | 65 | - name: oraclelinux-9 66 | driver: 67 | image: dokken/oraclelinux-9 68 | pid_one_command: /usr/lib/systemd/systemd 69 | 70 | - name: rockylinux-8 71 | driver: 72 | image: dokken/rockylinux-8 73 | pid_one_command: /usr/lib/systemd/systemd 74 | 75 | - name: rockylinux-9 76 | driver: 77 | image: dokken/rockylinux-9 78 | pid_one_command: /usr/lib/systemd/systemd 79 | 80 | - name: ubuntu-20.04 81 | driver: 82 | image: dokken/ubuntu-20.04 83 | pid_one_command: /bin/systemd 84 | 85 | - name: ubuntu-22.04 86 | driver: 87 | image: dokken/ubuntu-22.04 88 | pid_one_command: /bin/systemd 89 | 90 | - name: ubuntu-24.04 91 | driver: 92 | image: dokken/ubuntu-24.04 93 | pid_one_command: /bin/systemd 94 | -------------------------------------------------------------------------------- /kitchen.exec.yml: -------------------------------------------------------------------------------- 1 | --- 2 | driver: { name: exec } 3 | transport: { name: exec } 4 | 5 | platforms: 6 | - name: macos-latest 7 | - name: windows-latest 8 | -------------------------------------------------------------------------------- /kitchen.global.yml: -------------------------------------------------------------------------------- 1 | --- 2 | provisioner: 3 | name: chef_infra 4 | product_name: chef 5 | product_version: <%= ENV['CHEF_VERSION'] || 'latest' %> 6 | channel: stable 7 | install_strategy: once 8 | chef_license: accept 9 | enforce_idempotency: <%= ENV['ENFORCE_IDEMPOTENCY'] || true %> 10 | multiple_converge: <%= ENV['MULTIPLE_CONVERGE'] || 2 %> 11 | deprecations_as_errors: true 12 | log_level: <%= ENV['CHEF_LOG_LEVEL'] || 'auto' %> 13 | 14 | verifier: 15 | name: inspec 16 | 17 | platforms: 18 | - name: almalinux-8 19 | - name: almalinux-9 20 | - name: amazonlinux-2023 21 | - name: centos-stream-9 22 | - name: debian-11 23 | - name: debian-12 24 | - name: fedora-latest 25 | - name: opensuse-leap-15 26 | - name: oraclelinux-8 27 | - name: oraclelinux-9 28 | - name: rockylinux-8 29 | - name: rockylinux-9 30 | - name: ubuntu-20.04 31 | - name: ubuntu-22.04 32 | - name: ubuntu-24.04 33 | -------------------------------------------------------------------------------- /kitchen.yml: -------------------------------------------------------------------------------- 1 | --- 2 | driver: 3 | name: vagrant 4 | 5 | provisioner: 6 | name: chef_zero 7 | product_name: chef 8 | deprecations_as_errors: true 9 | chef_license: accept-no-persist 10 | 11 | verifier: 12 | name: inspec 13 | 14 | platforms: 15 | - name: amazonlinux-2 16 | - name: centos-7 17 | - name: centos-stream-8 18 | - name: debian-10 19 | - name: debian-10 20 | - name: freebsd-12 21 | - name: fedora-latest 22 | - name: opensuse-leap-15 23 | - name: ubuntu-18.04 24 | - name: ubuntu-20.04 25 | - name: macosx-10.15 26 | driver_config: 27 | box: tas50/macos_10.15 28 | provider: vmware_desktop 29 | - name: windows-2016 30 | driver_config: 31 | box: tas50/windows_2016 32 | - name: windows-2019 33 | driver_config: 34 | box: tas50/windows_2019 35 | 36 | suites: 37 | - name: default 38 | run_list: openssh::default 39 | excludes: 40 | - windows-2016 41 | - windows-2019 42 | - windows-latest 43 | - name: iptables 44 | run_list: 45 | - openssh::default 46 | - openssh::iptables 47 | excludes: 48 | - almalinux-8 49 | - centos-7 50 | - centos-stream-8 51 | - macosx-10.15 52 | - opensuse-leap-15 53 | - rockylinux-8 54 | - windows-2016 55 | - windows-2019 56 | - windows-latest 57 | - name: windows-default 58 | run_list: openssh::default 59 | includes: 60 | - windows-2016 61 | - windows-2019 62 | - windows-latest 63 | -------------------------------------------------------------------------------- /libraries/helpers.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook:: openssh 3 | # library:: helpers 4 | # 5 | # Copyright:: 2016-2019, Chef Software, 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 | # Attributes are commented out using the default config file values. 20 | # Uncomment the ones you need, or set attributes in a role. 21 | # 22 | 23 | module Openssh 24 | module Helpers 25 | def openssh_server_options 26 | options = node['openssh']['server'].sort.reject { |key, _value| key == 'port' || key == 'match' || key == 'subsystem' } 27 | unless node['openssh']['server']['port'].nil? 28 | port = node['openssh']['server'].select { |key| key == 'port' }.to_a 29 | options.unshift(*port) 30 | end 31 | options 32 | end 33 | 34 | # are we on a platform that has the sshd-keygen command. It's a redhat-ism so it's a limited number 35 | def keygen_platform? 36 | return true if platform?('amazon', 'fedora') 37 | return true if rhel_7_plus? 38 | opensuse_15_plus? 39 | end 40 | 41 | # are any of the host keys defined in the attribute missing from the filesystem 42 | def sshd_host_keys_missing? 43 | !node['openssh']['server']['host_key'].all? { |f| ::File.exist?(f) } 44 | end 45 | 46 | def openssh_service_name 47 | if platform_family?('rhel', 'fedora', 'suse', 'freebsd', 'gentoo', 'arch', 'mac_os_x', 'amazon', 'aix', 'windows') 48 | 'sshd' 49 | else 50 | 'ssh' 51 | end 52 | end 53 | 54 | def base_ssh_config_dir 55 | platform_family?('windows') ? 'C:\\ProgramData\\ssh' : '/etc/ssh' 56 | end 57 | 58 | def base_ssh_bin_dir 59 | platform_family?('windows') ? 'C:\\Program Files\\OpenSSH' : '/usr/sbin/' 60 | end 61 | 62 | def join_path(*path) 63 | Chef::Util::PathHelper.cleanpath(::File.join(path)) 64 | end 65 | 66 | def supported_ssh_host_keys 67 | keys = [join_path(base_ssh_config_dir, 'ssh_host_rsa_key'), join_path(base_ssh_config_dir, 'ssh_host_ecdsa_key')] 68 | keys << join_path(base_ssh_config_dir, 'ssh_host_dsa_key') if platform_family?('smartos', 'suse', 'windows') 69 | keys << join_path(base_ssh_config_dir, 'ssh_host_ed25519_key') if rhel_7_plus? || platform?('amazon', 'fedora') || platform_family?('debian', 'windows') || opensuse_15_plus? 70 | keys 71 | end 72 | 73 | def rhel_7_plus? 74 | platform_family?('rhel') && node['platform_version'].to_i >= 7 75 | end 76 | 77 | def opensuse_15_plus? 78 | platform_family?('suse') && node['platform_version'].to_i >= 15 && node['platform_version'].to_i < 42 79 | end 80 | end 81 | end 82 | 83 | Chef::Resource.include ::Openssh::Helpers 84 | Chef::DSL::Recipe.include ::Openssh::Helpers 85 | Chef::Node.include ::Openssh::Helpers 86 | -------------------------------------------------------------------------------- /metadata.rb: -------------------------------------------------------------------------------- 1 | name 'openssh' 2 | maintainer 'Sous Chefs' 3 | maintainer_email 'help@sous-chefs.org' 4 | license 'Apache-2.0' 5 | description 'Installs and configures OpenSSH client and daemon' 6 | version '2.11.15' 7 | source_url 'https://github.com/sous-chefs/openssh' 8 | issues_url 'https://github.com/sous-chefs/openssh/issues' 9 | chef_version '>= 12.15' 10 | 11 | supports 'aix' 12 | supports 'amazon' 13 | supports 'arch' 14 | supports 'centos' 15 | supports 'fedora' 16 | supports 'freebsd' 17 | supports 'opensuseleap' 18 | supports 'oracle' 19 | supports 'redhat' 20 | supports 'scientific' 21 | supports 'smartos' 22 | supports 'suse' 23 | supports 'ubuntu' 24 | supports 'windows' 25 | supports 'zlinux' 26 | 27 | depends 'iptables', '>= 7.0' 28 | -------------------------------------------------------------------------------- /mlc_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "ignorePatterns": [ 3 | { 4 | "pattern": "^https://tickets.chef.io/browse/COOK" 5 | }, 6 | { 7 | "pattern": "^https://opencollective.com/sous-chefs/contributors.svg" 8 | } 9 | ] 10 | } -------------------------------------------------------------------------------- /recipes/default.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook:: openssh 3 | # Recipe:: default 4 | # 5 | # Copyright:: 2008-2019, Chef Software, 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 | 20 | def listen_addr_for(interface, type) 21 | interface_node = node['network']['interfaces'][interface]['addresses'] 22 | interface_node.select { |_address, data| data['family'] == type }.keys[0] 23 | end 24 | 25 | package node['openssh']['package_name'] unless node['openssh']['package_name'].empty? 26 | 27 | template join_path(base_ssh_config_dir, 'ssh_config') do 28 | source 'ssh_config.erb' 29 | mode '0644' unless platform_family?('windows') 30 | owner 'root' unless platform_family?('windows') 31 | group node['root_group'] 32 | end 33 | 34 | if node['openssh']['listen_interfaces'] 35 | listen_addresses = [].tap do |a| 36 | node['openssh']['listen_interfaces'].each_pair do |interface, type| 37 | a << listen_addr_for(interface, type) 38 | end 39 | end 40 | 41 | node.default['openssh']['server']['listen_address'] = listen_addresses 42 | end 43 | 44 | template 'sshd_ca_keys_file' do 45 | source 'ca_keys.erb' 46 | path node['openssh']['server']['trusted_user_c_a_keys'] 47 | mode node['openssh']['config_mode'] unless platform_family?('windows') 48 | owner 'root' unless platform_family?('windows') 49 | group node['root_group'] 50 | end 51 | 52 | template 'sshd_revoked_keys_file' do 53 | source 'revoked_keys.erb' 54 | path node['openssh']['server']['revoked_keys'] 55 | mode node['openssh']['config_mode'] unless platform_family?('windows') 56 | owner 'root' unless platform_family?('windows') 57 | group node['root_group'] 58 | end 59 | 60 | # this will only execute on RHEL / Fedora systems where sshd has never been started 61 | # 99.99% of the time this is going to be a docker container 62 | if keygen_platform? && sshd_host_keys_missing? 63 | if platform_family?('fedora', 'rhel', 'amazon') && node['platform_version'].to_i >= 8 # fedora, RHEL 8+, Amazonlinux 2023+ 64 | node['openssh']['server']['host_key'].each do |key| 65 | keytype = key.split('_')[-2] 66 | execute "/usr/libexec/openssh/sshd-keygen #{keytype}" 67 | end 68 | elsif platform_family?('rhel', 'amazon') # RHEL < 8 or Amazon Linux 2 69 | execute '/usr/sbin/sshd-keygen' 70 | elsif platform_family?('suse') 71 | execute '/usr/sbin/sshd-gen-keys-start' 72 | end 73 | end 74 | 75 | # we probably need this on multiple platforms but we 100% need it on debian based platforms 76 | if platform_family?('debian') 77 | dir = platform?('ubuntu') && node['platform_version'].to_i >= 18 ? '/run/sshd' : '/var/run/sshd' 78 | directory dir 79 | end 80 | 81 | default_sshd_path = if platform_family?('windows') 82 | "\"#{join_path(base_ssh_bin_dir, 'sshd.exe')}\"" 83 | else 84 | join_path(base_ssh_bin_dir, 'sshd') 85 | end 86 | 87 | template join_path(base_ssh_config_dir, 'sshd_config') do 88 | source 'sshd_config.erb' 89 | mode node['openssh']['config_mode'] unless platform_family?('windows') 90 | owner 'root' unless platform_family?('windows') 91 | group node['root_group'] 92 | variables(options: openssh_server_options) 93 | verify "#{default_sshd_path} -t -f %{path}" 94 | notifies :restart, 'service[ssh]' 95 | end 96 | 97 | service 'ssh' do 98 | service_name openssh_service_name 99 | supports value_for_platform_family( 100 | %w(debian rhel fedora aix) => [:restart, :reload, :status], 101 | %w(arch) => [:restart], 102 | 'default' => [:restart, :reload] 103 | ) 104 | action value_for_platform_family( 105 | %w(aix) => [:start], 106 | 'default' => [:enable, :start] 107 | ) 108 | end 109 | -------------------------------------------------------------------------------- /recipes/iptables.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook:: openssh 3 | # Recipe:: iptables 4 | # 5 | # Copyright:: 2013-2019, Chef Software, 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 | 20 | # The iptables cookbook doesn't support Windows 21 | return if platform_family?('windows') 22 | 23 | iptables_packages 'install iptables' 24 | iptables_service 'start iptables' 25 | 26 | sshd_port = if node['openssh'].attribute?('server') && node['openssh']['server'].attribute?('port') 27 | if node['openssh']['server']['port'].is_a?(Array) 28 | node['openssh']['server']['port'] 29 | else 30 | [node['openssh']['server']['port']] 31 | end 32 | else 33 | [22] 34 | end 35 | 36 | sshd_port.each do |port| 37 | iptables_rule 'ssh_input' do 38 | chain :INPUT 39 | jump 'ACCEPT' 40 | protocol 'tcp' 41 | match 'tcp' 42 | extra_options "--dport #{port}" 43 | end 44 | 45 | iptables_rule 'ssh_output' do 46 | chain :OUTPUT 47 | jump 'ACCEPT' 48 | protocol 'tcp' 49 | match 'tcp' 50 | extra_options "--sport #{port}" 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": ["config:base"], 4 | "packageRules": [ 5 | { 6 | "groupName": "Actions", 7 | "matchUpdateTypes": ["minor", "patch", "pin"], 8 | "automerge": true, 9 | "addLabels": ["Release: Patch", "Skip: Announcements"] 10 | }, 11 | { 12 | "groupName": "Actions", 13 | "matchUpdateTypes": ["major"], 14 | "automerge": false, 15 | "addLabels": ["Release: Patch", "Skip: Announcements"] 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'chefspec' 2 | require 'chefspec/berkshelf' 3 | 4 | RSpec.configure do |config| 5 | config.color = true # Use color in STDOUT 6 | config.formatter = :documentation # Use the specified formatter 7 | config.log_level = :error # Avoid deprecation notice SPAM 8 | config.platform = 'ubuntu' 9 | config.version = '18.04' 10 | end 11 | 12 | def join_path(*path) 13 | Chef::Util::PathHelper.cleanpath(::File.join(path)) 14 | end 15 | -------------------------------------------------------------------------------- /spec/unit/recipes/default_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'openssh::default' do 4 | it 'writes the ssh_config' do 5 | template = chef_run.template('/etc/ssh/ssh_config') 6 | expect(template).to be 7 | expect(template.mode).to eq('0644') 8 | expect(template.owner).to eq('root') 9 | expect(template.group).to eq('root') 10 | end 11 | 12 | describe 'sshd_config' do 13 | it 'writes the sshd_config' do 14 | template = chef_run.template('/etc/ssh/sshd_config') 15 | expect(template).to be 16 | expect(template.mode).to eq('0644') 17 | expect(template.owner).to eq('root') 18 | expect(template.group).to eq('root') 19 | end 20 | 21 | it 'allow legacy default AuthorizedKeysFile behavior' do 22 | expect(chef_run).to_not render_file('/etc/ssh/sshd_config').with_content(/AuthorizedKeysFile.*/) 23 | end 24 | 25 | it 'writes a match group block' do 26 | chef_run.node.override['openssh']['server']['match'] = { 'Group admins' => { 'permit_tunnel' => 'yes' } } 27 | chef_run.converge('openssh::default') 28 | expect(chef_run).to render_file('/etc/ssh/sshd_config').with_content(/Match Group admins\n\s\sPermitTunnel yes/) 29 | end 30 | 31 | it 'skips match group block' do 32 | chef_run.node.override['openssh']['server']['match'] = {} 33 | chef_run.converge('openssh::default') 34 | expect(chef_run).to_not render_file('/etc/ssh/sshd_config').with_content(/Match Group admins\n\s\sPermitTunnel yes/) 35 | end 36 | 37 | it 'write multiple SSH subsystems from Hash' do 38 | chef_run.node.override['openssh']['server']['subsystem'] = { 39 | sftp: '/usr/lib/openssh/sftp-server', 40 | test: '/my/subsystem/bin', 41 | } 42 | chef_run.converge('openssh::default') 43 | expect(chef_run).to render_file('/etc/ssh/sshd_config').with_content(%r{Subsystem sftp \/usr\/lib\/openssh\/sftp-server\nSubsystem test.*}) 44 | end 45 | 46 | it 'skips subsystems block' do 47 | chef_run.node.override['openssh']['server']['subsystem'] = {} 48 | chef_run.converge('openssh::default') 49 | expect(chef_run).to_not render_file('/etc/ssh/sshd_config').with_content(/^Subsystem?/) 50 | end 51 | 52 | it 'supports legacy subsystem format' do 53 | chef_run.node.override['openssh']['server']['subsystem'] = 'sftp /usr/lib/openssh/sftp-server' 54 | chef_run.converge('openssh::default') 55 | expect(chef_run).to render_file('/etc/ssh/sshd_config').with_content(%r{Subsystem sftp \/usr\/lib\/openssh\/sftp-server\n}) 56 | end 57 | 58 | it 'allows subsystem from Array attribute' do 59 | chef_run.node.override['openssh']['server']['subsystem'] = [ 60 | 'sftp /usr/lib/openssh/sftp-server', 61 | 'test /my/subsystem/bin', 62 | ] 63 | chef_run.converge('openssh::default') 64 | expect(chef_run).to render_file('/etc/ssh/sshd_config').with_content(%r{Subsystem sftp \/usr\/lib\/openssh\/sftp-server\nSubsystem test.*}) 65 | end 66 | 67 | context 'port set without listen address set' do 68 | cached(:chef_run) do 69 | ChefSpec::SoloRunner.new(platform: 'ubuntu', version: '20.04') do |node| 70 | node.normal['openssh']['server']['port'] = 1234 71 | end.converge('openssh::default') 72 | end 73 | 74 | it 'writes out port at the top of the config' do 75 | expect(chef_run).to render_file('/etc/ssh/sshd_config') 76 | .with_content(/# Do NOT modify this file by hand!\n\nPort 1234/) 77 | end 78 | end 79 | 80 | context 'supports multiple ports' do 81 | cached(:chef_run) do 82 | ChefSpec::SoloRunner.new do |node| 83 | node.normal['openssh']['server']['port'] = [1234, 1235] 84 | end.converge('openssh::default') 85 | end 86 | 87 | it 'writes both ports to sshd_config' do 88 | expect(chef_run).to render_file('/etc/ssh/sshd_config') 89 | .with_content(/Port 1234/) 90 | .with_content(/Port 1235/) 91 | end 92 | end 93 | 94 | context 'supports ca keys and revoked keys' do 95 | cached(:chef_run) do 96 | ChefSpec::SoloRunner.new do |node| 97 | node.normal['openssh']['ca_keys'] = ['ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDJt7VN0YkI2jVnWUod8I/Qy9Am4lq0VOmFUTbzrMVTM8iut8E7heWu8G5QsDFLi3BNcU5wnwWO8rTWZZh1CJq6+zVn010rUYZhDxjlvFD4ZOUrN4RqsxSPBAaW2tgNXwoNeBgx/ZIrDSqj1xKP2Dixri2AxAuTQvxLn249dAv6MRwBGWJDtqOo0606VdQ933lq7eoYy57wvLtHBQHqZnjboIUtBxQTLyHrGTc0UdUrWRTtU8geynX2ABRWYKrHsXixgqPcYiiJOyrMufQEWzXr4u6PQs5LiSVsM9b6n8Aq184LDJiybDhQXEYnO8VeCV8v8GaDOGV4HB9W/15Fpxd/ ca'] 98 | node.normal['openssh']['revoked_keys'] = ['ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCeNFbS05i75Na662aH5uzXvdWqWxLELs1kEy3L60EYJpZ9GzJ4ByR7Gk2EQE5Knvpm/ck3en6ef1nyJzniELrPwO1OwVqVfNGjiz+4cl9EjReuk+wKWhoHpM2clEpp52Kl0TSBKt+oDCsv0REc0uSyi7rWkQSuRqnZvoxx3M7UIWJhMpFYKM2Few8c90ckHG4SY1Qcj2E/zI5ueVDz/jRfogF10dgSC8J4H6OO9+4N42EASQDbWFx1CO5jqB+1dmf3/7KbvdZUsO9zF1D5Kphk+bLm4SnIQsOJE5cfnqSNIvP6UcW2gNxHD4inxGQvz5Gljk3yYZ7n6HwDHo7hukpP user'] 99 | end.converge('openssh::default') 100 | end 101 | 102 | it 'writes the ca_keys' do 103 | template = chef_run.template('/etc/ssh/ca_keys') 104 | expect(template).to be 105 | expect(template.mode).to eq('0644') 106 | expect(template.owner).to eq('root') 107 | expect(template.group).to eq('root') 108 | end 109 | 110 | it 'writes the revoked_keys' do 111 | template = chef_run.template('/etc/ssh/revoked_keys') 112 | expect(template).to be 113 | expect(template.mode).to eq('0644') 114 | expect(template.owner).to eq('root') 115 | expect(template.group).to eq('root') 116 | end 117 | 118 | it 'writes ca public key to ca_keys file' do 119 | expect(chef_run).to render_file('/etc/ssh/ca_keys') 120 | .with_content { |content| 121 | expect(content).to include('ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDJt7VN0YkI2jVnWUod8I/Qy9Am4lq0VOmFUTbzrMVTM8iut8E7heWu8G5QsDFLi3BNcU5wnwWO8rTWZZh1CJq6+zVn010rUYZhDxjlvFD4ZOUrN4RqsxSPBAaW2tgNXwoNeBgx/ZIrDSqj1xKP2Dixri2AxAuTQvxLn249dAv6MRwBGWJDtqOo0606VdQ933lq7eoYy57wvLtHBQHqZnjboIUtBxQTLyHrGTc0UdUrWRTtU8geynX2ABRWYKrHsXixgqPcYiiJOyrMufQEWzXr4u6PQs5LiSVsM9b6n8Aq184LDJiybDhQXEYnO8VeCV8v8GaDOGV4HB9W/15Fpxd/ ca') 122 | } 123 | end 124 | 125 | it 'writes user public key to revoked_keys file' do 126 | expect(chef_run).to render_file('/etc/ssh/revoked_keys') 127 | .with_content { |content| 128 | expect(content).to include('ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCeNFbS05i75Na662aH5uzXvdWqWxLELs1kEy3L60EYJpZ9GzJ4ByR7Gk2EQE5Knvpm/ck3en6ef1nyJzniELrPwO1OwVqVfNGjiz+4cl9EjReuk+wKWhoHpM2clEpp52Kl0TSBKt+oDCsv0REc0uSyi7rWkQSuRqnZvoxx3M7UIWJhMpFYKM2Few8c90ckHG4SY1Qcj2E/zI5ueVDz/jRfogF10dgSC8J4H6OO9+4N42EASQDbWFx1CO5jqB+1dmf3/7KbvdZUsO9zF1D5Kphk+bLm4SnIQsOJE5cfnqSNIvP6UcW2gNxHD4inxGQvz5Gljk3yYZ7n6HwDHo7hukpP user') 129 | } 130 | end 131 | end 132 | end 133 | 134 | context 'openssh::default on Debian 9' do 135 | platform 'debian', '9' 136 | 137 | it 'installs the openssh packages' do 138 | expect(chef_run).to install_package(%w(openssh-client openssh-server)) 139 | end 140 | 141 | it 'starts the ssh service' do 142 | expect(chef_run).to start_service('ssh') 143 | expect(chef_run).to enable_service('ssh') 144 | end 145 | end 146 | 147 | context 'openssh::default on Debian 10' do 148 | platform 'debian', '10' 149 | 150 | it 'installs the openssh packages' do 151 | expect(chef_run).to install_package(%w(openssh-client openssh-server)) 152 | end 153 | 154 | it 'starts the ssh service' do 155 | expect(chef_run).to start_service('ssh') 156 | expect(chef_run).to enable_service('ssh') 157 | end 158 | end 159 | 160 | context 'openssh::default on Ubuntu 18.04' do 161 | platform 'ubuntu', '18.04' 162 | 163 | it 'installs the openssh packages' do 164 | expect(chef_run).to install_package(%w(openssh-client openssh-server)) 165 | end 166 | 167 | it 'starts the ssh service' do 168 | expect(chef_run).to start_service('ssh') 169 | expect(chef_run).to enable_service('ssh') 170 | end 171 | end 172 | 173 | context 'openssh::default on Ubuntu 20.04' do 174 | platform 'ubuntu', '20.04' 175 | 176 | it 'installs the openssh packages' do 177 | expect(chef_run).to install_package(%w(openssh-client openssh-server)) 178 | end 179 | 180 | it 'starts the ssh service' do 181 | expect(chef_run).to start_service('ssh') 182 | expect(chef_run).to enable_service('ssh') 183 | end 184 | end 185 | 186 | context 'openssh::default on CentOS 7' do 187 | platform 'centos', '7' 188 | 189 | it 'installs the openssh packages' do 190 | expect(chef_run).to install_package(%w(openssh-clients openssh-server)) 191 | end 192 | 193 | it 'starts the ssh service' do 194 | expect(chef_run).to start_service('ssh') 195 | expect(chef_run).to enable_service('ssh') 196 | end 197 | end 198 | 199 | context 'openssh::default on CentOS 8' do 200 | platform 'centos', '8' 201 | 202 | it 'installs the openssh packages' do 203 | expect(chef_run).to install_package(%w(openssh-clients openssh-server)) 204 | end 205 | 206 | it 'starts the ssh service' do 207 | expect(chef_run).to start_service('ssh') 208 | expect(chef_run).to enable_service('ssh') 209 | end 210 | end 211 | 212 | context 'openssh::default on Fedora' do 213 | platform 'fedora', '32' 214 | 215 | it 'installs the openssh packages' do 216 | expect(chef_run).to install_package(%w(openssh-clients openssh-server)) 217 | end 218 | 219 | it 'starts the ssh service' do 220 | expect(chef_run).to start_service('ssh') 221 | expect(chef_run).to enable_service('ssh') 222 | end 223 | end 224 | 225 | context 'openssh::default on openSUSE 15' do 226 | platform 'opensuse', '15' 227 | 228 | it 'installs the openssh packages' do 229 | expect(chef_run).to install_package(['openssh']) 230 | end 231 | 232 | it 'starts the ssh service' do 233 | expect(chef_run).to start_service('ssh') 234 | expect(chef_run).to enable_service('ssh') 235 | end 236 | end 237 | 238 | # context 'openssh::default on openSUSE 15' do 239 | # platform 'opensuse', '15' 240 | 241 | # it 'installs the openssh packages' do 242 | # expect(chef_run).to install_package(['openssh']) 243 | # end 244 | 245 | # it 'starts the ssh service' do 246 | # expect(chef_run).to start_service('ssh') 247 | # expect(chef_run).to enable_service('ssh') 248 | # end 249 | # end 250 | 251 | context 'openssh::default on macOS' do 252 | cached(:chef_run) do 253 | ChefSpec::SoloRunner.new(platform: 'mac_os_x', version: '10.15') do 254 | stub_command('sudo systemsetup -getremotelogin | grep "On"').and_return(1) 255 | end.converge('openssh::default') 256 | end 257 | 258 | it 'does not install an openssh package' do 259 | expect(chef_run).to_not install_package(['openssh']) 260 | end 261 | 262 | it 'starts the ssh service' do 263 | expect(chef_run).to start_service('ssh') 264 | expect(chef_run).to enable_service('ssh') 265 | end 266 | end 267 | end 268 | -------------------------------------------------------------------------------- /spec/unit/recipes/default_windows_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'openssh::default' do 4 | context 'Windows' do 5 | platform 'windows', '2016' 6 | 7 | let(:ssh_config_path) { join_path('C:\\ProgramData\\ssh', 'ssh_config') } 8 | let(:sshd_config_path) { join_path('C:\\ProgramData\\ssh', 'sshd_config') } 9 | let(:ca_keys_path) { join_path('C:\\ProgramData\\ssh', 'ca_userkeys.pub') } 10 | let(:revoked_keys_path) { join_path('C:\\ProgramData\\ssh', 'revoked_keys') } 11 | 12 | describe 'ssh_config' do 13 | it 'writes the ssh_config' do 14 | template = chef_run.template(ssh_config_path) 15 | expect(template).to be 16 | end 17 | end 18 | 19 | describe 'sshd_config' do 20 | it 'writes the sshd_config' do 21 | template = chef_run.template(sshd_config_path) 22 | expect(template).to be 23 | end 24 | 25 | it 'does not use PAM option on Windows' do 26 | expect(chef_run).to_not render_file(sshd_config_path).with_content(/UsePAM/) 27 | end 28 | 29 | it 'allow legacy default AuthorizedKeysFile behavior' do 30 | expect(chef_run).to_not render_file(sshd_config_path).with_content(/AuthorizedKeysFile.*/) 31 | end 32 | 33 | it 'writes a match group block' do 34 | chef_run.node.override['openssh']['server']['match'] = { 'Group admins' => { 'permit_tunnel' => 'yes' } } 35 | chef_run.converge('openssh::default') 36 | expect(chef_run).to render_file(sshd_config_path).with_content(/Match Group admins\n\s\sPermitTunnel yes/) 37 | end 38 | 39 | it 'skips match group block' do 40 | chef_run.node.override['openssh']['server']['match'] = {} 41 | chef_run.converge('openssh::default') 42 | expect(chef_run).to_not render_file(sshd_config_path).with_content(/Match Group admins\n\s\sPermitTunnel yes/) 43 | end 44 | 45 | it 'write multiple SSH subsystems from Hash' do 46 | chef_run.node.override['openssh']['server']['subsystem'] = { 47 | sftp: '/usr/lib/openssh/sftp-server', 48 | test: '/my/subsystem/bin', 49 | } 50 | chef_run.converge('openssh::default') 51 | expect(chef_run).to render_file(sshd_config_path).with_content(%r{Subsystem sftp \/usr\/lib\/openssh\/sftp-server\nSubsystem test.*}) 52 | end 53 | 54 | it 'skips subsystems block' do 55 | chef_run.node.override['openssh']['server']['subsystem'] = {} 56 | chef_run.converge('openssh::default') 57 | expect(chef_run).to_not render_file(sshd_config_path).with_content(/^Subsystem?/) 58 | end 59 | 60 | it 'supports legacy subsystem format' do 61 | chef_run.node.override['openssh']['server']['subsystem'] = 'sftp /usr/lib/openssh/sftp-server' 62 | chef_run.converge('openssh::default') 63 | expect(chef_run).to render_file(sshd_config_path).with_content(%r{Subsystem sftp \/usr\/lib\/openssh\/sftp-server\n}) 64 | end 65 | 66 | it 'allows subsystem from Array attribute' do 67 | chef_run.node.override['openssh']['server']['subsystem'] = [ 68 | 'sftp /usr/lib/openssh/sftp-server', 69 | 'test /my/subsystem/bin', 70 | ] 71 | chef_run.converge('openssh::default') 72 | expect(chef_run).to render_file(sshd_config_path).with_content(%r{Subsystem sftp \/usr\/lib\/openssh\/sftp-server\nSubsystem test.*}) 73 | end 74 | 75 | context 'port set without listen address set' do 76 | cached(:chef_run) do 77 | ChefSpec::SoloRunner.new(platform: 'windows', version: '2016') do |node| 78 | node.normal['openssh']['server']['port'] = 1234 79 | end.converge('openssh::default') 80 | end 81 | 82 | it 'writes out port at the top of the config' do 83 | expect(chef_run).to render_file(sshd_config_path) 84 | .with_content(/# Do NOT modify this file by hand!\n\nPort 1234/) 85 | end 86 | end 87 | 88 | context 'supports multiple ports' do 89 | cached(:chef_run) do 90 | ChefSpec::SoloRunner.new(platform: 'windows', version: '2016') do |node| 91 | node.normal['openssh']['server']['port'] = [1234, 1235] 92 | end.converge('openssh::default') 93 | end 94 | 95 | it 'writes both ports to sshd_config' do 96 | expect(chef_run).to render_file(sshd_config_path) 97 | .with_content(/Port 1234/) 98 | .with_content(/Port 1235/) 99 | end 100 | end 101 | 102 | context 'supports ca keys and revoked keys' do 103 | cached(:chef_run) do 104 | ChefSpec::SoloRunner.new(platform: 'windows', version: '2016') do |node| 105 | node.normal['openssh']['ca_keys'] = ['ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDJt7VN0YkI2jVnWUod8I/Qy9Am4lq0VOmFUTbzrMVTM8iut8E7heWu8G5QsDFLi3BNcU5wnwWO8rTWZZh1CJq6+zVn010rUYZhDxjlvFD4ZOUrN4RqsxSPBAaW2tgNXwoNeBgx/ZIrDSqj1xKP2Dixri2AxAuTQvxLn249dAv6MRwBGWJDtqOo0606VdQ933lq7eoYy57wvLtHBQHqZnjboIUtBxQTLyHrGTc0UdUrWRTtU8geynX2ABRWYKrHsXixgqPcYiiJOyrMufQEWzXr4u6PQs5LiSVsM9b6n8Aq184LDJiybDhQXEYnO8VeCV8v8GaDOGV4HB9W/15Fpxd/ ca'] 106 | node.normal['openssh']['revoked_keys'] = ['ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCeNFbS05i75Na662aH5uzXvdWqWxLELs1kEy3L60EYJpZ9GzJ4ByR7Gk2EQE5Knvpm/ck3en6ef1nyJzniELrPwO1OwVqVfNGjiz+4cl9EjReuk+wKWhoHpM2clEpp52Kl0TSBKt+oDCsv0REc0uSyi7rWkQSuRqnZvoxx3M7UIWJhMpFYKM2Few8c90ckHG4SY1Qcj2E/zI5ueVDz/jRfogF10dgSC8J4H6OO9+4N42EASQDbWFx1CO5jqB+1dmf3/7KbvdZUsO9zF1D5Kphk+bLm4SnIQsOJE5cfnqSNIvP6UcW2gNxHD4inxGQvz5Gljk3yYZ7n6HwDHo7hukpP user'] 107 | end.converge('openssh::default') 108 | end 109 | 110 | it 'writes the ca_keys' do 111 | template = chef_run.template(ca_keys_path) 112 | expect(template).to be 113 | end 114 | 115 | it 'writes the revoked_keys' do 116 | template = chef_run.template(revoked_keys_path) 117 | expect(template).to be 118 | end 119 | 120 | it 'writes ca public key to ca_keys file' do 121 | expect(chef_run).to render_file(ca_keys_path) 122 | .with_content { |content| 123 | expect(content).to include('ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDJt7VN0YkI2jVnWUod8I/Qy9Am4lq0VOmFUTbzrMVTM8iut8E7heWu8G5QsDFLi3BNcU5wnwWO8rTWZZh1CJq6+zVn010rUYZhDxjlvFD4ZOUrN4RqsxSPBAaW2tgNXwoNeBgx/ZIrDSqj1xKP2Dixri2AxAuTQvxLn249dAv6MRwBGWJDtqOo0606VdQ933lq7eoYy57wvLtHBQHqZnjboIUtBxQTLyHrGTc0UdUrWRTtU8geynX2ABRWYKrHsXixgqPcYiiJOyrMufQEWzXr4u6PQs5LiSVsM9b6n8Aq184LDJiybDhQXEYnO8VeCV8v8GaDOGV4HB9W/15Fpxd/ ca') 124 | } 125 | end 126 | 127 | it 'writes user public key to revoked_keys file' do 128 | expect(chef_run).to render_file(revoked_keys_path) 129 | .with_content { |content| 130 | expect(content).to include('ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCeNFbS05i75Na662aH5uzXvdWqWxLELs1kEy3L60EYJpZ9GzJ4ByR7Gk2EQE5Knvpm/ck3en6ef1nyJzniELrPwO1OwVqVfNGjiz+4cl9EjReuk+wKWhoHpM2clEpp52Kl0TSBKt+oDCsv0REc0uSyi7rWkQSuRqnZvoxx3M7UIWJhMpFYKM2Few8c90ckHG4SY1Qcj2E/zI5ueVDz/jRfogF10dgSC8J4H6OO9+4N42EASQDbWFx1CO5jqB+1dmf3/7KbvdZUsO9zF1D5Kphk+bLm4SnIQsOJE5cfnqSNIvP6UcW2gNxHD4inxGQvz5Gljk3yYZ7n6HwDHo7hukpP user') 131 | } 132 | end 133 | end 134 | end 135 | end 136 | end 137 | -------------------------------------------------------------------------------- /spec/unit/recipes/iptables_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'openssh::iptables' do 4 | context 'default attribute, no port set' do 5 | let(:chef_run) { ChefSpec::SoloRunner.new(platform: 'ubuntu', version: '20.04', step_into: ['iptables_rule']).converge(described_recipe) } 6 | 7 | it 'renders the iptables rule in a template' do 8 | expect(chef_run).to create_template('/etc/iptables/rules.v4') 9 | end 10 | 11 | it 'contains the default port (22) for sshd' do 12 | expect(chef_run).to render_file('/etc/iptables/rules.v4') 13 | .with_content('-A INPUT -p tcp -m tcp -j ACCEPT --dport 22') 14 | .with_content('-A OUTPUT -p tcp -m tcp -j ACCEPT --sport 22') 15 | end 16 | end 17 | 18 | context 'non-default port set' do 19 | let(:chef_run) do 20 | ChefSpec::SoloRunner.new(step_into: ['iptables_rule']) do |node| 21 | node.normal['openssh']['server']['port'] = '4242' 22 | end.converge(described_recipe) 23 | end 24 | 25 | it 'contains the non-default port from the attribute' do 26 | expect(chef_run).to render_file('/etc/iptables/rules.v4') 27 | .with_content('-A INPUT -p tcp -m tcp -j ACCEPT --dport 4242') 28 | .with_content('-A OUTPUT -p tcp -m tcp -j ACCEPT --sport 4242') 29 | end 30 | end 31 | 32 | context 'supports multiple ports' do 33 | let(:chef_run) do 34 | ChefSpec::SoloRunner.new(platform: 'ubuntu', version: '20.04', step_into: ['iptables_rule']) do |node| 35 | node.normal['openssh']['server']['port'] = [1234, 1235] 36 | end.converge(described_recipe) 37 | end 38 | 39 | it 'contains both ports from' do 40 | expect(chef_run).to render_file('/etc/iptables/rules.v4') 41 | .with_content('-A INPUT -p tcp -m tcp -j ACCEPT --dport 1234') 42 | .with_content('-A OUTPUT -p tcp -m tcp -j ACCEPT --sport 1234') 43 | .with_content('-A INPUT -p tcp -m tcp -j ACCEPT --dport 1235') 44 | .with_content('-A OUTPUT -p tcp -m tcp -j ACCEPT --sport 1235') 45 | end 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /templates/ca_keys.erb: -------------------------------------------------------------------------------- 1 | # This file was generated by Chef 2 | # Do NOT modify this file by hand! 3 | 4 | # One CA public key per line 5 | <% node['openssh']['ca_keys'].each do |key| -%> 6 | <%=key %> 7 | <% end -%> 8 | -------------------------------------------------------------------------------- /templates/port_ssh.erb: -------------------------------------------------------------------------------- 1 | # SSH 2 | <% if @port.kind_of? Array -%> 3 | <% @port.each do |item| -%> 4 | -A FWR -p tcp -m tcp --dport <%= item %> -j ACCEPT 5 | <% end -%> 6 | <% else -%> 7 | -A FWR -p tcp -m tcp --dport <%= @port %> -j ACCEPT 8 | <% end -%> 9 | -------------------------------------------------------------------------------- /templates/revoked_keys.erb: -------------------------------------------------------------------------------- 1 | # This file was generated by Chef 2 | # Do NOT modify this file by hand! 3 | 4 | # One certificate/public key per line 5 | <% node['openssh']['revoked_keys'].each do |key| -%> 6 | <%=key %> 7 | <% end -%> 8 | -------------------------------------------------------------------------------- /templates/ssh_config.erb: -------------------------------------------------------------------------------- 1 | # This file was generated by Chef 2 | # Do NOT modify this file by hand! 3 | 4 | <% node['openssh']['client'].each do |key, value| -%> 5 | <% if value.kind_of? Array -%> 6 | <% value.each do |item| -%> 7 | <%= "#{key.split("_").map { |w| w.capitalize}.join} #{item}" %> 8 | <% end -%> 9 | <% elsif value.kind_of? Hash -%> 10 | <%= "Host #{key}"%> 11 | <% value.each do |host_key, host_value| -%> 12 | <% if host_value.kind_of? Array -%> 13 | <% host_value.each do |item| -%> 14 | <%= " #{host_key.split("_").map { |w| w.capitalize}.join} #{item}" %> 15 | <% end -%> 16 | <% else -%> 17 | <%= " #{host_key.split("_").map { |w| w.capitalize}.join} #{host_value}" %> 18 | <% end -%> 19 | <% end -%> 20 | <% else -%> 21 | <%= "#{key.split("_").map { |w| w.capitalize}.join} #{value}"%> 22 | <% end -%> 23 | <% end -%> 24 | -------------------------------------------------------------------------------- /templates/sshd_config.erb: -------------------------------------------------------------------------------- 1 | # This file was generated by Chef 2 | # Do NOT modify this file by hand! 3 | 4 | <% @options.map do |key, value| -%> 5 | <% if value.kind_of? Array -%> 6 | <% value.each do |item| -%> 7 | <%= "#{key.split("_").map { |w| w.capitalize}.join} #{item}" %> 8 | <% end -%> 9 | <% else -%> 10 | <%= "#{key.split("_").map { |w| w.capitalize}.join} #{value}"%> 11 | <% end -%> 12 | <% end -%> 13 | 14 | <% if defined?(node['openssh']['server']['subsystem']) -%> 15 | <% if node['openssh']['server']['subsystem'].is_a?(Hash) -%> 16 | <% node['openssh']['server']['subsystem'].map do |subsystem_name, subsystem_value| -%> 17 | Subsystem <%= subsystem_name %> <%= subsystem_value %> 18 | <% end -%> 19 | <% elsif node['openssh']['server']['subsystem'].is_a?(Array) -%> 20 | <% node['openssh']['server']['subsystem'].each do |subsystem| -%> 21 | Subsystem <%= subsystem %> 22 | <% end -%> 23 | <% elsif node['openssh']['server']['subsystem'].is_a?(String) -%> 24 | Subsystem <%= node['openssh']['server']['subsystem'] %> 25 | <% end -%> 26 | <% end -%> 27 | 28 | <% unless node['openssh']['server']['match'].empty? || !defined?(node['openssh']['server']['match']) -%> 29 | <% node['openssh']['server']['match'].sort.map do |match_key, match_items| -%> 30 | Match <%= match_key.sub(/^[0-9]+/, '').strip %> 31 | <% match_items.sort.map do |key, value| -%> 32 | <% if value.kind_of? Array -%> 33 | <% value.each do |item| -%> 34 | <%= " #{key.split("_").map { |w| w.capitalize}.join} #{item}" %> 35 | <% end -%> 36 | <% else -%> 37 | <%= " #{key.split("_").map { |w| w.capitalize}.join} #{value}"%> 38 | <% end -%> 39 | <% end -%> 40 | <% end -%> 41 | Match all 42 | <% end -%> 43 | -------------------------------------------------------------------------------- /test/integration/default/default_spec.rb: -------------------------------------------------------------------------------- 1 | service_name = if os.debian? || os.darwin? 2 | 'ssh' 3 | else 4 | 'sshd' 5 | end 6 | 7 | describe service(service_name) do 8 | it { should be_enabled } 9 | it { should be_running } 10 | end 11 | 12 | describe port(22) do 13 | it { should be_listening } 14 | end 15 | 16 | describe ssh_config do 17 | its('UseRoaming') { should eq 'no' } 18 | end 19 | 20 | # Attempt to ssh to localhost 21 | describe command('ssh -oStrictHostKeyChecking=no -v localhost') do 22 | # No way of actually sshing in without a keypair or password 23 | # but being prompted for an authentication method should be sufficient to 24 | # test that SSH is working as expected, for the most part 25 | its('stderr') { should match 'Next authentication method' } 26 | end 27 | -------------------------------------------------------------------------------- /test/integration/iptables/default_spec.rb: -------------------------------------------------------------------------------- 1 | service_name = case os[:family] 2 | when 'ubuntu', 'debian' 3 | 'ssh' 4 | else 5 | 'sshd' 6 | end 7 | 8 | describe service(service_name) do 9 | it { should be_enabled } 10 | it { should be_running } 11 | end 12 | 13 | describe port(22) do 14 | it { should be_listening } 15 | end 16 | 17 | describe iptables do 18 | it { should have_rule('-A INPUT -p tcp -m tcp --dport 22 -j ACCEPT') } 19 | it { should have_rule('-A OUTPUT -p tcp -m tcp --sport 22 -j ACCEPT') } 20 | end 21 | -------------------------------------------------------------------------------- /test/integration/windows-default/default_spec.rb: -------------------------------------------------------------------------------- 1 | service_name = 'sshd' 2 | 3 | describe service(service_name) do 4 | it { should be_enabled } 5 | it { should be_running } 6 | end 7 | 8 | describe port(22) do 9 | it { should be_listening } 10 | end 11 | 12 | describe ssh_config do 13 | its('UseRoaming') { should eq 'no' } 14 | end 15 | 16 | # Attempt to ssh to localhost 17 | script_stderr = <<-EOF 18 | Start-Process ssh "-oStrictHostKeyChecking=no -oPasswordAuthentication=no -v localhost" -NoNewWindow -Wait -RedirectStandardOutput stdOut.log -RedirectStandardError stdErr.log; gc stdErr.log; rm *.log 19 | EOF 20 | 21 | describe powershell(script_stderr) do 22 | # No way of actually sshing in without a keypair or password 23 | # but being prompted for an authentication method should be sufficient to 24 | # test that SSH is working as expected, for the most part 25 | its('stdout') { should match /Next authentication method/ } 26 | end 27 | 28 | script_stdout = <<-EOF 29 | Start-Process ssh "-oStrictHostKeyChecking=no -oPasswordAuthentication=no -v localhost" -NoNewWindow -Wait -RedirectStandardOutput stdOut.log -RedirectStandardError stdErr.log; gc stdOut.log; rm *.log 30 | EOF 31 | 32 | describe powershell(script_stdout) do 33 | # No way of actually sshing in without a keypair or password 34 | # but being prompted for an authentication method should be sufficient to 35 | # test that SSH is working as expected, for the most part 36 | its('stdout') { should eq '' } 37 | end 38 | --------------------------------------------------------------------------------