diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..ecb10a80 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +# editorconfig.org + +# Managed by modulesync - DO NOT EDIT +# https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ + +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 2 +tab_width = 2 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true diff --git a/.fixtures.yml b/.fixtures.yml index 0d939656..fde683af 100644 --- a/.fixtures.yml +++ b/.fixtures.yml @@ -1,8 +1,6 @@ fixtures: repositories: stdlib: "https://github.com/puppetlabs/puppetlabs-stdlib" - concat: - repo: "https://github.com/puppetlabs/puppetlabs-concat" - ref: "1.2.0" - symlinks: - ssh: "#{source_dir}" + concat: "https://github.com/puppetlabs/puppetlabs-concat" + systemd: "https://github.com/voxpupuli/puppet-systemd" + sshkeys_core: "https://github.com/puppetlabs/puppetlabs-sshkeys_core" diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 00000000..593e7aa8 --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,26 @@ + + +## Affected Puppet, Ruby, OS and module versions/distributions + +- Puppet: +- Ruby: +- Distribution: +- Module version: + +## How to reproduce (e.g Puppet code you use) + +## What are you seeing + +## What behaviour did you expect instead + +## Output log + +## Any additional information you'd like to impart diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..342807bc --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,20 @@ + +#### Pull Request (PR) description + + +#### This Pull Request (PR) fixes the following issues + diff --git a/.github/labeler.yml b/.github/labeler.yml new file mode 100644 index 00000000..f2d08d6b --- /dev/null +++ b/.github/labeler.yml @@ -0,0 +1,6 @@ +--- +# Managed by modulesync - DO NOT EDIT +# https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ + +skip-changelog: + - head-branch: ['^release-*', 'release'] diff --git a/.github/release.yml b/.github/release.yml new file mode 100644 index 00000000..f5b5d7a9 --- /dev/null +++ b/.github/release.yml @@ -0,0 +1,42 @@ +--- +# Managed by modulesync - DO NOT EDIT +# https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ + +# https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes + +changelog: + exclude: + labels: + - duplicate + - invalid + - modulesync + - question + - skip-changelog + - wont-fix + - wontfix + + categories: + - title: Breaking Changes 🛠 + labels: + - backwards-incompatible + + - title: New Features 🎉 + labels: + - enhancement + + - title: Bug Fixes 🐛 + labels: + - bug + + - title: Documentation Updates 📚 + labels: + - documentation + - docs + + - title: Dependency Updates ⬆️ + labels: + - dependencies + + - title: Other Changes + labels: + - "*" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..44674150 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,25 @@ +--- +# Managed by modulesync - DO NOT EDIT +# https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ + +name: CI + +# yamllint disable-line rule:truthy +on: + pull_request: {} + push: + branches: + - main + - master + +concurrency: + group: ${{ github.ref_name }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + puppet: + name: Puppet + uses: voxpupuli/gha-puppet/.github/workflows/beaker.yml@v3 diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml new file mode 100644 index 00000000..eacd0b33 --- /dev/null +++ b/.github/workflows/labeler.yml @@ -0,0 +1,22 @@ +--- +# Managed by modulesync - DO NOT EDIT +# https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ + +name: "Pull Request Labeler" + +# yamllint disable-line rule:truthy +on: + pull_request_target: {} + +permissions: + contents: read + pull-requests: write + +jobs: + labeler: + permissions: + contents: read + pull-requests: write + runs-on: ubuntu-latest + steps: + - uses: actions/labeler@v5 diff --git a/.github/workflows/prepare_release.yml b/.github/workflows/prepare_release.yml new file mode 100644 index 00000000..1b515440 --- /dev/null +++ b/.github/workflows/prepare_release.yml @@ -0,0 +1,27 @@ +--- +# Managed by modulesync - DO NOT EDIT +# https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ + +name: 'Prepare Release' + +on: + workflow_dispatch: + inputs: + version: + description: 'Module version to be released. Must be a valid semver string without leading v. (1.2.3)' + required: false + +permissions: + contents: write + pull-requests: write + +jobs: + release_prep: + uses: 'voxpupuli/gha-puppet/.github/workflows/prepare_release.yml@v3' + with: + version: ${{ github.event.inputs.version }} + allowed_owner: 'saz' + secrets: + # Configure secrets here: + # https://docs.github.com/en/actions/security-guides/encrypted-secrets + github_pat: '${{ secrets.PCCI_PAT_RELEASE_PREP }}' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..6de3c633 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,26 @@ +--- +# Managed by modulesync - DO NOT EDIT +# https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ + +name: Release + +# yamllint disable-line rule:truthy +on: + push: + tags: + - '*' + +permissions: + contents: write + +jobs: + release: + name: Release + uses: voxpupuli/gha-puppet/.github/workflows/release.yml@v3 + with: + allowed_owner: 'saz' + secrets: + # Configure secrets here: + # https://docs.github.com/en/actions/security-guides/encrypted-secrets + username: ${{ secrets.PUPPET_FORGE_USERNAME }} + api_key: ${{ secrets.PUPPET_FORGE_API_KEY }} diff --git a/.gitignore b/.gitignore index 4235cdae..adea1b01 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,25 @@ -pkg/ -*.swp -.DS_Store -Gemfile.lock -vendor/ -.bundle/ +# Managed by modulesync - DO NOT EDIT +# https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ + +/pkg/ +/Gemfile.lock +/Gemfile.local +/vendor/ +/.vendor/ +/spec/fixtures/manifests/ +/spec/fixtures/modules/ +/.vagrant/ +/.bundle/ +/.ruby-version +/coverage/ +/log/ +/.idea/ +/.dependencies/ +/.librarian/ +/Puppetfile.lock +*.iml +.*.sw? +/.yardoc/ +/Guardfile +bolt-debug.log +.rerun.json diff --git a/.msync.yml b/.msync.yml new file mode 100644 index 00000000..814fbd04 --- /dev/null +++ b/.msync.yml @@ -0,0 +1,5 @@ +--- +# Managed by modulesync - DO NOT EDIT +# https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ + +modulesync_config_version: '10.3.0' diff --git a/.overcommit.yml b/.overcommit.yml new file mode 100644 index 00000000..4ed994cc --- /dev/null +++ b/.overcommit.yml @@ -0,0 +1,67 @@ +# Managed by modulesync - DO NOT EDIT +# https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ +# +# Hooks are only enabled if you take action. +# +# To enable the hooks run: +# +# ``` +# bundle exec overcommit --install +# # ensure .overcommit.yml does not harm to you and then +# bundle exec overcommit --sign +# ``` +# +# (it will manage the .git/hooks directory): +# +# Examples howto skip a test for a commit or push: +# +# ``` +# SKIP=RuboCop git commit +# SKIP=PuppetLint git commit +# SKIP=RakeTask git push +# ``` +# +# Don't invoke overcommit at all: +# +# ``` +# OVERCOMMIT_DISABLE=1 git commit +# ``` +# +# Read more about overcommit: https://github.com/brigade/overcommit +# +# To manage this config yourself in your module add +# +# ``` +# .overcommit.yml: +# unmanaged: true +# ``` +# +# to your modules .sync.yml config +--- +PreCommit: + RuboCop: + enabled: true + description: 'Runs rubocop on modified files only' + command: ['bundle', 'exec', 'rubocop'] + RakeTarget: + enabled: true + description: 'Runs lint on modified files only' + targets: + - 'lint' + command: ['bundle', 'exec', 'rake'] + YamlSyntax: + enabled: true + JsonSyntax: + enabled: true + TrailingWhitespace: + enabled: true + +PrePush: + RakeTarget: + enabled: true + description: 'Run rake targets' + targets: + - 'validate' + - 'test' + - 'rubocop' + command: ['bundle', 'exec', 'rake'] diff --git a/.pmtignore b/.pmtignore new file mode 100644 index 00000000..a9d37aa0 --- /dev/null +++ b/.pmtignore @@ -0,0 +1,39 @@ +# Managed by modulesync - DO NOT EDIT +# https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ + +/docs/ +/pkg/ +/Gemfile +/Gemfile.lock +/Gemfile.local +/vendor/ +/.vendor/ +/spec/ +/Rakefile +/.vagrant/ +/.bundle/ +/.ruby-version +/coverage/ +/log/ +/.idea/ +/.dependencies/ +/.github/ +/.librarian/ +/Puppetfile.lock +/Puppetfile +*.iml +/.editorconfig +/.fixtures.yml +/.gitignore +/.msync.yml +/.overcommit.yml +/.pmtignore +/.rspec +/.rspec_parallel +/.rubocop.yml +/.sync.yml +.*.sw? +/.yardoc/ +/.yardopts +/Dockerfile +/HISTORY.md diff --git a/.puppet-lint.rc b/.puppet-lint.rc new file mode 100644 index 00000000..05d28a26 --- /dev/null +++ b/.puppet-lint.rc @@ -0,0 +1,6 @@ +# Managed by modulesync - DO NOT EDIT +# https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ + +--fail-on-warnings +--no-parameter_documentation-check +--no-parameter_types-check diff --git a/.rubocop.yml b/.rubocop.yml index f3ceb943..53ac1898 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,506 +1,6 @@ -require: rubocop-rspec -AllCops: - TargetRubyVersion: 1.9 - Include: - - ./**/*.rb - Exclude: - - vendor/**/* - - .vendor/**/* - - pkg/**/* - - spec/fixtures/**/* -Lint/ConditionPosition: - Enabled: true - -Lint/ElseLayout: - Enabled: true - -Lint/UnreachableCode: - Enabled: true - -Lint/UselessComparison: - Enabled: true - -Lint/EnsureReturn: - Enabled: true - -Lint/HandleExceptions: - Enabled: true - -Lint/LiteralInCondition: - Enabled: true - -Lint/ShadowingOuterLocalVariable: - Enabled: true - -Lint/LiteralInInterpolation: - Enabled: true - -Style/HashSyntax: - Enabled: true - -Style/RedundantReturn: - Enabled: true - -Lint/AmbiguousOperator: - Enabled: true - -Lint/AssignmentInCondition: - Enabled: true - -Style/SpaceBeforeComment: - Enabled: true - -Style/AndOr: - Enabled: true - -Style/RedundantSelf: - Enabled: true - -# Method length is not necessarily an indicator of code quality -Metrics/MethodLength: - Enabled: false - -# Module length is not necessarily an indicator of code quality -Metrics/ModuleLength: - Enabled: false - -Style/WhileUntilModifier: - Enabled: true - -Lint/AmbiguousRegexpLiteral: - Enabled: true - -Lint/Eval: - Enabled: true - -Lint/BlockAlignment: - Enabled: true - -Lint/DefEndAlignment: - Enabled: true - -Lint/EndAlignment: - Enabled: true - -Lint/DeprecatedClassMethods: - Enabled: true - -Lint/Loop: - Enabled: true - -Lint/ParenthesesAsGroupedExpression: - Enabled: true - -Lint/RescueException: - Enabled: true - -Lint/StringConversionInInterpolation: - Enabled: true - -Lint/UnusedBlockArgument: - Enabled: true - -Lint/UnusedMethodArgument: - Enabled: true - -Lint/UselessAccessModifier: - Enabled: true - -Lint/UselessAssignment: - Enabled: true - -Lint/Void: - Enabled: true - -Style/AccessModifierIndentation: - Enabled: true - -Style/AccessorMethodName: - Enabled: true - -Style/Alias: - Enabled: true - -Style/AlignArray: - Enabled: true - -Style/AlignHash: - Enabled: true - -Style/AlignParameters: - Enabled: true - -Metrics/BlockNesting: - Enabled: true - -Style/AsciiComments: - Enabled: true - -Style/Attr: - Enabled: true - -Style/BracesAroundHashParameters: - Enabled: true - -Style/CaseEquality: - Enabled: true - -Style/CaseIndentation: - Enabled: true - -Style/CharacterLiteral: - Enabled: true - -Style/ClassAndModuleCamelCase: - Enabled: true - -Style/ClassAndModuleChildren: - Enabled: false - -Style/ClassCheck: - Enabled: true - -# Class length is not necessarily an indicator of code quality -Metrics/ClassLength: - Enabled: false - -Style/ClassMethods: - Enabled: true - -Style/ClassVars: - Enabled: true - -Style/WhenThen: - Enabled: true - -Style/WordArray: - Enabled: true - -Style/UnneededPercentQ: - Enabled: true - -Style/Tab: - Enabled: true - -Style/SpaceBeforeSemicolon: - Enabled: true - -Style/TrailingBlankLines: - Enabled: true - -Style/SpaceInsideBlockBraces: - Enabled: true - -Style/SpaceInsideBrackets: - Enabled: true - -Style/SpaceInsideHashLiteralBraces: - Enabled: true - -Style/SpaceInsideParens: - Enabled: true - -Style/LeadingCommentSpace: - Enabled: true - -Style/SpaceBeforeFirstArg: - Enabled: true - -Style/SpaceAfterColon: - Enabled: true - -Style/SpaceAfterComma: - Enabled: true - -Style/SpaceAfterMethodName: - Enabled: true - -Style/SpaceAfterNot: - Enabled: true - -Style/SpaceAfterSemicolon: - Enabled: true - -Style/SpaceAroundEqualsInParameterDefault: - Enabled: true - -Style/SpaceAroundOperators: - Enabled: true - -Style/SpaceBeforeBlockBraces: - Enabled: true - -Style/SpaceBeforeComma: - Enabled: true - -Style/CollectionMethods: - Enabled: true - -Style/CommentIndentation: - Enabled: true - -Style/ColonMethodCall: - Enabled: true - -Style/CommentAnnotation: - Enabled: true - -# 'Complexity' is very relative -Metrics/CyclomaticComplexity: - Enabled: false - -Style/ConstantName: - Enabled: true - -Style/Documentation: - Enabled: false - -Style/DefWithParentheses: - Enabled: true - -Style/PreferredHashMethods: - Enabled: true - -Style/DotPosition: - EnforcedStyle: trailing - -Style/DoubleNegation: - Enabled: true - -Style/EachWithObject: - Enabled: true - -Style/EmptyLineBetweenDefs: - Enabled: true - -Style/IndentArray: - Enabled: true - -Style/IndentHash: - Enabled: true - -Style/IndentationConsistency: - Enabled: true - -Style/IndentationWidth: - Enabled: true - -Style/EmptyLines: - Enabled: true - -Style/EmptyLinesAroundAccessModifier: - Enabled: true - -Style/EmptyLiteral: - Enabled: true - -# Configuration parameters: AllowURI, URISchemes. -Metrics/LineLength: - Enabled: false - -Style/MethodCallParentheses: - Enabled: true - -Style/MethodDefParentheses: - Enabled: true - -Style/LineEndConcatenation: - Enabled: true - -Style/TrailingWhitespace: - Enabled: true - -Style/StringLiterals: - Enabled: true - -Style/TrailingCommaInArguments: - Enabled: true - -Style/TrailingCommaInLiteral: - Enabled: true - -Style/GlobalVars: - Enabled: true - -Style/GuardClause: - Enabled: true - -Style/IfUnlessModifier: - Enabled: true - -Style/MultilineIfThen: - Enabled: true - -Style/NegatedIf: - Enabled: true - -Style/NegatedWhile: - Enabled: true - -Style/Next: - Enabled: true - -Style/SingleLineBlockParams: - Enabled: true - -Style/SingleLineMethods: - Enabled: true - -Style/SpecialGlobalVars: - Enabled: true - -Style/TrivialAccessors: - Enabled: true - -Style/UnlessElse: - Enabled: true - -Style/VariableInterpolation: - Enabled: true - -Style/VariableName: - Enabled: true - -Style/WhileUntilDo: - Enabled: true - -Style/EvenOdd: - Enabled: true - -Style/FileName: - Enabled: true - -Style/For: - Enabled: true - -Style/Lambda: - Enabled: true - -Style/MethodName: - Enabled: true - -Style/MultilineTernaryOperator: - Enabled: true - -Style/NestedTernaryOperator: - Enabled: true - -Style/NilComparison: - Enabled: true - -Style/FormatString: - Enabled: true - -Style/MultilineBlockChain: - Enabled: true - -Style/Semicolon: - Enabled: true - -Style/SignalException: - Enabled: true - -Style/NonNilCheck: - Enabled: true - -Style/Not: - Enabled: true - -Style/NumericLiterals: - Enabled: true - -Style/OneLineConditional: - Enabled: true - -Style/OpMethod: - Enabled: true - -Style/ParenthesesAroundCondition: - Enabled: true - -Style/PercentLiteralDelimiters: - Enabled: true - -Style/PerlBackrefs: - Enabled: true - -Style/PredicateName: - Enabled: true - -Style/RedundantException: - Enabled: true - -Style/SelfAssignment: - Enabled: true - -Style/Proc: - Enabled: true - -Style/RaiseArgs: - Enabled: true - -Style/RedundantBegin: - Enabled: true - -Style/RescueModifier: - Enabled: true - -# based on https://github.com/voxpupuli/modulesync_config/issues/168 -Style/RegexpLiteral: - EnforcedStyle: percent_r - Enabled: true - -Lint/UnderscorePrefixedVariableName: - Enabled: true - -Metrics/ParameterLists: - Enabled: false - -Lint/RequireParentheses: - Enabled: true - -Style/SpaceBeforeFirstArg: - Enabled: true - -Style/ModuleFunction: - Enabled: true - -Lint/Debugger: - Enabled: true - -Style/IfWithSemicolon: - Enabled: true - -Style/Encoding: - Enabled: true - -Style/BlockDelimiters: - Enabled: true - -Style/MultilineBlockLayout: - Enabled: true - -# 'Complexity' is very relative -Metrics/AbcSize: - Enabled: False - -# 'Complexity' is very relative -Metrics/PerceivedComplexity: - Enabled: False - -Lint/UselessAssignment: - Enabled: true - -Style/ClosingParenthesisIndentation: - Enabled: false - -# RSpec - -# We don't use rspec in this way -RSpec/DescribeClass: - Enabled: False - -# Example length is not necessarily an indicator of code quality -RSpec/ExampleLength: - Enabled: False +--- +# Managed by modulesync - DO NOT EDIT +# https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ +inherit_gem: + voxpupuli-test: rubocop.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index f20707ea..00000000 --- a/.travis.yml +++ /dev/null @@ -1,43 +0,0 @@ -sudo: false -language: ruby -cache: bundler -bundler_args: "--without system_tests development" -before_install: -- bundle -v -- rm Gemfile.lock || true -- gem update --system -- gem update bundler -- gem --version -- bundle -v -script: -- bundle exec rake $CHECK -matrix: - fast_finish: true - include: - - rvm: 1.9.3 - env: PUPPET_VERSION="~> 3.0" STRICT_VARIABLES="yes" CHECK=test - - rvm: 1.9.3 - env: PUPPET_VERSION="~> 3.0" STRICT_VARIABLES="yes" FUTURE_PARSER="yes" CHECK=test - - rvm: 2.1 - env: PUPPET_VERSION="~> 3.0" STRICT_VARIABLES="yes" CHECK=test - - rvm: 2.1 - env: PUPPET_VERSION="~> 4.0" STRICT_VARIABLES="yes" CHECK=test - - rvm: 2.2 - env: PUPPET_VERSION="~> 4.0" STRICT_VARIABLES="yes" CHECK=test - - rvm: 2.3.1 - env: PUPPET_VERSION="~> 4.0" STRICT_VARIABLES="yes" CHECK=build - - rvm: 2.3.1 - env: PUPPET_VERSION="~> 4.0" STRICT_VARIABLES="yes" CHECK=rubocop - - rvm: 2.3.1 - env: PUPPET_VERSION="~> 4.0" STRICT_VARIABLES="yes" CHECK=test -notifications: - email: false -deploy: - provider: puppetforge - user: saz - password: - secure: HOEacsz4i4p5Bagrotnyrst0TPMsbqlpfK8X2j/7ieGRqTJPuLx2yarz2ILHsEUnVPqlSJd4mDXabMZbPcpJlD/tJfNeoU1nVGUH+RX1BcXpakDeZrkraImDLjpnuw3ANtGgUpCFJlQLbYylHBA84RH/mZuroNhW5zi0polPz5M= - on: - tags: true - all_branches: true - rvm: 2.3.1 diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..dbaba9ce --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,288 @@ +# Changelog + +All notable changes to this project will be documented in this file. +Each new release typically also includes the latest modulesync defaults. +These should not affect the functionality of the module. + +## [v14.1.0](https://github.com/saz/puppet-ssh/tree/v14.1.0) (2025-10-30) + +[Full Changelog](https://github.com/saz/puppet-ssh/compare/v14.0.2...v14.1.0) + +**Implemented enhancements:** + +- make user/group of resources configurable [\#436](https://github.com/saz/puppet-ssh/pull/436) ([saz](https://github.com/saz)) +- set KbdInteractiveAuthentication on newer SSH versions [\#434](https://github.com/saz/puppet-ssh/pull/434) ([saz](https://github.com/saz)) + +**Fixed bugs:** + +- use proper sshd binary location in config validate command [\#435](https://github.com/saz/puppet-ssh/pull/435) ([saz](https://github.com/saz)) + +## [v14.0.2](https://github.com/saz/puppet-ssh/tree/v14.0.2) (2025-10-30) + +[Full Changelog](https://github.com/saz/puppet-ssh/compare/v14.0.1...v14.0.2) + +**Fixed bugs:** + +- Change variable type to String for ssh::server::ensure [\#432](https://github.com/saz/puppet-ssh/pull/432) ([marek130](https://github.com/marek130)) + +**Closed issues:** + +- Misleading parameter [\#431](https://github.com/saz/puppet-ssh/issues/431) + +## [v14.0.1](https://github.com/saz/puppet-ssh/tree/v14.0.1) (2025-10-28) + +[Full Changelog](https://github.com/saz/puppet-ssh/compare/v14.0.0...v14.0.1) + +**Fixed bugs:** + +- Fix ssh hostkeys export after merging \#423 [\#429](https://github.com/saz/puppet-ssh/pull/429) ([saz](https://github.com/saz)) + +## [v14.0.0](https://github.com/saz/puppet-ssh/tree/v14.0.0) (2025-10-28) + +[Full Changelog](https://github.com/saz/puppet-ssh/compare/v13.1.0...v14.0.0) + +**Breaking changes:** + +- replace puppet requirement by openvox [\#425](https://github.com/saz/puppet-ssh/pull/425) ([saz](https://github.com/saz)) +- hostkeys: remove Puppet 4 workaround [\#423](https://github.com/saz/puppet-ssh/pull/423) ([kenyon](https://github.com/kenyon)) + +**Implemented enhancements:** + +- hostkeys: allow for excluding key types [\#424](https://github.com/saz/puppet-ssh/pull/424) ([kenyon](https://github.com/kenyon)) +- puppet/systemd: Allow 9.x [\#419](https://github.com/saz/puppet-ssh/pull/419) ([bastelfreak](https://github.com/bastelfreak)) +- Add Ubuntu 24.04 [\#418](https://github.com/saz/puppet-ssh/pull/418) ([bwitt](https://github.com/bwitt)) +- Parameterize mode of private key. [\#410](https://github.com/saz/puppet-ssh/pull/410) ([mojibake-umd](https://github.com/mojibake-umd)) + +**Fixed bugs:** + +- AIX: fix `ssh::client::ssh_config` setting [\#422](https://github.com/saz/puppet-ssh/pull/422) ([kenyon](https://github.com/kenyon)) +- AIX: remove nonexistent `ssh::server::ssh_known_hosts` setting [\#421](https://github.com/saz/puppet-ssh/pull/421) ([kenyon](https://github.com/kenyon)) +- ssh\_instance: write more values as comma-separated strings [\#416](https://github.com/saz/puppet-ssh/pull/416) ([kbcz1989](https://github.com/kbcz1989)) +- replace legacy facts in issue.net template, fixes \#408 [\#409](https://github.com/saz/puppet-ssh/pull/409) ([saz](https://github.com/saz)) + +**Closed issues:** + +- Add support for Ubuntu 24.04 [\#417](https://github.com/saz/puppet-ssh/issues/417) +- hostkeys: exclude ip classes for hostkey [\#413](https://github.com/saz/puppet-ssh/issues/413) +- Legacy fact hostname referenced in template erb file [\#408](https://github.com/saz/puppet-ssh/issues/408) + +**Merged pull requests:** + +- fix version numbers in HISTORY.md [\#427](https://github.com/saz/puppet-ssh/pull/427) ([saz](https://github.com/saz)) +- add HISTORY.md [\#426](https://github.com/saz/puppet-ssh/pull/426) ([saz](https://github.com/saz)) +- metadata.json [\#420](https://github.com/saz/puppet-ssh/pull/420) ([bastelfreak](https://github.com/bastelfreak)) + +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [v13.1.0] + +### Added + +- puppet/systemd: allow 8.x (#404) + +## [v13.0.0] + +### Removed + +- BREAKING CHANGE: remove Ubuntu 18.04 as supported OS (#402) + +### Fixed + +- ssh_instance: write ciphers,macs and kex as comma-separated string (#401) +- Purge and Recurse should be set together (#399) + +### Added + +- Add support for sshd_config include files (#390) + +### Changed + +- Set merge behavior of ssh::server_instances to deep (#395) + +## [v12.1.0] + +### Added + +- allow puppet/systemd < 8, fixes #382 + +### Changed + +- set sshd config mode to 0644 on AIX, fixes #371 (#383) +- use `contain` instead of `include`, fixes #367 (#387) + +### Fixed + +- fix tests on OpenBSD (#384) +- drop tag from concat_{file,fragment}, fixes #304 (#385) +- fix subsystem option if use_augeas = true, fixes #376 (#386) + +## [v12.0.1] + +### Fixed + +- make ssh::hostkeys::exclude_interfaces_re parameter work properly (#380) + +## [v12.0.0] + +### Added + +- add parameter to exclude interfaces with a regex (#378) +- Allow User to add additonal systemd options to instances (#374) + +### Changed + +- puppet/systemd: Allow 6.x (#364) + +### Fixed + +- allow ssh::server::ensure = latest, fixes #370 (#377) + +## [v11.1.0] + +### Fixed + +- write ciphers,macs and kex as comma-separated string (#362) +- Fix "No ssh_server_version_major created with OpenSSH 9.2" (#359) + +## [v11.0.0] + +### Removed + +- BREAKING CHANGE: drop support for puppet 6 + +### Changed + +- puppetlabs/concat: Allow 9.x (#354) +- puppet/systemd: Allow 5.x (#354) +- puppetlabs/stdlib: Require 9.x (#354) + +### Added + +- add Debian 12 as supported OS + +## [v10.2.0] + +### Changed + +- bump puppetlabs/concat to < 9.0.0 (#352) +- Replace deprecated functions (#350) + +## [v10.1.0] + +### Added + +- Support assigning multiple tags to a hostkey (#345) +- Add AIX support (#341) + +### Changed + +- bump puppet/systemd to < 5.0.0 (#344) + +### Fixed + +- Fix for service name on latest versions of opensuse. (#343) + +## [v10.0.0] + +### Added + +- Add support for client "match blocks" (#332, #333) +- Add data file for OpenBSD (#339) +- Add support for service_ensure/service_enable in `ssh::server::instances` (#338) + +### Changed + +- Use hiera instead of params.pp (#325, #328) + +### Fixed + +- Fix parameter lookup for `ssh::server` and `ssh::client` (#331) + +## [v9.0.0] + +### Added + +- Support for multiple instances (#318, #319, #321) - Thanks! + +### Changed + +- "hostkeys.pp" isn't marked private anymore (#317) + +## [v8.0.0] + +### Changed + +- update path to sftp server on Gentoo (#315, breaking change) + +## [v7.0.2] + +### Added + +- allow stdlib < 9.0.0 (#314) + +## [v7.0.1] + +### Fixed + +- ssh_config: Don't populate options that are set to undef (#312) + +## [v7.0.0] + +### Fixed + +- Fix grammar and spelling in various places + +### Changed + +- Use GitHub Actions instead of TravisCI +- Update module dependencies + +### Removed + +- Dropped support for puppet 4 and 5 (Breaking Change) + +## [v6.2.0] + +### Changed + +- support older facter versions (#293) + +## [v6.1.0] + +### Fixed + +- Fix absolute class name includes +- Use gid 0 instead of group name for $host_priv_key_group (#289) +- Sort hostkeys (#288) +- Do not show diff when installing a ssh private host key (#283) +- Don't populate options which have a value of `undef` (#281) + +### Added + +- document exclusion of interfaces and ipaddresses within hostkeys.pp (#267) +- add parameter to use trusted facts to hostkeys.pp (#226) + +## [v6.0.0] + +### Fixed + +- don't fail at deep_merge if hiera data not available, see #272 +- Fix typo in match_block example in README, see #271, #273 + +### Added + +- Add CHANGELOG (starting with this release), see #222 +- Test module with Puppet 6.1, see #269 + +### Changed + +- Convert `ipaddresses` to 4x API namespaced function, see #270 +- Allow `puppetlabs` `stdlib` and `concat` 6.x, see #280 + + +\* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)* diff --git a/Gemfile b/Gemfile index 74058d16..56259860 100644 --- a/Gemfile +++ b/Gemfile @@ -1,63 +1,28 @@ -source ENV['GEM_SOURCE'] || 'https://rubygems.org' +# Managed by modulesync - DO NOT EDIT +# https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ -def location_for(place, fake_version = nil) - if place =~ /^(git[:@][^#]*)#(.*)/ - [fake_version, { git: $1, branch: $2, require: false }].compact - elsif place =~ /^file:\/\/(.*)/ - ['>= 0', { path: File.expand_path($1), require: false }] - else - [place, { require: false }] - end -end +source ENV['GEM_SOURCE'] || 'https://rubygems.org' group :test do - gem 'puppetlabs_spec_helper', require: false - gem 'rspec-puppet', require: false, git: 'https://github.com/rodjek/rspec-puppet.git' - gem 'rspec-puppet-facts', require: false - gem 'rspec-puppet-utils', require: false - gem 'puppet-lint-absolute_classname-check', require: false - gem 'puppet-lint-leading_zero-check', require: false - gem 'puppet-lint-trailing_comma-check', require: false - gem 'puppet-lint-version_comparison-check', require: false - gem 'puppet-lint-classes_and_types_beginning_with_digits-check', require: false - gem 'puppet-lint-unquoted_string-check', require: false - gem 'puppet-lint-variable_contains_upcase', require: false - gem 'metadata-json-lint', require: false - gem 'puppet-blacksmith', require: false - gem 'voxpupuli-release', require: false, git: 'https://github.com/voxpupuli/voxpupuli-release-gem.git' - gem 'puppet-strings', require: false, git: 'https://github.com/puppetlabs/puppetlabs-strings.git' - gem 'rubocop-rspec', '~> 1.5', require: false if RUBY_VERSION >= '2.2.0' - gem 'json_pure', '<= 2.0.1', require: false if RUBY_VERSION < '2.0.0' - gem 'rspec-its', require: false + gem 'voxpupuli-test', '~> 13.0', :require => false + gem 'puppet_metadata', '~> 5.0', :require => false end group :development do - gem 'travis', require: false - gem 'travis-lint', require: false - gem 'guard-rake', require: false + gem 'guard-rake', :require => false + gem 'overcommit', '>= 0.39.1', :require => false end group :system_tests do - if (beaker_version = ENV['BEAKER_VERSION']) - gem 'beaker', *location_for(beaker_version) - end - if (beaker_rspec_version = ENV['BEAKER_RSPEC_VERSION']) - gem 'beaker-rspec', *location_for(beaker_rspec_version) - else - gem 'beaker-rspec', require: false - end - gem 'beaker-puppet_install_helper', require: false + gem 'voxpupuli-acceptance', '~> 4.0', :require => false end - - -if (facterversion = ENV['FACTER_GEM_VERSION']) - gem 'facter', facterversion.to_s, require: false, groups: [:test] -else - gem 'facter', require: false, groups: [:test] +group :release do + gem 'voxpupuli-release', '~> 5.0', :require => false end -ENV['PUPPET_VERSION'].nil? ? puppetversion = '~> 4.0' : puppetversion = ENV['PUPPET_VERSION'].to_s -gem 'puppet', puppetversion, require: false, groups: [:test] +gem 'rake', :require => false + +gem 'openvox', ENV.fetch('OPENVOX_GEM_VERSION', [">= 7", "< 9"]), :require => false, :groups => [:test] # vim: syntax=ruby diff --git a/HISTORY.md b/HISTORY.md new file mode 100644 index 00000000..b3b03c3d --- /dev/null +++ b/HISTORY.md @@ -0,0 +1,211 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [v13.1.0] + +### Added + +- puppet/systemd: allow 8.x (#404) + +## [v13.0.0] + +### Removed + +- BREAKING CHANGE: remove Ubuntu 18.04 as supported OS (#402) + +### Fixed + +- ssh_instance: write ciphers,macs and kex as comma-separated string (#401) +- Purge and Recurse should be set together (#399) + +### Added + +- Add support for sshd_config include files (#390) + +### Changed + +- Set merge behavior of ssh::server_instances to deep (#395) + +## [v12.1.0] + +### Added + +- allow puppet/systemd < 8, fixes #382 + +### Changed + +- set sshd config mode to 0644 on AIX, fixes #371 (#383) +- use `contain` instead of `include`, fixes #367 (#387) + +### Fixed + +- fix tests on OpenBSD (#384) +- drop tag from concat_{file,fragment}, fixes #304 (#385) +- fix subsystem option if use_augeas = true, fixes #376 (#386) + +## [v12.0.1] + +### Fixed + +- make ssh::hostkeys::exclude_interfaces_re parameter work properly (#380) + +## [v12.0.0] + +### Added + +- add parameter to exclude interfaces with a regex (#378) +- Allow User to add additonal systemd options to instances (#374) + +### Changed + +- puppet/systemd: Allow 6.x (#364) + +### Fixed + +- allow ssh::server::ensure = latest, fixes #370 (#377) + +## [v11.1.0] + +### Fixed + +- write ciphers,macs and kex as comma-separated string (#362) +- Fix "No ssh_server_version_major created with OpenSSH 9.2" (#359) + +## [v11.0.0] + +### Removed + +- BREAKING CHANGE: drop support for puppet 6 + +### Changed + +- puppetlabs/concat: Allow 9.x (#354) +- puppet/systemd: Allow 5.x (#354) +- puppetlabs/stdlib: Require 9.x (#354) + +### Added + +- add Debian 12 as supported OS + +## [v10.2.0] + +### Changed + +- bump puppetlabs/concat to < 9.0.0 (#352) +- Replace deprecated functions (#350) + +## [v10.1.0] + +### Added + +- Support assigning multiple tags to a hostkey (#345) +- Add AIX support (#341) + +### Changed + +- bump puppet/systemd to < 5.0.0 (#344) + +### Fixed + +- Fix for service name on latest versions of opensuse. (#343) + +## [v10.0.0] + +### Added + +- Add support for client "match blocks" (#332, #333) +- Add data file for OpenBSD (#339) +- Add support for service_ensure/service_enable in `ssh::server::instances` (#338) + +### Changed + +- Use hiera instead of params.pp (#325, #328) + +### Fixed + +- Fix parameter lookup for `ssh::server` and `ssh::client` (#331) + +## [v9.0.0] + +### Added + +- Support for multiple instances (#318, #319, #321) - Thanks! + +### Changed + +- "hostkeys.pp" isn't marked private anymore (#317) + +## [v8.0.0] + +### Changed + +- update path to sftp server on Gentoo (#315, breaking change) + +## [v7.0.2] + +### Added + +- allow stdlib < 9.0.0 (#314) + +## [v7.0.1] + +### Fixed + +- ssh_config: Don't populate options that are set to undef (#312) + +## [v7.0.0] + +### Fixed + +- Fix grammar and spelling in various places + +### Changed + +- Use GitHub Actions instead of TravisCI +- Update module dependencies + +### Removed + +- Dropped support for puppet 4 and 5 (Breaking Change) + +## [v6.2.0] + +### Changed + +- support older facter versions (#293) + +## [v6.1.0] + +### Fixed + +- Fix absolute class name includes +- Use gid 0 instead of group name for $host_priv_key_group (#289) +- Sort hostkeys (#288) +- Do not show diff when installing a ssh private host key (#283) +- Don't populate options which have a value of `undef` (#281) + +### Added + +- document exclusion of interfaces and ipaddresses within hostkeys.pp (#267) +- add parameter to use trusted facts to hostkeys.pp (#226) + +## [v6.0.0] + +### Fixed + +- don't fail at deep_merge if hiera data not available, see #272 +- Fix typo in match_block example in README, see #271, #273 + +### Added + +- Add CHANGELOG (starting with this release), see #222 +- Test module with Puppet 6.1, see #269 + +### Changed + +- Convert `ipaddresses` to 4x API namespaced function, see #270 +- Allow `puppetlabs` `stdlib` and `concat` 6.x, see #280 diff --git a/README.md b/README.md index 89ad0885..2f012953 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,15 @@ -# Puppet SSH [![Support via Gratipay](https://cdn.rawgit.com/gratipay/gratipay-badge/2.3.0/dist/gratipay.svg)](https://gratipay.com/~saz/) +# Puppet SSH [![Puppet Forge modules by saz](https://img.shields.io/puppetforge/mc/saz.svg)](https://forge.puppetlabs.com/saz) [![Puppet Forge](http://img.shields.io/puppetforge/v/saz/ssh.svg)](https://forge.puppetlabs.com/saz/ssh) [![Puppet Forge downloads](https://img.shields.io/puppetforge/dt/saz/ssh.svg)](https://forge.puppetlabs.com/saz/ssh) [![Puppet Forge score](https://img.shields.io/puppetforge/f/saz/ssh.svg)](https://forge.puppetlabs.com/saz/ssh) -[![Build Status](https://travis-ci.org/saz/puppet-ssh.png)](https://travis-ci.org/saz/puppet-ssh) +[![Build Status](https://github.com/saz/puppet-ssh/workflows/CI/badge.svg)](https://github.com/saz/puppet-ssh/actions?query=workflow%3ACI) Manage SSH client and server via Puppet. -Source: https://github.com/saz/puppet-ssh ## Requirements + * Exported resources for host keys management * puppetlabs/stdlib * puppetlabs/concat @@ -22,56 +22,58 @@ client and server, configuration files. Multiple occurrences of one config key (e.g. sshd should be listening on port 22 and 2222) should be passed as an array. -``` - options => { - 'Port' => [22, 2222], - } +```puppet +options => { + 'Port' => [22, 2222], +} ``` This is working for both, client and server. ### Both client, server and per user client configuration + Host keys will be collected and distributed unless `storeconfigs_enabled` is `false`. -``` - include ssh +```puppet +include ssh ``` or -``` - class { 'ssh': - storeconfigs_enabled => false, - server_options => { - 'Match User www-data' => { - 'ChrootDirectory' => '%h', - 'ForceCommand' => 'internal-sftp', - 'PasswordAuthentication' => 'yes', - 'AllowTcpForwarding' => 'no', - 'X11Forwarding' => 'no', - }, - 'Port' => [22, 2222, 2288], - }, - client_options => { - 'Host *.amazonaws.com' => { - 'User' => 'ec2-user', - }, - }, - users_client_options => { - 'bob' => { - options => { - 'Host *.alice.fr' => { - 'User' => 'alice', - }, - }, +```puppet +class { 'ssh': + storeconfigs_enabled => false, + server_options => { + 'Match User www-data' => { + 'ChrootDirectory' => '%h', + 'ForceCommand' => 'internal-sftp', + 'PasswordAuthentication' => 'yes', + 'AllowTcpForwarding' => 'no', + 'X11Forwarding' => 'no', + }, + 'Port' => [22, 2222, 2288], + }, + client_options => { + 'Host *.amazonaws.com' => { + 'User' => 'ec2-user', + }, + }, + users_client_options => { + 'bob' => { + options => { + 'Host *.alice.fr' => { + 'User' => 'alice', }, }, - } + }, + }, +} ``` ### Hiera example -``` + +```yaml ssh::storeconfigs_enabled: true ssh::server_options: @@ -84,7 +86,7 @@ ssh::server_options: UsePAM: 'yes' X11Forwarding: 'yes' -ssh::server_match_block: +ssh::server::match_block: filetransfer: type: group options: @@ -106,29 +108,30 @@ ssh::users_client_options: ``` ### Client only + Collected host keys from servers will be written to `known_hosts` unless `storeconfigs_enabled` is `false` -``` - include ssh::client +```puppet +include ssh::client ``` or -``` - class { 'ssh::client': - storeconfigs_enabled => false, - options => { - 'Host short' => { - 'User' => 'my-user', - 'HostName' => 'extreme.long.and.complicated.hostname.domain.tld', - }, - 'Host *' => { - 'User' => 'andromeda', - 'UserKnownHostsFile' => '/dev/null', - }, - }, - } +```puppet +class { 'ssh::client': + storeconfigs_enabled => false, + options => { + 'Host short' => { + 'User' => 'my-user', + 'HostName' => 'extreme.long.and.complicated.hostname.domain.tld', + }, + 'Host *' => { + 'User' => 'andromeda', + 'UserKnownHostsFile' => '/dev/null', + }, + }, +} ``` ### Per user client configuration @@ -189,80 +192,81 @@ SSH configuration file will be `/var/lib/bob/.ssh/config`. ``` ### Server only + Host keys will be collected for client distribution unless `storeconfigs_enabled` is `false` -``` - include ssh::server +```puppet +include ssh::server ``` or -``` - class { 'ssh::server': - storeconfigs_enabled => false, - options => { - 'Match User www-data' => { - 'ChrootDirectory' => '%h', - 'ForceCommand' => 'internal-sftp', - 'PasswordAuthentication' => 'yes', - 'AllowTcpForwarding' => 'no', - 'X11Forwarding' => 'no', - }, - 'PasswordAuthentication' => 'no', - 'PermitRootLogin' => 'no', - 'Port' => [22, 2222], - }, - } +```puppet +class { 'ssh::server': + storeconfigs_enabled => false, + options => { + 'Match User www-data' => { + 'ChrootDirectory' => '%h', + 'ForceCommand' => 'internal-sftp', + 'PasswordAuthentication' => 'yes', + 'AllowTcpForwarding' => 'no', + 'X11Forwarding' => 'no', + }, + 'PasswordAuthentication' => 'no', + 'PermitRootLogin' => 'no', + 'Port' => [22, 2222], + }, +} ``` ### Validate config before replacing it `validate_sshd_file` allows you to run `/usr/sbin/sshd -tf` against the sshd config file before it gets replaced, and will raise an error if the config is incorrect. -``` +```puppet class { 'ssh::server': validate_sshd_file => true, } ``` - ## Default options ### Client -``` - 'Host *' => { - 'SendEnv' => 'LANG LC_*', - 'HashKnownHosts' => 'yes', - 'GSSAPIAuthentication' => 'yes', - } +```puppet +'Host *' => { + 'SendEnv' => 'LANG LC_*', + 'HashKnownHosts' => 'yes', + 'GSSAPIAuthentication' => 'yes', +} ``` ### Server -``` - 'ChallengeResponseAuthentication' => 'no', - 'X11Forwarding' => 'yes', - 'PrintMotd' => 'no', - 'AcceptEnv' => 'LANG LC_*', - 'Subsystem' => 'sftp /usr/lib/openssh/sftp-server', - 'UsePAM' => 'yes', +```puppet +'ChallengeResponseAuthentication' => 'no', +'X11Forwarding' => 'yes', +'PrintMotd' => 'no', +'AcceptEnv' => 'LANG LC_*', +'Subsystem' => 'sftp /usr/lib/openssh/sftp-server', +'UsePAM' => 'yes', ``` ## Overwriting default options + Default options will be merged with options passed in. -If an option is set both as default and via options parameter, the latter will +If an option is set both as default and via options parameter, the latter will win. The following example will disable X11Forwarding, which is enabled by default: -``` - class { 'ssh::server': - options => { - 'X11Forwarding' => 'no', - }, - } +```puppet +class { 'ssh::server': + options => { + 'X11Forwarding' => 'no', + }, +} ``` Which will lead to the following `sshd_config` file: @@ -273,7 +277,7 @@ Which will lead to the following `sshd_config` file: ChallengeResponseAuthentication no X11Forwarding no PrintMotd no -AcceptEnv LANG LC_* +AcceptEnv LANG LC\_\* Subsystem sftp /usr/lib/openssh/sftp-server UsePAM yes PasswordAuthentication no @@ -281,12 +285,12 @@ PasswordAuthentication no Values can also be arrays, which will result in the option being specified multiple times -``` - class { 'ssh::server': - options => { - 'HostKey' => ['/etc/ssh/ssh_host_ed25519_key', '/etc/ssh/ssh_host_rsa_key'], - }, - } +```puppet +class { 'ssh::server': + options => { + 'HostKey' => ['/etc/ssh/ssh_host_ed25519_key', '/etc/ssh/ssh_host_rsa_key'], + }, +} ``` Which will lead to the following `sshd_config` file: @@ -298,16 +302,17 @@ ChallengeResponseAuthentication no HostKey /etc/ssh/ssh_host_ed25519_key HostKey /etc/ssh/ssh_host_rsa_key PrintMotd no -AcceptEnv LANG LC_* +AcceptEnv LANG LC_\* Subsystem sftp /usr/lib/openssh/sftp-server UsePAM yes PasswordAuthentication no ``` ## Defining host keys for server + You can define host keys your server will use -``` +```puppet ssh::server::host_key {'ssh_host_rsa_key': private_key_content => '', public_key_content => '', @@ -317,7 +322,7 @@ ssh::server::host_key {'ssh_host_rsa_key': Alternately, you could create the host key providing the files, instead of the content: -``` +```puppet ssh::server::host_key {'ssh_host_rsa_key': private_key_source => 'puppet:///mymodule/ssh_host_rsa_key', public_key_source => 'puppet:///mymodule/ssh_host_rsa_key.pub', @@ -327,10 +332,9 @@ ssh::server::host_key {'ssh_host_rsa_key': Both of these definitions will create ```/etc/ssh/ssh_host_rsa_key``` and ```/etc/ssh/ssh_host_rsa_key.pub``` and restart sshd daemon. - ## Adding custom match blocks -``` +```puppet class YOURCUSTOMCLASS { include ssh @@ -348,6 +352,35 @@ class YOURCUSTOMCLASS { } ``` +## Tag hostkey + +Assign tags to exported `sshkey` resources (when `ssh::storeconfigs_enabled` is set to `true`). + +```yaml +ssh::hostkeys::tags: + - hostkey_group1 + - hostkey_group2 +``` + +Host keys then can be imported using: + +```puppet +Sshkey <<| tag == "hostkey_group1" |>> +``` + +## Excluding network interfaces or ipaddresses + +Use hiera to exclude interfaces or ipaddresses from hostkey inclusion + +```yaml +ssh::hostkeys::exclude_interfaces: + - eth0 + - eth3 +ssh::hostkeys::exclude_ipaddresses: + - 192.168.0.1 + - 10.42.24.242 +``` + ## Facts This module provides facts detailing the available SSH client and server diff --git a/REFERENCE.md b/REFERENCE.md new file mode 100644 index 00000000..53c7fef3 --- /dev/null +++ b/REFERENCE.md @@ -0,0 +1,1511 @@ +# Reference + + + +## Table of Contents + +### Classes + +#### Public Classes + +* [`ssh`](#ssh): This class manages ssh client and server +* [`ssh::client`](#ssh--client): This class add ssh client management +* [`ssh::hostkeys`](#ssh--hostkeys): This class manages hostkeys +* [`ssh::knownhosts`](#ssh--knownhosts): This class manages knownhosts if collection is enabled. +* [`ssh::server`](#ssh--server): This class managed ssh server + +#### Private Classes + +* `ssh::client::config`: Manages ssh configuration +* `ssh::client::install`: Install ssh client package +* `ssh::server::config`: Managed ssh server configuration +* `ssh::server::install`: Install ssh server package +* `ssh::server::service`: This class managed ssh server service + +### Defined types + +* [`ssh::client::config::user`](#ssh--client--config--user): This defined type manages a users ssh config +* [`ssh::client::match_block`](#ssh--client--match_block): Add match_block to ssh client config (concat needed) +* [`ssh::server::config::setting`](#ssh--server--config--setting): Internal define to managed ssh server param +* [`ssh::server::config_file`](#ssh--server--config_file): Resource type for managing a config file in the include dir. +* [`ssh::server::host_key`](#ssh--server--host_key): Manage a ssh host key + +This module install a ssh host key in the server (basically, it is +a file resource but it also notifies to the ssh service) + +Important! This define does not modify any option in sshd_config, so +you have to manually define the HostKey option in the server options +if you haven't done yet. +* [`ssh::server::instances`](#ssh--server--instances): Configure separate ssh server instances +* [`ssh::server::match_block`](#ssh--server--match_block): Add match_block to ssh server config +* [`ssh::server::options`](#ssh--server--options): This defined type manages ssh server options + +### Functions + +#### Public Functions + +* [`sshclient_options_to_augeas_ssh_config`](#sshclient_options_to_augeas_ssh_config): This function will convert a key-value hash to a format understandable by the augeas sshd_config provider It will also optionally deal with k +* [`sshserver_options_to_augeas_sshd_config`](#sshserver_options_to_augeas_sshd_config): This function will convert a key-value hash to a format understandable by the augeas sshd_config provider It will also optionally deal with k + +#### Private Functions + +* `ssh::ipaddresses`: Returns ip addresses of network interfaces (except lo) found by facter. + +### Data types + +* [`Ssh::ClientMatch`](#Ssh--ClientMatch): OpenSSH client `Match` criteria. See `ssh_config(5)` + +## Classes + +### `ssh` + +} + +#### Examples + +##### Puppet usage + +```puppet +class { 'ssh': + storeconfigs_enabled => false, + server_options => { + 'Match User www-data' => { + 'ChrootDirectory' => '%h', + 'ForceCommand' => 'internal-sftp', + 'PasswordAuthentication' => 'yes', + 'AllowTcpForwarding' => 'no', + 'X11Forwarding' => 'no', + }, + 'Port' => [22, 2222, 2288], + }, + client_options => { + 'Host *.amazonaws.com' => { + 'User' => 'ec2-user', + }, + }, + users_client_options => { + 'bob' => { + options => { + 'Host *.alice.fr' => { + 'User' => 'alice', + }, + }, + }, + }, + 'server_instances' => { + 'sftp_server_init' => { + 'ensure' => 'present', + 'options' => { + 'sshd_config' => { + 'Port' => 8022, + 'Protocol' => 2, + 'AddressFamily' => 'any', + 'HostKey' => '/etc/ssh/ssh_host_rsa_key', + 'SyslogFacility' => 'AUTH', + 'LogLevel' => 'INFO', + 'PermitRootLogin' => 'no', + }, + 'sshd_service_options' => '', + 'match_blocks' => { + '*,!ssh_exempt_ldap_authkey,!sshlokey' => { + 'type' => 'group', + 'options' => { + 'AuthorizedKeysCommand' => '/usr/local/bin/getauthkey', + 'AuthorizedKeysCommandUser' => 'nobody', + 'AuthorizedKeysFile' => '/dev/null', + }, + }, + }, + }, + }, + }, +``` + +##### hiera usage + +```puppet +ssh::storeconfigs_enabled: true + +ssh::server_options: + Protocol: '2' + ListenAddress: + - '127.0.0.0' + - '%{::hostname}' + PasswordAuthentication: 'yes' + SyslogFacility: 'AUTHPRIV' + UsePAM: 'yes' + X11Forwarding: 'yes' + +ssh::server::match_block: + filetransfer: + type: group + options: + ChrootDirectory: /home/sftp + ForceCommand: internal-sftp + +ssh::client_options: + 'Host *': + SendEnv: 'LANG LC_*' + ForwardX11Trusted: 'yes' + ServerAliveInterval: '10' + +ssh::users_client_options: + 'bob': + 'options': + 'Host *.alice.fr': + 'User': 'alice' + 'PasswordAuthentication': 'no' + ssh::server::server_instances: + sftp_server_init: + ensure: present + options: + sshd_config: + Port: 8022 + Protocol: 2 + AddressFamily: 'any' + HostKey: '/etc/ssh/ssh_host_rsa_key' + SyslogFacility: 'AUTH' + LogLevel: INFO + PermitRootLogin: 'no' + sshd_service_options: '' + match_blocks: + '*,!ssh_exempt_ldap_authkey,!sshlokey': + type: group + options: + AuthorizedKeysCommand: '/usr/local/bin/getauthkey' + AuthorizedKeysCommandUser: 'nobody' + AuthorizedKeysFile: '/dev/null' +``` + +#### Parameters + +The following parameters are available in the `ssh` class: + +* [`server_options`](#-ssh--server_options) +* [`server_match_block`](#-ssh--server_match_block) +* [`client_options`](#-ssh--client_options) +* [`client_match_block`](#-ssh--client_match_block) +* [`users_client_options`](#-ssh--users_client_options) +* [`version`](#-ssh--version) +* [`storeconfigs_enabled`](#-ssh--storeconfigs_enabled) +* [`validate_sshd_file`](#-ssh--validate_sshd_file) +* [`use_augeas`](#-ssh--use_augeas) +* [`server_options_absent`](#-ssh--server_options_absent) +* [`client_options_absent`](#-ssh--client_options_absent) +* [`use_issue_net`](#-ssh--use_issue_net) +* [`purge_unmanaged_sshkeys`](#-ssh--purge_unmanaged_sshkeys) +* [`server_instances`](#-ssh--server_instances) + +##### `server_options` + +Data type: `Optional[Hash]` + +Add dynamic options for ssh server config + +Default value: `undef` + +##### `server_match_block` + +Data type: `Hash` + +Add match block for ssh server config + +Default value: `{}` + +##### `client_options` + +Data type: `Optional[Hash]` + +Add dynamic options for ssh client config + +Default value: `undef` + +##### `client_match_block` + +Data type: `Hash` + +Add match block for ssh client config + +Default value: `{}` + +##### `users_client_options` + +Data type: `Hash` + +Add users options for ssh client config + +Default value: `{}` + +##### `version` + +Data type: `String` + +Define package version (package ressource) + +Default value: `'present'` + +##### `storeconfigs_enabled` + +Data type: `Boolean` + +Default value for storeconfigs_enabled (client and server) + +Default value: `true` + +##### `validate_sshd_file` + +Data type: `Boolean` + +Default value for validate_sshd_file (server) + +Default value: `false` + +##### `use_augeas` + +Data type: `Boolean` + +Default value to use augeas (client and server) + +Default value: `false` + +##### `server_options_absent` + +Data type: `Array` + +List of options to remove for server config (augeas only) + +Default value: `[]` + +##### `client_options_absent` + +Data type: `Array` + +List of options to remove for client config (augeas only) + +Default value: `[]` + +##### `use_issue_net` + +Data type: `Boolean` + +Use issue_net header + +Default value: `false` + +##### `purge_unmanaged_sshkeys` + +Data type: `Boolean` + +Purge unmanaged sshkeys + +Default value: `true` + +##### `server_instances` + +Data type: `Hash[String[1],Hash[String[1],NotUndef]]` + +Configure SSH instances + +Default value: `{}` + +### `ssh::client` + +This class add ssh client management + +#### Examples + +##### Puppet usage + +```puppet +class { 'ssh::client': + ensure => present, + storeconfigs_enabled => true, + use_augeas => false, +} +``` + +#### Parameters + +The following parameters are available in the `ssh::client` class: + +* [`ssh_config`](#-ssh--client--ssh_config) +* [`client_package_name`](#-ssh--client--client_package_name) +* [`ensure`](#-ssh--client--ensure) +* [`storeconfigs_enabled`](#-ssh--client--storeconfigs_enabled) +* [`options`](#-ssh--client--options) +* [`use_augeas`](#-ssh--client--use_augeas) +* [`options_absent`](#-ssh--client--options_absent) +* [`default_options`](#-ssh--client--default_options) +* [`match_block`](#-ssh--client--match_block) +* [`config_user`](#-ssh--client--config_user) +* [`config_group`](#-ssh--client--config_group) + +##### `ssh_config` + +Data type: `Stdlib::Absolutepath` + +Path to ssh client config file + +##### `client_package_name` + +Data type: `Optional[String[1]]` + +Name of the client package + +Default value: `undef` + +##### `ensure` + +Data type: `String` + +Ensurable param to ssh client + +Default value: `present` + +##### `storeconfigs_enabled` + +Data type: `Boolean` + +Collected host keys from servers will be written to known_hosts unless storeconfigs_enabled is false + +Default value: `true` + +##### `options` + +Data type: `Hash` + +SSH client options, will be deep_merged with default_options. This parameter takes precedence over default_options + +Default value: `{}` + +##### `use_augeas` + +Data type: `Boolean` + +Use augeas to configure ssh client + +Default value: `false` + +##### `options_absent` + +Data type: `Array` + +Remove options (with augeas style) + +Default value: `[]` + +##### `default_options` + +Data type: `Hash` + +Default options to set, will be merged with options parameter + +##### `match_block` + +Data type: `Hash` + +Add ssh match_block (with concat) + +Default value: `{}` + +##### `config_user` + +Data type: `Variant[Integer, String[1]]` + +Numeric id or name of the user for the config file + +##### `config_group` + +Data type: `Variant[Integer, String[1]]` + +Numeric id or name of the group for the config file + +### `ssh::hostkeys` + +This class manages hostkeys + +#### Parameters + +The following parameters are available in the `ssh::hostkeys` class: + +* [`export_ipaddresses`](#-ssh--hostkeys--export_ipaddresses) +* [`storeconfigs_group`](#-ssh--hostkeys--storeconfigs_group) +* [`extra_aliases`](#-ssh--hostkeys--extra_aliases) +* [`exclude_interfaces`](#-ssh--hostkeys--exclude_interfaces) +* [`exclude_interfaces_re`](#-ssh--hostkeys--exclude_interfaces_re) +* [`exclude_ipaddresses`](#-ssh--hostkeys--exclude_ipaddresses) +* [`exclude_key_types`](#-ssh--hostkeys--exclude_key_types) +* [`use_trusted_facts`](#-ssh--hostkeys--use_trusted_facts) +* [`tags`](#-ssh--hostkeys--tags) + +##### `export_ipaddresses` + +Data type: `Boolean` + +Whether ip addresses should be added as aliases + +Default value: `true` + +##### `storeconfigs_group` + +Data type: `Optional[String[1]]` + +Tag hostkeys with this group to allow segregation + +Default value: `undef` + +##### `extra_aliases` + +Data type: `Array` + +Additional aliases to set for host keys + +Default value: `[]` + +##### `exclude_interfaces` + +Data type: `Array` + +List of interfaces to exclude + +Default value: `[]` + +##### `exclude_interfaces_re` + +Data type: `Array` + +List of regular expressions to exclude interfaces + +Default value: `[]` + +##### `exclude_ipaddresses` + +Data type: `Array` + +List of ip addresses to exclude + +Default value: `[]` + +##### `exclude_key_types` + +Data type: `Array[String[1]]` + +List of key types to exclude from exported resources. + +Default value: `[]` + +##### `use_trusted_facts` + +Data type: `Boolean` + +Whether to use trusted or normal facts + +Default value: `false` + +##### `tags` + +Data type: `Optional[Array[String[1]]]` + +Array of custom tags + +Default value: `undef` + +### `ssh::knownhosts` + +This class manages knownhosts if collection is enabled. + +#### Parameters + +The following parameters are available in the `ssh::knownhosts` class: + +* [`collect_enabled`](#-ssh--knownhosts--collect_enabled) +* [`storeconfigs_group`](#-ssh--knownhosts--storeconfigs_group) + +##### `collect_enabled` + +Data type: `Boolean` + +Enable collection + +Default value: `$ssh::knownhosts::collect_enabled` + +##### `storeconfigs_group` + +Data type: `Optional[String[1]]` + +Define the hostkeys group storage + +Default value: `undef` + +### `ssh::server` + +This class managed ssh server + +#### Examples + +##### Puppet usage + +```puppet +class { 'ssh::server': + ensure => present, + storeconfigs_enabled => true, + use_issue_net => false, +} +``` + +#### Parameters + +The following parameters are available in the `ssh::server` class: + +* [`service_name`](#-ssh--server--service_name) +* [`sshd_config`](#-ssh--server--sshd_config) +* [`sshd_dir`](#-ssh--server--sshd_dir) +* [`sshd_binary`](#-ssh--server--sshd_binary) +* [`sshd_config_mode`](#-ssh--server--sshd_config_mode) +* [`host_priv_key_user`](#-ssh--server--host_priv_key_user) +* [`host_priv_key_group`](#-ssh--server--host_priv_key_group) +* [`host_priv_key_mode`](#-ssh--server--host_priv_key_mode) +* [`config_user`](#-ssh--server--config_user) +* [`config_group`](#-ssh--server--config_group) +* [`default_options`](#-ssh--server--default_options) +* [`ensure`](#-ssh--server--ensure) +* [`include_dir`](#-ssh--server--include_dir) +* [`include_dir_mode`](#-ssh--server--include_dir_mode) +* [`include_dir_purge`](#-ssh--server--include_dir_purge) +* [`config_files`](#-ssh--server--config_files) +* [`storeconfigs_enabled`](#-ssh--server--storeconfigs_enabled) +* [`options`](#-ssh--server--options) +* [`validate_sshd_file`](#-ssh--server--validate_sshd_file) +* [`use_augeas`](#-ssh--server--use_augeas) +* [`options_absent`](#-ssh--server--options_absent) +* [`match_block`](#-ssh--server--match_block) +* [`use_issue_net`](#-ssh--server--use_issue_net) +* [`sshd_environments_file`](#-ssh--server--sshd_environments_file) +* [`server_package_name`](#-ssh--server--server_package_name) + +##### `service_name` + +Data type: `String[1]` + +Name of the sshd service + +##### `sshd_config` + +Data type: `Stdlib::Absolutepath` + +Path to the sshd_config file + +##### `sshd_dir` + +Data type: `Stdlib::Absolutepath` + +Path to the sshd dir (e.g. /etc/ssh) + +##### `sshd_binary` + +Data type: `Stdlib::Absolutepath` + +Path to the sshd binary + +##### `sshd_config_mode` + +Data type: `Stdlib::Filemode` + +Mode to set on the sshd config file + +##### `host_priv_key_user` + +Data type: `Variant[Integer, String[1]]` + +Numeric id or name of the user for the private host key + +##### `host_priv_key_group` + +Data type: `Variant[Integer, String[1]]` + +Numeric id or name of the group for the private host key + +##### `host_priv_key_mode` + +Data type: `Stdlib::Filemode` + +Mode of the private host key + +##### `config_user` + +Data type: `Variant[Integer, String[1]]` + +Numeric id or name of the user for the sshd config file + +##### `config_group` + +Data type: `Variant[Integer, String[1]]` + +Numeric id or name of the group for the sshd config file + +##### `default_options` + +Data type: `Hash` + +Default options to set, will be merged with options parameter + +##### `ensure` + +Data type: `String` + +Ensurable param to ssh server + +Default value: `present` + +##### `include_dir` + +Data type: `Optional[Stdlib::Absolutepath]` + +Path to sshd include directory. + +Default value: `undef` + +##### `include_dir_mode` + +Data type: `Stdlib::Filemode` + +Mode to set on the sshd include directory. + +Default value: `'0700'` + +##### `include_dir_purge` + +Data type: `Boolean` + +Purge the include directory if true. + +Default value: `true` + +##### `config_files` + +Data type: `Hash[String, Hash]` + +Hash of config files to add to the ssh include directory. + +Default value: `{}` + +##### `storeconfigs_enabled` + +Data type: `Boolean` + +Host keys will be collected and distributed unless storeconfigs_enabled is false. + +Default value: `true` + +##### `options` + +Data type: `Hash` + +Dynamic hash for openssh server option + +Default value: `{}` + +##### `validate_sshd_file` + +Data type: `Boolean` + +Add sshd file validate cmd + +Default value: `false` + +##### `use_augeas` + +Data type: `Boolean` + +Use augeas for configuration (default concat) + +Default value: `false` + +##### `options_absent` + +Data type: `Array` + +Remove options (with augeas style) + +Default value: `[]` + +##### `match_block` + +Data type: `Hash` + +Add sshd match_block (with concat) + +Default value: `{}` + +##### `use_issue_net` + +Data type: `Boolean` + +Add issue_net banner + +Default value: `false` + +##### `sshd_environments_file` + +Data type: `Optional[Stdlib::Absolutepath]` + +Path to a sshd environments file (e.g. /etc/defaults/ssh on Debian) + +Default value: `undef` + +##### `server_package_name` + +Data type: `Optional[String[1]]` + +Name of the server package to install + +Default value: `undef` + +## Defined types + +### `ssh::client::config::user` + +Copyright (c) IN2P3 Computing Centre, IN2P3, CNRS +Contributor: Remi Ferrand (2015) +Contributor: Tim Meusel (2017) + +#### Parameters + +The following parameters are available in the `ssh::client::config::user` defined type: + +* [`ensure`](#-ssh--client--config--user--ensure) +* [`target`](#-ssh--client--config--user--target) +* [`user_home_dir`](#-ssh--client--config--user--user_home_dir) +* [`manage_user_ssh_dir`](#-ssh--client--config--user--manage_user_ssh_dir) +* [`options`](#-ssh--client--config--user--options) +* [`user`](#-ssh--client--config--user--user) +* [`ssh_directory_default_mode`](#-ssh--client--config--user--ssh_directory_default_mode) +* [`ssh_config_default_mode`](#-ssh--client--config--user--ssh_config_default_mode) + +##### `ensure` + +Data type: `Enum['present', 'absent']` + +Specifies whether the config file should be present or absent + +Default value: `present` + +##### `target` + +Data type: `Optional[Stdlib::Absolutepath]` + +Sets the config file location, defaults to `~/.ssh/config` if $target and $user_home_dir are not set + +Default value: `undef` + +##### `user_home_dir` + +Data type: `Optional[Stdlib::Absolutepath]` + +Sets the location of users home dir, defaults to `/home/$user` + +Default value: `undef` + +##### `manage_user_ssh_dir` + +Data type: `Boolean` + +Whether the users ssh dir should be managed or not + +Default value: `true` + +##### `options` + +Data type: `Hash` + +Options which should be set + +Default value: `{}` + +##### `user` + +Data type: `String[1]` + +The name of the user the config should be managed for + +Default value: `$name` + +##### `ssh_directory_default_mode` + +Data type: `String[1]` + +Default mode for the users ssh dir + +Default value: `'0700'` + +##### `ssh_config_default_mode` + +Data type: `String[1]` + +Default mode for the ssh config file + +Default value: `'0600'` + +### `ssh::client::match_block` + +Add match_block to ssh client config (concat needed) + +#### Parameters + +The following parameters are available in the `ssh::client::match_block` defined type: + +* [`options`](#-ssh--client--match_block--options) +* [`type`](#-ssh--client--match_block--type) +* [`order`](#-ssh--client--match_block--order) +* [`target`](#-ssh--client--match_block--target) + +##### `options` + +Data type: `Hash` + +Options which should be set + +Default value: `{}` + +##### `type` + +Data type: `Ssh::ClientMatch` + +Type of match_block, e.g. user, group, host, ... + +Default value: `'user'` + +##### `order` + +Data type: `Integer` + +Orders your settings within the config file + +Default value: `50` + +##### `target` + +Data type: `Stdlib::Absolutepath` + +Sets the target file of the concat fragment + +Default value: `$ssh::client::ssh_config` + +### `ssh::server::config::setting` + +Internal define to managed ssh server param + +#### Parameters + +The following parameters are available in the `ssh::server::config::setting` defined type: + +* [`key`](#-ssh--server--config--setting--key) +* [`value`](#-ssh--server--config--setting--value) +* [`order`](#-ssh--server--config--setting--order) + +##### `key` + +Data type: `String[1]` + +Key of the value which should be set + +##### `value` + +Data type: `Variant[Boolean, Array, Hash, String]` + +Value which should be set + +##### `order` + +Data type: `Variant[String[1], Integer]` + +Orders your setting within the config file + +Default value: `'10'` + +### `ssh::server::config_file` + +Resource type for managing a config file in the include dir. + +#### Parameters + +The following parameters are available in the `ssh::server::config_file` defined type: + +* [`mode`](#-ssh--server--config_file--mode) +* [`include`](#-ssh--server--config_file--include) +* [`options`](#-ssh--server--config_file--options) +* [`path`](#-ssh--server--config_file--path) + +##### `mode` + +Data type: `Stdlib::Filemode` + +File mode for the config file. + +Default value: `$ssh::server::sshd_config_mode` + +##### `include` + +Data type: `Optional[Stdlib::Absolutepath]` + +Absolute path to config file to include at the top of the config file. This +is intended for including files not managed by this module (crypto policies). + +Default value: `undef` + +##### `options` + +Data type: `Hash` + +Dynamic hash for openssh server option + +Default value: `{}` + +##### `path` + +Data type: `Stdlib::Absolutepath` + + + +Default value: `"${ssh::server::include_dir}/${name}.conf"` + +### `ssh::server::host_key` + +Manage a ssh host key + +This module install a ssh host key in the server (basically, it is +a file resource but it also notifies to the ssh service) + +Important! This define does not modify any option in sshd_config, so +you have to manually define the HostKey option in the server options +if you haven't done yet. + +#### Parameters + +The following parameters are available in the `ssh::server::host_key` defined type: + +* [`ensure`](#-ssh--server--host_key--ensure) +* [`public_key_source`](#-ssh--server--host_key--public_key_source) +* [`public_key_content`](#-ssh--server--host_key--public_key_content) +* [`private_key_source`](#-ssh--server--host_key--private_key_source) +* [`private_key_content`](#-ssh--server--host_key--private_key_content) +* [`certificate_source`](#-ssh--server--host_key--certificate_source) +* [`certificate_content`](#-ssh--server--host_key--certificate_content) + +##### `ensure` + +Data type: `Enum[present, absent]` + +Set to 'absent' to remove host_key files + +Default value: `'present'` + +##### `public_key_source` + +Data type: `Optional[String[1]]` + +Sets the content of the source parameter for the public key file +Note public_key_source and public_key_content are mutually exclusive. + +Default value: `undef` + +##### `public_key_content` + +Data type: `Optional[String[1]]` + +Sets the content for the public key file. +Note public_key_source and public_key_content are mutually exclusive. + +Default value: `undef` + +##### `private_key_source` + +Data type: `Optional[String[1]]` + +Sets the content of the source parameter for the private key file +Note private_key_source and private_key_content are mutually exclusive. + +Default value: `undef` + +##### `private_key_content` + +Data type: `Optional[String[1]]` + +Sets the content for the private key file. +Note private_key_source and private_key_content are mutually exclusive. + +Default value: `undef` + +##### `certificate_source` + +Data type: `Optional[String[1]]` + +Sets the content of the source parameter for the host key certificate. +Note certificate_source and certificate_content are mutually exclusive. + +Default value: `undef` + +##### `certificate_content` + +Data type: `Optional[String[1]]` + +Sets the content for the host key certificate. +Note certificate_source and certificate_content are mutually exclusive. + +Default value: `undef` + +### `ssh::server::instances` + +Configure separate ssh server instances + +#### Parameters + +The following parameters are available in the `ssh::server::instances` defined type: + +* [`ensure`](#-ssh--server--instances--ensure) +* [`options`](#-ssh--server--instances--options) +* [`service_ensure`](#-ssh--server--instances--service_ensure) +* [`service_enable`](#-ssh--server--instances--service_enable) +* [`validate_config_file`](#-ssh--server--instances--validate_config_file) +* [`sshd_instance_config_file`](#-ssh--server--instances--sshd_instance_config_file) +* [`sshd_binary`](#-ssh--server--instances--sshd_binary) +* [`sshd_environments_file`](#-ssh--server--instances--sshd_environments_file) + +##### `ensure` + +Data type: `Enum[present, absent]` + +Specifies whether the instance should be added or removed + +Default value: `present` + +##### `options` + +Data type: `Hash` + +Set options for the instance + +Default value: `{}` + +##### `service_ensure` + +Data type: `Stdlib::Ensure::Service` + +Whether this instance service should be running or stopped, defaults to true when ensure is set to present, otherwise false + +Default value: `$ensure ? { 'present' => 'running', 'absent' => 'stopped'` + +##### `service_enable` + +Data type: `Boolean` + +Whether this instance service should be started at boot. Will be added automatically if ensure is running/removed if ensure is stopped + +Default value: `($service_ensure == 'running'` + +##### `validate_config_file` + +Data type: `Boolean` + +Validate config file before applying + +Default value: `false` + +##### `sshd_instance_config_file` + +Data type: `Stdlib::Absolutepath` + +Path of the instance sshd config + +Default value: `"${ssh::server::sshd_dir}/sshd_config.${title}"` + +##### `sshd_binary` + +Data type: `Stdlib::Absolutepath` + +Path to sshd binary + +Default value: `$ssh::server::sshd_binary` + +##### `sshd_environments_file` + +Data type: `Optional[Stdlib::Absolutepath]` + +Path to environments file, if any + +Default value: `$ssh::server::sshd_environments_file` + +### `ssh::server::match_block` + +Add match_block to ssh server config + +#### Parameters + +The following parameters are available in the `ssh::server::match_block` defined type: + +* [`options`](#-ssh--server--match_block--options) +* [`type`](#-ssh--server--match_block--type) +* [`order`](#-ssh--server--match_block--order) +* [`target`](#-ssh--server--match_block--target) + +##### `options` + +Data type: `Hash` + +Options which should be set + +Default value: `{}` + +##### `type` + +Data type: `String[1]` + +Type of match_block, e.g. user, group, host, ... + +Default value: `'user'` + +##### `order` + +Data type: `Integer` + +Orders your settings within the config file + +Default value: `50` + +##### `target` + +Data type: `Stdlib::Absolutepath` + +Sets the target file of the concat fragment + +Default value: `$ssh::server::sshd_config` + +### `ssh::server::options` + +This defined type manages ssh server options + +#### Parameters + +The following parameters are available in the `ssh::server::options` defined type: + +* [`options`](#-ssh--server--options--options) +* [`order`](#-ssh--server--options--order) + +##### `options` + +Data type: `Hash` + +Options which should be set + +Default value: `{}` + +##### `order` + +Data type: `Integer` + +Orders your settings within the config file + +Default value: `50` + +## Functions + +### `sshclient_options_to_augeas_ssh_config` + +Type: Ruby 3.x API + +This function will convert a key-value hash to a format understandable by the augeas sshd_config provider +It will also optionally deal with keys that should be absent, and inject static parameters if supplied. + +Usage: sshclient_options_to_augeas_ssh_config($options_present, $options_absent, $other_parameters) +- $options_hash is mandatory and must be a hash. +- $options_absent is optional and can be either a single value or an array. +- $other_parameters is optional and must be a hash. + +Example: +$options = { + 'Host *.example.com' => { + 'ForwardAgent' => 'yes', + 'BatchMode' => 'yes', + }, + 'ForwardAgent' => 'no', + 'BatchMode' => 'no', + 'StrictHostKeyChecking' => 'no', + } +$options_absent = ['StrictHostKeyChecking','NoneField'] +$other_parameters = { 'target' => '/etc/ssh/ssh_config' } + +$options_final_augeas = sshclient_options_to_augeas_ssh_config($options, $options_absent, $other_parameters) + +In this case, the value of $options_final_augeas would be: + +'ForwardAgent *.example.com' => { + 'ensure' => 'present', + 'host' => '*.example.com', + 'key' => 'ForwardAgent', + 'value' => 'yes', + 'target' => '/etc/ssh/ssh_config', + } +'BatchMode *.example.com' => { + 'ensure' => 'present', + 'host' => '*.example.com', + 'key' => 'BatchMode', + 'value' => 'yes', + 'target' => '/etc/ssh/ssh_config', + } +'ForwardAgent' => { + 'ensure' => 'present', + 'key' => 'ForwardAgent', + 'value' => 'yes', + 'target' => '/etc/ssh/ssh_config', + } +'BatchMode' => { + 'ensure' => 'present', + 'key' => 'BatchMode', + 'value' => 'yes', + 'target' => '/etc/ssh/ssh_config', + } +'StrictHostKeyChecking' => { + 'ensure' => 'absent', + 'key' => 'StrictHostKeyChecking', + 'target' => '/etc/ssh/ssh_config', + } + 'NoneField' => { + 'ensure' => 'absent', + 'key' => 'NoneField', + 'target' => '/etc/ssh/ssh_config', + } + +Note how the word "Host" is stripped a + +#### `sshclient_options_to_augeas_ssh_config()` + +This function will convert a key-value hash to a format understandable by the augeas sshd_config provider +It will also optionally deal with keys that should be absent, and inject static parameters if supplied. + +Usage: sshclient_options_to_augeas_ssh_config($options_present, $options_absent, $other_parameters) +- $options_hash is mandatory and must be a hash. +- $options_absent is optional and can be either a single value or an array. +- $other_parameters is optional and must be a hash. + +Example: +$options = { + 'Host *.example.com' => { + 'ForwardAgent' => 'yes', + 'BatchMode' => 'yes', + }, + 'ForwardAgent' => 'no', + 'BatchMode' => 'no', + 'StrictHostKeyChecking' => 'no', + } +$options_absent = ['StrictHostKeyChecking','NoneField'] +$other_parameters = { 'target' => '/etc/ssh/ssh_config' } + +$options_final_augeas = sshclient_options_to_augeas_ssh_config($options, $options_absent, $other_parameters) + +In this case, the value of $options_final_augeas would be: + +'ForwardAgent *.example.com' => { + 'ensure' => 'present', + 'host' => '*.example.com', + 'key' => 'ForwardAgent', + 'value' => 'yes', + 'target' => '/etc/ssh/ssh_config', + } +'BatchMode *.example.com' => { + 'ensure' => 'present', + 'host' => '*.example.com', + 'key' => 'BatchMode', + 'value' => 'yes', + 'target' => '/etc/ssh/ssh_config', + } +'ForwardAgent' => { + 'ensure' => 'present', + 'key' => 'ForwardAgent', + 'value' => 'yes', + 'target' => '/etc/ssh/ssh_config', + } +'BatchMode' => { + 'ensure' => 'present', + 'key' => 'BatchMode', + 'value' => 'yes', + 'target' => '/etc/ssh/ssh_config', + } +'StrictHostKeyChecking' => { + 'ensure' => 'absent', + 'key' => 'StrictHostKeyChecking', + 'target' => '/etc/ssh/ssh_config', + } + 'NoneField' => { + 'ensure' => 'absent', + 'key' => 'NoneField', + 'target' => '/etc/ssh/ssh_config', + } + +Note how the word "Host" is stripped a + +Returns: `Any` + +### `sshserver_options_to_augeas_sshd_config` + +Type: Ruby 3.x API + +This function will convert a key-value hash to a format understandable by the augeas sshd_config provider +It will also optionally deal with keys that should be absent, and inject static parameters if supplied. + +Usage: sshserver_options_to_augeas_sshd_config($options_present, $options_absent, $other_parameters) +- $options_hash is mandatory and must be a hash. +- $options_absent is optional and can be either a single value or an array. +- $other_parameters is optional and must be a hash. + +Example: +$options = { + 'Match User www-data' => { + 'PasswordAuthentication' => 'yes', + 'X11Forwarding' => 'no', + }, + 'Match Group bamboo' => { + 'ForcedCommand' => '/bin/echo hello world', + }, + 'X11Forwarding' => 'yes', + 'DebianBanner' => '/etc/banner.net', + 'AllowGroups' => ["sshgroups", "admins"], + } +$options_absent = ['DebianBanner','NoneField'] +$other_parameters = { 'target' => '/etc/ssh/sshd_config' } + +$options_final_augeas = sshserver_options_to_augeas_sshd_config($options, $options_absent, $other_parameters) + +In this case, the value of $options_final_augeas would be: + +'PasswordAuthentication User www-data' => { + 'ensure' => 'present', + 'condition' => 'User www-data', + 'key' => 'PasswordAuthentication', + 'value' => 'yes', + 'target' => '/etc/ssh/sshd_config', + } + 'X11Forwarding User www-data' => { + 'ensure' => 'present', + 'condition' => 'User www-data', + 'key' => 'X11Forwarding', + 'value' => 'no', + 'target' => '/etc/ssh/sshd_config', + } + 'ForcedCommand Group bamboo' => { + 'ensure' => 'present', + 'condition' => 'Group bamboo', + 'key' => 'ForcedCommand', + 'value' => '/bin/echo hello world', + 'target' => '/etc/ssh/sshd_config', + } + 'X11Forwarding' => { + 'ensure' => 'present', + 'key' => 'X11Forwarding', + 'value' => 'yes', + 'target' => '/etc/ssh/sshd_config', + } + 'DebianBanner' => { + 'ensure' => 'absent', + 'key' => 'DebianBanner', + 'target' => '/etc/ssh/sshd_config', + } + 'AllowGroups' => { + 'ensure' => 'present', + 'key' => 'AllowGroups', + 'value' => ['sshgroups','admins'], + 'target' => '/etc/ssh/sshd_config', + } + 'NoneField' => { + 'ensure' => 'absent', + 'key' => 'NoneField', + 'target' => '/etc/ssh/sshd_config', + } + +Note how the word "Match" is stripped a + +#### `sshserver_options_to_augeas_sshd_config()` + +This function will convert a key-value hash to a format understandable by the augeas sshd_config provider +It will also optionally deal with keys that should be absent, and inject static parameters if supplied. + +Usage: sshserver_options_to_augeas_sshd_config($options_present, $options_absent, $other_parameters) +- $options_hash is mandatory and must be a hash. +- $options_absent is optional and can be either a single value or an array. +- $other_parameters is optional and must be a hash. + +Example: +$options = { + 'Match User www-data' => { + 'PasswordAuthentication' => 'yes', + 'X11Forwarding' => 'no', + }, + 'Match Group bamboo' => { + 'ForcedCommand' => '/bin/echo hello world', + }, + 'X11Forwarding' => 'yes', + 'DebianBanner' => '/etc/banner.net', + 'AllowGroups' => ["sshgroups", "admins"], + } +$options_absent = ['DebianBanner','NoneField'] +$other_parameters = { 'target' => '/etc/ssh/sshd_config' } + +$options_final_augeas = sshserver_options_to_augeas_sshd_config($options, $options_absent, $other_parameters) + +In this case, the value of $options_final_augeas would be: + +'PasswordAuthentication User www-data' => { + 'ensure' => 'present', + 'condition' => 'User www-data', + 'key' => 'PasswordAuthentication', + 'value' => 'yes', + 'target' => '/etc/ssh/sshd_config', + } + 'X11Forwarding User www-data' => { + 'ensure' => 'present', + 'condition' => 'User www-data', + 'key' => 'X11Forwarding', + 'value' => 'no', + 'target' => '/etc/ssh/sshd_config', + } + 'ForcedCommand Group bamboo' => { + 'ensure' => 'present', + 'condition' => 'Group bamboo', + 'key' => 'ForcedCommand', + 'value' => '/bin/echo hello world', + 'target' => '/etc/ssh/sshd_config', + } + 'X11Forwarding' => { + 'ensure' => 'present', + 'key' => 'X11Forwarding', + 'value' => 'yes', + 'target' => '/etc/ssh/sshd_config', + } + 'DebianBanner' => { + 'ensure' => 'absent', + 'key' => 'DebianBanner', + 'target' => '/etc/ssh/sshd_config', + } + 'AllowGroups' => { + 'ensure' => 'present', + 'key' => 'AllowGroups', + 'value' => ['sshgroups','admins'], + 'target' => '/etc/ssh/sshd_config', + } + 'NoneField' => { + 'ensure' => 'absent', + 'key' => 'NoneField', + 'target' => '/etc/ssh/sshd_config', + } + +Note how the word "Match" is stripped a + +Returns: `Any` + +## Data types + +### `Ssh::ClientMatch` + +OpenSSH client `Match` criteria. See `ssh_config(5)` + +Alias of `Enum['!all', 'all', '!canonical', 'canonical', '!exec', 'exec', '!final', 'final', '!host', 'host', '!localuser', 'localuser', '!originalhost', 'originalhost', '!user', 'user']` + diff --git a/Rakefile b/Rakefile index ec672404..6922b675 100644 --- a/Rakefile +++ b/Rakefile @@ -1,44 +1,36 @@ -require 'puppetlabs_spec_helper/rake_tasks' -require 'puppet_blacksmith/rake_tasks' -require 'voxpupuli/release/rake_tasks' -require 'puppet-strings/rake_tasks' +# Managed by modulesync - DO NOT EDIT +# https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ -if RUBY_VERSION >= '2.2.0' - require 'rubocop/rake_task' - - RuboCop::RakeTask.new(:rubocop) do |task| - # These make the rubocop experience maybe slightly less terrible - task.options = ['-D', '-S', '-E'] - end +begin + require 'voxpupuli/test/rake' +rescue LoadError + # only available if gem group test is installed end -PuppetLint.configuration.log_format = '%{path}:%{linenumber}:%{check}:%{KIND}:%{message}' -PuppetLint.configuration.fail_on_warnings = true -PuppetLint.configuration.send('relative') -PuppetLint.configuration.send('disable_140chars') -PuppetLint.configuration.send('disable_class_inherits_from_params_class') -PuppetLint.configuration.send('disable_documentation') -PuppetLint.configuration.send('disable_single_quote_string_with_variables') +begin + require 'voxpupuli/acceptance/rake' +rescue LoadError + # only available if gem group acceptance is installed +end -exclude_paths = %w( - pkg/**/* - vendor/**/* - .vendor/**/* - spec/**/* -) -PuppetLint.configuration.ignore_paths = exclude_paths -PuppetSyntax.exclude_paths = exclude_paths +begin + require 'voxpupuli/release/rake_tasks' +rescue LoadError + # only available if gem group releases is installed +else + GCGConfig.user = 'saz' + GCGConfig.project = 'puppet-ssh' +end -desc 'Run acceptance tests' -RSpec::Core::RakeTask.new(:acceptance) do |t| - t.pattern = 'spec/acceptance' +desc "Run main 'test' task and report merged results to coveralls" +task test_with_coveralls: [:test] do + if Dir.exist?(File.expand_path('../lib', __FILE__)) + require 'coveralls/rake/task' + Coveralls::RakeTask.new + Rake::Task['coveralls:push'].invoke + else + puts 'Skipping reporting to coveralls. Module has no lib dir' + end end -desc 'Run tests metadata_lint, lint, syntax, spec' -task test: [ - :metadata_lint, - :lint, - :syntax, - :spec, -] # vim: syntax=ruby diff --git a/data/AIX.yaml b/data/AIX.yaml new file mode 100644 index 00000000..21c3df57 --- /dev/null +++ b/data/AIX.yaml @@ -0,0 +1,15 @@ +--- +ssh::server::sshd_dir: '/etc/ssh' +ssh::server::sshd_binary: '/usr/sbin/sshd' +ssh::server::sshd_config: '/etc/ssh/sshd_config' +ssh::server::sshd_config_mode: '0644' +ssh::client::ssh_config: '/etc/ssh/ssh_config' +ssh::server::service_name: 'sshd' +ssh::sftp_server_path: '/usr/sbin/sftp-server' +ssh::server::host_priv_key_group: 0 +ssh::server::default_options: + X11Forwarding: 'yes' + PrintMotd: 'no' + AcceptEnv: 'LANG LC_*' + Subsystem: "sftp %{lookup('ssh::sftp_server_path')}" + UsePAM: 'no' diff --git a/data/Amazon.yaml b/data/Amazon.yaml new file mode 100644 index 00000000..a2042c3d --- /dev/null +++ b/data/Amazon.yaml @@ -0,0 +1,11 @@ +--- +ssh::server::server_package_name: 'openssh-server' +ssh::client::client_package_name: 'openssh-clients' +ssh::server::sshd_dir: '/etc/ssh' +ssh::server::sshd_binary: '/usr/sbin/sshd' +ssh::server::sshd_environments_file: '/etc/sysconfig/sshd' +ssh::server::sshd_config: '/etc/ssh/sshd_config' +ssh::client::ssh_config: '/etc/ssh/ssh_config' +ssh::server::service_name: 'sshd' +ssh::sftp_server_path: '/usr/libexec/openssh/sftp-server' +ssh::server::host_priv_key_group: 0 diff --git a/data/Archlinux.yaml b/data/Archlinux.yaml new file mode 100644 index 00000000..3255fb6d --- /dev/null +++ b/data/Archlinux.yaml @@ -0,0 +1,10 @@ +--- +ssh::server::server_package_name: 'openssh' +ssh::client::client_package_name: 'openssh' +ssh::server::sshd_dir: '/etc/ssh' +ssh::server::sshd_binary: '/usr/bin/sshd' +ssh::server::sshd_config: '/etc/ssh/sshd_config' +ssh::client::ssh_config: '/etc/ssh/ssh_config' +ssh::server::service_name: 'sshd.service' +ssh::sftp_server_path: '/usr/lib/ssh/sftp-server' +ssh::server::host_priv_key_group: 0 diff --git a/data/Darwin.yaml b/data/Darwin.yaml new file mode 100644 index 00000000..5a6a4610 --- /dev/null +++ b/data/Darwin.yaml @@ -0,0 +1,7 @@ +--- +ssh::server::sshd_dir: '/etc/ssh' +ssh::server::sshd_config: '/etc/ssh/sshd_config' +ssh::client::ssh_config: '/etc/ssh/ssh_config' +ssh::server::service_name: 'com.openssh.sshd' +ssh::sftp_server_path: '/usr/libexec/sftp-server' +ssh::server::host_priv_key_group: 0 diff --git a/data/Debian.yaml b/data/Debian.yaml new file mode 100644 index 00000000..e59e67ab --- /dev/null +++ b/data/Debian.yaml @@ -0,0 +1,11 @@ +--- +ssh::server::server_package_name: 'openssh-server' +ssh::client::client_package_name: 'openssh-client' +ssh::server::sshd_dir: '/etc/ssh' +ssh::server::sshd_binary: '/usr/sbin/sshd' +ssh::server::sshd_config: '/etc/ssh/sshd_config' +ssh::server::sshd_environments_file: '/etc/default/ssh' +ssh::client::ssh_config: '/etc/ssh/ssh_config' +ssh::server::service_name: 'ssh' +ssh::sftp_server_path: '/usr/lib/openssh/sftp-server' +ssh::server::host_priv_key_group: 0 diff --git a/data/DragonFly.yaml b/data/DragonFly.yaml new file mode 100644 index 00000000..d7d94bc4 --- /dev/null +++ b/data/DragonFly.yaml @@ -0,0 +1,8 @@ +--- +ssh::server::sshd_dir: '/etc/ssh' +ssh::server::sshd_binary: '/usr/local/sbin/sshd' +ssh::server::sshd_config: '/etc/ssh/sshd_config' +ssh::client::ssh_config: '/etc/ssh/ssh_config' +ssh::server::service_name: 'sshd' +ssh::sftp_server_path: '/usr/libexec/sftp-server' +ssh::server::host_priv_key_group: 0 diff --git a/data/FreeBSD.yaml b/data/FreeBSD.yaml new file mode 100644 index 00000000..d7d94bc4 --- /dev/null +++ b/data/FreeBSD.yaml @@ -0,0 +1,8 @@ +--- +ssh::server::sshd_dir: '/etc/ssh' +ssh::server::sshd_binary: '/usr/local/sbin/sshd' +ssh::server::sshd_config: '/etc/ssh/sshd_config' +ssh::client::ssh_config: '/etc/ssh/ssh_config' +ssh::server::service_name: 'sshd' +ssh::sftp_server_path: '/usr/libexec/sftp-server' +ssh::server::host_priv_key_group: 0 diff --git a/data/Gentoo.yaml b/data/Gentoo.yaml new file mode 100644 index 00000000..37014c39 --- /dev/null +++ b/data/Gentoo.yaml @@ -0,0 +1,10 @@ +--- +ssh::server::server_package_name: 'openssh' +ssh::client::client_package_name: 'openssh' +ssh::server::sshd_dir: '/etc/ssh' +ssh::server::sshd_binary: '/usr/sbin/sshd' +ssh::server::sshd_config: '/etc/ssh/sshd_config' +ssh::client::ssh_config: '/etc/ssh/ssh_config' +ssh::server::service_name: 'sshd' +ssh::sftp_server_path: '/usr/lib64/misc/sftp-server' +ssh::server::host_priv_key_group: 0 diff --git a/data/OpenBSD.yaml b/data/OpenBSD.yaml new file mode 100644 index 00000000..e96aff2a --- /dev/null +++ b/data/OpenBSD.yaml @@ -0,0 +1,13 @@ +--- +ssh::server::sshd_dir: '/etc/ssh' +ssh::server::sshd_binary: '/usr/sbin/sshd' +ssh::server::sshd_config: '/etc/ssh/sshd_config' +ssh::client::ssh_config: '/etc/ssh/ssh_config' +ssh::server::service_name: 'sshd' +ssh::sftp_server_path: '/usr/libexec/sftp-server' +ssh::server::host_priv_key_group: 0 +ssh::server::default_options: + X11Forwarding: 'yes' + PrintMotd: 'no' + AcceptEnv: 'LANG LC_*' + Subsystem: "sftp %{lookup('ssh::sftp_server_path')}" diff --git a/data/OpenSuSE.yaml b/data/OpenSuSE.yaml new file mode 100644 index 00000000..41400cef --- /dev/null +++ b/data/OpenSuSE.yaml @@ -0,0 +1,3 @@ +--- +ssh::server::service_name: 'sshd' +ssh::sftp_server_path: '/usr/lib/ssh/sftp-server' diff --git a/data/RedHat-9.yaml b/data/RedHat-9.yaml new file mode 100644 index 00000000..8a6b09e5 --- /dev/null +++ b/data/RedHat-9.yaml @@ -0,0 +1,5 @@ +--- +ssh::server::include_dir: '/etc/ssh/sshd_config.d' +ssh::server::config_files: + 50-redhat: + include: '/etc/crypto-policies/back-ends/opensshserver.config' diff --git a/data/RedHat.yaml b/data/RedHat.yaml new file mode 100644 index 00000000..81138ce0 --- /dev/null +++ b/data/RedHat.yaml @@ -0,0 +1,11 @@ +--- +ssh::server::server_package_name: 'openssh-server' +ssh::client::client_package_name: 'openssh-clients' +ssh::server::sshd_dir: '/etc/ssh' +ssh::server::sshd_binary: '/usr/sbin/sshd' +ssh::server::sshd_config: '/etc/ssh/sshd_config' +ssh::server::sshd_environments_file: '/etc/sysconfig/sshd' +ssh::client::ssh_config: '/etc/ssh/ssh_config' +ssh::server::service_name: 'sshd' +ssh::sftp_server_path: '/usr/libexec/openssh/sftp-server' +ssh::server::host_priv_key_group: 0 diff --git a/data/SLES-10-x86_64.yaml b/data/SLES-10-x86_64.yaml new file mode 100644 index 00000000..cc0f1a61 --- /dev/null +++ b/data/SLES-10-x86_64.yaml @@ -0,0 +1,2 @@ +--- +ssh::sftp_server_path: '/usr/lib64/ssh/sftp-server' diff --git a/data/SLES-11-x86_64.yaml b/data/SLES-11-x86_64.yaml new file mode 100644 index 00000000..cc0f1a61 --- /dev/null +++ b/data/SLES-11-x86_64.yaml @@ -0,0 +1,2 @@ +--- +ssh::sftp_server_path: '/usr/lib64/ssh/sftp-server' diff --git a/data/SLES.yaml b/data/SLES.yaml new file mode 100644 index 00000000..41400cef --- /dev/null +++ b/data/SLES.yaml @@ -0,0 +1,3 @@ +--- +ssh::server::service_name: 'sshd' +ssh::sftp_server_path: '/usr/lib/ssh/sftp-server' diff --git a/data/SmartOS.yaml b/data/SmartOS.yaml new file mode 100644 index 00000000..d9625d4c --- /dev/null +++ b/data/SmartOS.yaml @@ -0,0 +1,7 @@ +--- +ssh::server::sshd_dir: '/etc/ssh' +ssh::server::sshd_config: '/etc/ssh/sshd_config' +ssh::client::ssh_config: '/etc/ssh/ssh_config' +ssh::server::service_name: 'svc:/network/ssh:default' +ssh::sftp_server_path: 'internal-sftp' +ssh::server::host_priv_key_group: 0 diff --git a/data/Solaris-10.yaml b/data/Solaris-10.yaml new file mode 100644 index 00000000..29b5d713 --- /dev/null +++ b/data/Solaris-10.yaml @@ -0,0 +1,3 @@ +--- +ssh::server::server_package_name: 'SUNWsshdu' +ssh::client::client_package_name: 'SUNWsshu' diff --git a/data/Solaris.yaml b/data/Solaris.yaml new file mode 100644 index 00000000..bf1c4e79 --- /dev/null +++ b/data/Solaris.yaml @@ -0,0 +1,16 @@ +--- +ssh::server::server_package_name: '/service/network/ssh' +ssh::client::client_package_name: '/network/ssh' +ssh::server::sshd_binary: '/lib/svc/method/sshd' +ssh::server::service_name: 'svc:/network/ssh:default' +ssh::sftp_server_path: 'internal-sftp' + +ssh::server::default_options: + X11Forwarding: 'yes' + PrintMotd: 'no' + Subsystem: "sftp %{lookup('ssh::sftp_server_path')}" + HostKey: + - "%{lookup('ssh::server::sshd_dir')}/ssh_host_rsa_key" + - "%{lookup('ssh::server::sshd_dir')}/ssh_host_dsa_key" + +ssh::client::default_options: {} diff --git a/data/Suse.yaml b/data/Suse.yaml new file mode 100644 index 00000000..84b48b49 --- /dev/null +++ b/data/Suse.yaml @@ -0,0 +1,10 @@ +--- +ssh::server::server_package_name: 'openssh' +ssh::client::client_package_name: 'openssh' +ssh::server::sshd_dir: '/etc/ssh' +ssh::server::sshd_binary: '/usr/sbin/sshd' +ssh::server::sshd_config: '/etc/ssh/sshd_config' +ssh::server::sshd_environments_file: '/etc/sysconfig/ssh' +ssh::server::service_name: 'sshd' +ssh::client::ssh_config: '/etc/ssh/ssh_config' +ssh::server::host_priv_key_group: 0 diff --git a/data/common.yaml b/data/common.yaml new file mode 100644 index 00000000..cfc822e6 --- /dev/null +++ b/data/common.yaml @@ -0,0 +1,46 @@ +--- +lookup_options: + ssh::server_options: + merge: deep + ssh::server_match_block: + merge: deep + ssh::client_options: + merge: deep + ssh::users_client_options: + merge: deep + ssh::server::options: + merge: deep + ssh::client::options: + merge: deep + ssh::server_instances: + merge: deep + +ssh::server::sshd_dir: '/etc/ssh' +ssh::server::sshd_config: '/etc/ssh/sshd_config' +ssh::server::sshd_config_mode: '0600' +ssh::client::ssh_config: '/etc/ssh/ssh_config' +ssh::server::service_name: 'svc:/network/ssh:default' +ssh::sftp_server_path: 'internal-sftp' +ssh::client::config_user: 0 +ssh::client::config_group: 0 +ssh::server::config_user: 0 +ssh::server::config_group: 0 +ssh::server::host_priv_key_user: 0 +ssh::server::host_priv_key_group: 0 +ssh::server::host_priv_key_mode: '0600' +ssh::validate_sshd_file : false +ssh::collect_enabled : true # Collect sshkey resources +ssh::server::issue_net : '/etc/issue.net' +ssh::knownhosts::collect_enabled : true + +ssh::server::default_options: + X11Forwarding: 'yes' + PrintMotd: 'no' + AcceptEnv: 'LANG LC_*' + Subsystem: "sftp %{lookup('ssh::sftp_server_path')}" + UsePAM: 'yes' + +ssh::client::default_options: + 'Host *': + SendEnv: 'LANG LC_*' + HashKnownHosts: 'yes' diff --git a/files/sshd_config b/files/sshd_config deleted file mode 100644 index 05248741..00000000 --- a/files/sshd_config +++ /dev/null @@ -1,77 +0,0 @@ -# Package generated configuration file -# See the sshd(8) manpage for details - -# What ports, IPs and protocols we listen for -Port 22 -# Use these options to restrict which interfaces/protocols sshd will bind to -#ListenAddress :: -#ListenAddress 0.0.0.0 -Protocol 2 -# HostKeys for protocol version 2 -HostKey /etc/ssh/ssh_host_rsa_key -HostKey /etc/ssh/ssh_host_dsa_key -#Privilege Separation is turned on for security -UsePrivilegeSeparation yes - -# Lifetime and size of ephemeral version 1 server key -KeyRegenerationInterval 3600 -ServerKeyBits 768 - -# Logging -SyslogFacility AUTH -LogLevel INFO - -# Authentication: -LoginGraceTime 120 -PermitRootLogin yes -StrictModes yes - -RSAAuthentication yes -PubkeyAuthentication yes -#AuthorizedKeysFile %h/.ssh/authorized_keys - -# Don't read the user's ~/.rhosts and ~/.shosts files -IgnoreRhosts yes -# For this to work you will also need host keys in /etc/ssh_known_hosts -RhostsRSAAuthentication no -# similar for protocol version 2 -HostbasedAuthentication no -# Uncomment if you don't trust ~/.ssh/known_hosts for RhostsRSAAuthentication -#IgnoreUserKnownHosts yes - -# To enable empty passwords, change to yes (NOT RECOMMENDED) -PermitEmptyPasswords no - -# Change to yes to enable challenge-response passwords (beware issues with -# some PAM modules and threads) -ChallengeResponseAuthentication no - -# Change to no to disable tunnelled clear text passwords -#PasswordAuthentication yes - -# Kerberos options -#KerberosAuthentication no -#KerberosGetAFSToken no -#KerberosOrLocalPasswd yes -#KerberosTicketCleanup yes - -# GSSAPI options -#GSSAPIAuthentication no -#GSSAPICleanupCredentials yes - -X11Forwarding yes -X11DisplayOffset 10 -PrintMotd no -PrintLastLog yes -TCPKeepAlive yes -#UseLogin no - -#MaxStartups 10:30:60 -#Banner /etc/issue.net - -# Allow client to pass locale environment variables -AcceptEnv LANG LC_* - -Subsystem sftp /usr/lib/openssh/sftp-server - -UsePAM yes diff --git a/hiera.yaml b/hiera.yaml new file mode 100644 index 00000000..2a3dfb18 --- /dev/null +++ b/hiera.yaml @@ -0,0 +1,27 @@ +--- +version: 5 + +defaults: + datadir: 'data' + data_hash: 'yaml_data' + +hierarchy: + - name: 'Operating System Family' + path: '%{facts.os.family}.yaml' + + - name: 'Full Version' + path: '%{facts.os.name}-%{facts.os.release.full}.yaml' + + - name: 'Distribution Name' + path: '%{facts.os.name}.yaml' + + - name: 'Major Version' + paths: + - '%{facts.os.name}-%{facts.os.release.major}.yaml' + - '%{facts.os.family}-%{facts.os.release.major}.yaml' + + - name: 'Major Version with architecture' + path: '%{facts.os.name}-%{facts.os.release.major}-%{facts.os.architecture}.yaml' + + - name: 'common' + path: 'common.yaml' diff --git a/lib/facter/ssh_client_version.rb b/lib/facter/ssh_client_version.rb index 4c4cd6ea..70a3da52 100644 --- a/lib/facter/ssh_client_version.rb +++ b/lib/facter/ssh_client_version.rb @@ -1,34 +1,38 @@ +# frozen_string_literal: true + Facter.add('ssh_client_version_full') do - confine kernel: %w(Linux SunOS FreeBSD Darwin) + confine kernel: %w[Linux SunOS FreeBSD DragonFly Darwin] setcode do - version = Facter::Util::Resolution.exec('ssh -V 2>&1'). - lines. - to_a. - select { |line| line.match(%r{^OpenSSH_}) }. - first. - rstrip + if Facter::Util::Resolution.which('ssh') + version = Facter::Util::Resolution.exec('ssh -V 2>&1'). + lines. + to_a. + select { |line| line.match(%r{^OpenSSH_|^Sun_SSH_}) }. + first. + rstrip - version.gsub(%r{^OpenSSH_([^ ]+).*$}, '\1') unless version.nil? + version&.gsub(%r{^(OpenSSH_|Sun_SSH_)([^ ,]+).*$}, '\2') + end end end Facter.add('ssh_client_version_major') do - confine kernel: %w(Linux SunOS FreeBSD Darwin) + confine kernel: %w[Linux SunOS FreeBSD DragonFly Darwin] confine ssh_client_version_full: true setcode do version = Facter.value('ssh_client_version_full') - version.gsub(%r{^([0-9]+\.[0-9]+).*$}, '\1') + version&.gsub(%r{^([0-9]+\.[0-9]+).*$}, '\1') end end Facter.add('ssh_client_version_release') do - confine kernel: %w(Linux SunOS FreeBSD Darwin) + confine kernel: %w[Linux SunOS FreeBSD DragonFly Darwin] confine ssh_client_version_full: true setcode do version = Facter.value('ssh_client_version_full') - version.gsub(%r{^([0-9]+\.[0-9]+(?:\.[0-9]+)?).*$}, '\1') + version&.gsub(%r{^([0-9]+\.[0-9]+(?:\.[0-9]+)?).*$}, '\1') end end diff --git a/lib/facter/ssh_server_version.rb b/lib/facter/ssh_server_version.rb index 82005986..e18327c4 100644 --- a/lib/facter/ssh_server_version.rb +++ b/lib/facter/ssh_server_version.rb @@ -1,37 +1,40 @@ +# frozen_string_literal: true + Facter.add('ssh_server_version_full') do - confine kernel: %w(Linux SunOS FreeBSD Darwin) + confine kernel: %w[Linux SunOS FreeBSD DragonFly Darwin] setcode do - # sshd doesn't actually have a -V option (hopefully one will be added), - # by happy coincidence the usage information that is printed includes the - # version number. - version = Facter::Util::Resolution.exec('sshd -V 2>&1'). - lines. - to_a. - select { |line| line.match(%r{^OpenSSH_}) }. - first. - rstrip + if Facter::Util::Resolution.which('sshd') + # sshd doesn't actually have a -V option (hopefully one will be added), + # by happy coincidence the usage information that is printed includes the + # version number. + version = Facter::Util::Resolution.exec('sshd -V 2>&1'). + lines. + to_a. + select { |line| line.match(%r{^OpenSSH_|^Sun_SSH_}) }. + first. + rstrip - version.gsub(%r{^OpenSSH_([^ ]+).*$}, '\1') unless version.nil? + version&.gsub(%r{^(OpenSSH_|Sun_SSH_)([^ ,]+).*$}, '\2') + end end end Facter.add('ssh_server_version_major') do - confine kernel: %w(Linux SunOS FreeBSD Darwin) - confine ssh_server_version_full: true + confine kernel: %w[Linux SunOS FreeBSD DragonFly DragonFly Darwin] + confine ssh_server_version_full: %r{\d+} setcode do version = Facter.value('ssh_server_version_full') - version.gsub(%r{^([0-9]+\.[0-9]+).*$}, '\1') + version.gsub(%r{^([0-9]+)\..*$}, '\1') end end Facter.add('ssh_server_version_release') do - confine kernel: %w(Linux SunOS FreeBSD Darwin) - confine ssh_server_version_full: true + confine ssh_server_version_full: %r{\d+} setcode do version = Facter.value('ssh_server_version_full') - version.gsub(%r{^([0-9]+\.[0-9]+(?:\.[0-9]+)?).*$}, '\1') + version&.gsub(%r{^([0-9]+\.[0-9]+(?:\.[0-9]+)?).*$}, '\1') end end diff --git a/lib/puppet/functions/ssh/ipaddresses.rb b/lib/puppet/functions/ssh/ipaddresses.rb new file mode 100644 index 00000000..e3017be1 --- /dev/null +++ b/lib/puppet/functions/ssh/ipaddresses.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +# @summary Returns ip addresses of network interfaces (except lo) found by facter. +# @api private +# +# Returns all ip addresses of network interfaces (except lo) found by facter. +# Special network interfaces (e.g. docker0) can be excluded by an exclude list. +Puppet::Functions.create_function(:'ssh::ipaddresses') do + dispatch :ipaddresses do + # @param excluded_interfaces An array of interface names to be excluded. + param 'Array[String[1]]', :excluded_interfaces + # @param excluded_interfaces_re An array of regexp matching interface names to be excluded. + param 'Array', :excluded_interfaces_re + # @return The IP addresses found. + return_type 'Array[Stdlib::IP::Address]' + end + + def ipaddresses(excluded_interfaces, excluded_interfaces_re) + facts = closure_scope['facts'] + + # always exclude loopback interface + excluded_interfaces += ['lo'] + + if !facts['networking'].nil? && !facts['networking'].empty? + interfaces = facts['networking']['interfaces'] + else + interfaces = {} + facts['interfaces'].split(',').each do |iface| + next if facts["ipaddress_#{iface}"].nil? && facts["ipaddress6_#{iface}"].nil? + + interfaces[iface] = {} + interfaces[iface]['bindings'] = [{ 'address' => facts["ipaddress_#{iface}"] }] if !facts["ipaddress_#{iface}"].nil? && !facts["ipaddress_#{iface}"].empty? + interfaces[iface]['bindings6'] = [{ 'address' => facts["ipaddress6_#{iface}"] }] if !facts["ipaddress6_#{iface}"].nil? && !facts["ipaddress6_#{iface}"].empty? + end + end + + result = [] + interfaces.each do |iface, data| + # skip excluded interfaces + next if excluded_interfaces.include?(iface) + next if excluded_interfaces_re.any? { |pattern| Regexp.new(pattern).match?(iface) } + + %w[bindings bindings6].each do |binding_type| + next unless data.key?(binding_type) + + data[binding_type].each do |binding| + next unless binding.key?('address') + + result << binding['address'] + end + end + end + + # Throw away any v6 link-local addresses + fe8064 = IPAddr.new('fe80::/64') + result.delete_if { |ip| fe8064.include? IPAddr.new(ip) } + + result.uniq + end +end diff --git a/lib/puppet/parser/functions/ipaddresses.rb b/lib/puppet/parser/functions/ipaddresses.rb deleted file mode 100644 index 17f20ac9..00000000 --- a/lib/puppet/parser/functions/ipaddresses.rb +++ /dev/null @@ -1,34 +0,0 @@ -module Puppet::Parser::Functions - newfunction(:ipaddresses, type: :rvalue, doc: <<-EOS -Returns all ip addresses of network interfaces (except lo) found by facter. -EOS - ) do |_args| - interfaces = lookupvar('interfaces') - - # In Puppet v2.7, lookupvar returns :undefined if the variable does - # not exist. In Puppet 3.x, it returns nil. - # See http://docs.puppetlabs.com/guides/custom_functions.html - return false if interfaces.nil? || interfaces == :undefined - - result = [] - if interfaces.count(',') > 0 - interfaces = interfaces.split(',') - interfaces.each do |iface| - next if iface.include?('lo') - ipaddr = lookupvar("ipaddress_#{iface}") - ipaddr6 = lookupvar("ipaddress6_#{iface}") - result << ipaddr if ipaddr && (ipaddr != :undefined) - result << ipaddr6 if ipaddr6 && (ipaddr6 != :undefined) - end - else - unless interfaces.include?('lo') - ipaddr = lookupvar("ipaddress_#{interfaces}") - ipaddr6 = lookupvar("ipaddress6_#{interfaces}") - result << ipaddr if ipaddr && (ipaddr != :undefined) - result << ipaddr6 if ipaddr6 && (ipaddr6 != :undefined) - end - end - - return result - end -end diff --git a/lib/puppet/parser/functions/sshclient_options_to_augeas_ssh_config.rb b/lib/puppet/parser/functions/sshclient_options_to_augeas_ssh_config.rb new file mode 100644 index 00000000..7bf8d719 --- /dev/null +++ b/lib/puppet/parser/functions/sshclient_options_to_augeas_ssh_config.rb @@ -0,0 +1,99 @@ +# frozen_string_literal: true + +module Puppet::Parser::Functions + newfunction(:sshclient_options_to_augeas_ssh_config, type: :rvalue, doc: <<-DOC) do |args| + This function will convert a key-value hash to a format understandable by the augeas sshd_config provider + It will also optionally deal with keys that should be absent, and inject static parameters if supplied. + + Usage: sshclient_options_to_augeas_ssh_config($options_present, $options_absent, $other_parameters) + - $options_hash is mandatory and must be a hash. + - $options_absent is optional and can be either a single value or an array. + - $other_parameters is optional and must be a hash. + + Example: + $options = { + 'Host *.example.com' => { + 'ForwardAgent' => 'yes', + 'BatchMode' => 'yes', + }, + 'ForwardAgent' => 'no', + 'BatchMode' => 'no', + 'StrictHostKeyChecking' => 'no', + } + $options_absent = ['StrictHostKeyChecking','NoneField'] + $other_parameters = { 'target' => '/etc/ssh/ssh_config' } + + $options_final_augeas = sshclient_options_to_augeas_ssh_config($options, $options_absent, $other_parameters) + + In this case, the value of $options_final_augeas would be: + + 'ForwardAgent *.example.com' => { + 'ensure' => 'present', + 'host' => '*.example.com', + 'key' => 'ForwardAgent', + 'value' => 'yes', + 'target' => '/etc/ssh/ssh_config', + } + 'BatchMode *.example.com' => { + 'ensure' => 'present', + 'host' => '*.example.com', + 'key' => 'BatchMode', + 'value' => 'yes', + 'target' => '/etc/ssh/ssh_config', + } + 'ForwardAgent' => { + 'ensure' => 'present', + 'key' => 'ForwardAgent', + 'value' => 'yes', + 'target' => '/etc/ssh/ssh_config', + } + 'BatchMode' => { + 'ensure' => 'present', + 'key' => 'BatchMode', + 'value' => 'yes', + 'target' => '/etc/ssh/ssh_config', + } + 'StrictHostKeyChecking' => { + 'ensure' => 'absent', + 'key' => 'StrictHostKeyChecking', + 'target' => '/etc/ssh/ssh_config', + } + 'NoneField' => { + 'ensure' => 'absent', + 'key' => 'NoneField', + 'target' => '/etc/ssh/ssh_config', + } + + Note how the word "Host" is stripped away. + + DOC + + raise Puppet::ParseError, 'sshclient_options_to_augeas_ssh_config: expects at least one argument' if args.empty? + + options = args[0] + raise Puppet::ParseError, 'sshclient_options_to_augeas_ssh_config: first argument must be a hash' unless options.is_a?(Hash) + + options_absent = args[1] if args[1] + other_parameters = args[2] if args[2] + raise Puppet::ParseError, 'sshclient_options_to_augeas_ssh_config: second argument, if supplied, must be an array or a string' if options_absent && !(options_absent.is_a?(Array) || options_absent.is_a?(String)) + raise Puppet::ParseError, 'sshclient_options_to_augeas_ssh_config: third argument, if supplied, must be a hash' if other_parameters && !other_parameters.is_a?(Hash) + + options_final_augeas = {} + options.each do |key1, value1| + if value1.is_a?(Hash) + value1.each do |key2, value2| + v = { 'ensure' => 'present' }.merge('host' => key1.gsub('Host ', '')).merge('key' => key2, 'value' => value2) + options_final_augeas["#{key2} #{key1.gsub('Host ', '')}"] = v.merge(other_parameters) + end + else + options_final_augeas[key1] = { 'ensure' => 'present' }.merge('key' => key1, 'value' => value1).merge(other_parameters) + end + end + options_absent.each do |value| + options_final_augeas[value] = { 'ensure' => 'absent' }.merge('key' => value).merge(other_parameters) + end + return options_final_augeas + end + # newfunction +end +# module diff --git a/lib/puppet/parser/functions/sshserver_options_to_augeas_sshd_config.rb b/lib/puppet/parser/functions/sshserver_options_to_augeas_sshd_config.rb new file mode 100644 index 00000000..1da59bd8 --- /dev/null +++ b/lib/puppet/parser/functions/sshserver_options_to_augeas_sshd_config.rb @@ -0,0 +1,109 @@ +# frozen_string_literal: true + +module Puppet::Parser::Functions + newfunction(:sshserver_options_to_augeas_sshd_config, type: :rvalue, doc: <<-DOC) do |args| + This function will convert a key-value hash to a format understandable by the augeas sshd_config provider + It will also optionally deal with keys that should be absent, and inject static parameters if supplied. + + Usage: sshserver_options_to_augeas_sshd_config($options_present, $options_absent, $other_parameters) + - $options_hash is mandatory and must be a hash. + - $options_absent is optional and can be either a single value or an array. + - $other_parameters is optional and must be a hash. + + Example: + $options = { + 'Match User www-data' => { + 'PasswordAuthentication' => 'yes', + 'X11Forwarding' => 'no', + }, + 'Match Group bamboo' => { + 'ForcedCommand' => '/bin/echo hello world', + }, + 'X11Forwarding' => 'yes', + 'DebianBanner' => '/etc/banner.net', + 'AllowGroups' => ["sshgroups", "admins"], + } + $options_absent = ['DebianBanner','NoneField'] + $other_parameters = { 'target' => '/etc/ssh/sshd_config' } + + $options_final_augeas = sshserver_options_to_augeas_sshd_config($options, $options_absent, $other_parameters) + + In this case, the value of $options_final_augeas would be: + + 'PasswordAuthentication User www-data' => { + 'ensure' => 'present', + 'condition' => 'User www-data', + 'key' => 'PasswordAuthentication', + 'value' => 'yes', + 'target' => '/etc/ssh/sshd_config', + } + 'X11Forwarding User www-data' => { + 'ensure' => 'present', + 'condition' => 'User www-data', + 'key' => 'X11Forwarding', + 'value' => 'no', + 'target' => '/etc/ssh/sshd_config', + } + 'ForcedCommand Group bamboo' => { + 'ensure' => 'present', + 'condition' => 'Group bamboo', + 'key' => 'ForcedCommand', + 'value' => '/bin/echo hello world', + 'target' => '/etc/ssh/sshd_config', + } + 'X11Forwarding' => { + 'ensure' => 'present', + 'key' => 'X11Forwarding', + 'value' => 'yes', + 'target' => '/etc/ssh/sshd_config', + } + 'DebianBanner' => { + 'ensure' => 'absent', + 'key' => 'DebianBanner', + 'target' => '/etc/ssh/sshd_config', + } + 'AllowGroups' => { + 'ensure' => 'present', + 'key' => 'AllowGroups', + 'value' => ['sshgroups','admins'], + 'target' => '/etc/ssh/sshd_config', + } + 'NoneField' => { + 'ensure' => 'absent', + 'key' => 'NoneField', + 'target' => '/etc/ssh/sshd_config', + } + + Note how the word "Match" is stripped away. + + DOC + + raise Puppet::ParseError, 'sshserver_options_to_augeas_sshd_config: expects at least one argument' if args.empty? + + options = args[0] + raise Puppet::ParseError, 'sshserver_options_to_augeas_sshd_config: first argument must be a hash' unless options.is_a?(Hash) + + options_absent = args[1] if args[1] + other_parameters = args[2] if args[2] + raise Puppet::ParseError, 'sshserver_options_to_augeas_sshd_config: second argument, if supplied, must be an array or a string' if options_absent && !(options_absent.is_a?(Array) || options_absent.is_a?(String)) + raise Puppet::ParseError, 'sshserver_options_to_augeas_sshd_config: third argument, if supplied, must be a hash' if other_parameters && !other_parameters.is_a?(Hash) + + options_final_augeas = {} + options.each do |key1, value1| + if value1.is_a?(Hash) + value1.each do |key2, value2| + v = { 'ensure' => 'present' }.merge('condition' => key1.gsub('Match ', '')).merge('key' => key2, 'value' => value2) + options_final_augeas["#{key2} #{key1.gsub('Match ', '')}"] = v.merge(other_parameters) + end + else + options_final_augeas[key1] = { 'ensure' => 'present' }.merge('key' => key1, 'value' => value1).merge(other_parameters) + end + end + options_absent.each do |value| + options_final_augeas[value] = { 'ensure' => 'absent' }.merge('key' => value).merge(other_parameters) + end + return options_final_augeas + end + # newfunction +end +# module diff --git a/manifests/client.pp b/manifests/client.pp index 7fe487a0..73e4fc2c 100644 --- a/manifests/client.pp +++ b/manifests/client.pp @@ -1,40 +1,83 @@ -class ssh::client( - $ensure = present, - $storeconfigs_enabled = true, - $options = {} -) inherits ssh::params { - - # Merge hashes from multiple layer of hierarchy in hiera - $hiera_options = hiera_hash("${module_name}::client::options", undef) - - $fin_options = $hiera_options ? { - undef => $options, - '' => $options, - default => $hiera_options, +# @summary +# This class add ssh client management +# +# @example Puppet usage +# class { 'ssh::client': +# ensure => present, +# storeconfigs_enabled => true, +# use_augeas => false, +# } +# +# @param ssh_config +# Path to ssh client config file +# +# @param client_package_name +# Name of the client package +# +# @param ensure +# Ensurable param to ssh client +# +# @param storeconfigs_enabled +# Collected host keys from servers will be written to known_hosts unless storeconfigs_enabled is false +# +# @param options +# SSH client options, will be deep_merged with default_options. This parameter takes precedence over default_options +# +# @param use_augeas +# Use augeas to configure ssh client +# +# @param options_absent +# Remove options (with augeas style) +# +# @param default_options +# Default options to set, will be merged with options parameter +# +# @param match_block +# Add ssh match_block (with concat) +# +# @param config_user +# Numeric id or name of the user for the config file +# @param config_group +# Numeric id or name of the group for the config file +# +class ssh::client ( + Stdlib::Absolutepath $ssh_config, + Hash $default_options, + Variant[Integer, String[1]] $config_user, + Variant[Integer, String[1]] $config_group, + Optional[String[1]] $client_package_name = undef, + String $ensure = present, + Boolean $storeconfigs_enabled = true, + Hash $options = {}, + Boolean $use_augeas = false, + Array $options_absent = [], + Hash $match_block = {}, +) { + if $use_augeas { + $merged_options = sshclient_options_to_augeas_ssh_config($options, $options_absent, { 'target' => $ssh_config }) + } else { + $merged_options = deep_merge($options, delete($default_options, keys($options))) } - $merged_options = merge($ssh::params::ssh_default_options, $fin_options) - - include ::ssh::client::install - include ::ssh::client::config - - anchor { 'ssh::client::start': } - anchor { 'ssh::client::end': } + contain ssh::client::install + contain ssh::client::config # Provide option to *not* use storeconfigs/puppetdb, which means not managing # hostkeys and knownhosts if ($storeconfigs_enabled) { - include ::ssh::knownhosts + contain ssh::knownhosts - Anchor['ssh::client::start'] -> - Class['ssh::client::install'] -> - Class['ssh::client::config'] -> - Class['ssh::knownhosts'] -> - Anchor['ssh::client::end'] + Class['ssh::client::install'] + -> Class['ssh::client::config'] + -> Class['ssh::knownhosts'] } else { - Anchor['ssh::client::start'] -> - Class['ssh::client::install'] -> - Class['ssh::client::config'] -> - Anchor['ssh::client::end'] + Class['ssh::client::install'] + -> Class['ssh::client::config'] + } + + $match_block.each |String $k, Hash $v| { + ssh::client::match_block { $k: + * => $v, + } } } diff --git a/manifests/client/config.pp b/manifests/client/config.pp index 17ce5634..2547add7 100644 --- a/manifests/client/config.pp +++ b/manifests/client/config.pp @@ -1,24 +1,32 @@ -class ssh::client::config -{ - $options = $::ssh::client::merged_options +# @summary +# Manages ssh configuration +# +# @api private +# +class ssh::client::config { + assert_private() - file { $ssh::params::ssh_config: - ensure => present, - owner => '0', - group => '0', - mode => '0644', - content => template("${module_name}/ssh_config.erb"), - require => Class['ssh::client::install'], - } + $options = $ssh::client::merged_options + $use_augeas = $ssh::client::use_augeas + + if $use_augeas { + $options.each |String $k, Hash $v| { + ssh_config { $k: + * => $v, + } + } + } else { + concat { $ssh::client::ssh_config: + ensure => present, + owner => $ssh::client::config_user, + group => $ssh::client::config_group, + mode => '0644', + } - # Workaround for https://tickets.puppetlabs.com/browse/PUP-1177. - # Fixed in Puppet 3.7.0 - if versioncmp($::puppetversion, '3.7.0') < 0 { - ensure_resource('file', '/etc/ssh/ssh_known_hosts', { - 'ensure' => 'file', - 'owner' => 0, - 'group' => 0, - 'mode' => '0644', - }) + concat::fragment { 'ssh_config global config': + target => $ssh::client::ssh_config, + content => template("${module_name}/ssh_config.erb"), + order => '00', + } } } diff --git a/manifests/client/config/user.pp b/manifests/client/config/user.pp index 0c7b3836..753f0e40 100644 --- a/manifests/client/config/user.pp +++ b/manifests/client/config/user.pp @@ -1,36 +1,56 @@ # # Copyright (c) IN2P3 Computing Centre, IN2P3, CNRS # Contributor: Remi Ferrand (2015) +# Contributor: Tim Meusel (2017) # -define ssh::client::config::user( - $ensure = present, - $target = undef, - $user_home_dir = undef, - $manage_user_ssh_dir = true, - $options = {} -) -{ - validate_re($ensure, '^(present|absent)$') - validate_hash($options) - validate_bool($manage_user_ssh_dir) - - include ::ssh::params - - $_files_ensure = $ensure ? { 'present' => 'file', 'absent' => 'absent' } +# @summary +# This defined type manages a users ssh config +# +# @param ensure +# Specifies whether the config file should be present or absent +# +# @param target +# Sets the config file location, defaults to `~/.ssh/config` if $target and $user_home_dir are not set +# +# @param user_home_dir +# Sets the location of users home dir, defaults to `/home/$user` +# +# @param manage_user_ssh_dir +# Whether the users ssh dir should be managed or not +# +# @param options +# Options which should be set +# +# @param user +# The name of the user the config should be managed for +# +# @param ssh_directory_default_mode +# Default mode for the users ssh dir +# +# @param ssh_config_default_mode +# Default mode for the ssh config file +# +define ssh::client::config::user ( + Enum['present', 'absent'] $ensure = present, + Optional[Stdlib::Absolutepath] $target = undef, + Optional[Stdlib::Absolutepath] $user_home_dir = undef, + Boolean $manage_user_ssh_dir = true, + Hash $options = {}, + String[1] $user = $name, + String[1] $ssh_directory_default_mode = '0700', + String[1] $ssh_config_default_mode = '0600', +) { + contain ssh::client # If a specific target file was specified, # it must have higher priority than any # other parameter. if ($target != undef) { - validate_absolute_path($target) $_target = $target - } - else { + } else { if ($user_home_dir == undef) { - $_user_home_dir = "/home/${name}" - } - else { - validate_absolute_path($user_home_dir) + $_user_home_dir = "/home/${user}" + } else { $_user_home_dir = $user_home_dir } @@ -38,19 +58,26 @@ $_target = "${user_ssh_dir}/config" if ($manage_user_ssh_dir == true) { - file { $user_ssh_dir: - ensure => directory, - owner => $name, - mode => $::ssh::params::user_ssh_directory_default_mode, - before => File[$_target], + unless defined(File[$user_ssh_dir]) { + file { $user_ssh_dir: + ensure => directory, + owner => $user, + mode => $ssh_directory_default_mode, + before => Concat_file[$_target], + } } } } - file { $_target: - ensure => $_files_ensure, - owner => $name, - mode => $::ssh::params::user_ssh_config_default_mode, + unless defined(Concat_file[$_target]) { + concat_file { $_target: + ensure => $ensure, + owner => $user, + mode => $ssh_config_default_mode, + } + } + concat_fragment { $name: content => template("${module_name}/ssh_config.erb"), + target => $_target, } } diff --git a/manifests/client/install.pp b/manifests/client/install.pp index 86771d77..daa9a1b3 100644 --- a/manifests/client/install.pp +++ b/manifests/client/install.pp @@ -1,9 +1,16 @@ +# @summary +# Install ssh client package +# +# @api private +# class ssh::client::install { - if $ssh::params::client_package_name { - if !defined(Package[$ssh::params::client_package_name]) { - package { $ssh::params::client_package_name: - ensure => $ssh::client::ensure, - } - } + assert_private() + + if $ssh::client::client_package_name { + stdlib::ensure_packages([ + $ssh::client::client_package_name, + ], { + 'ensure' => $ssh::client::ensure, + }) } } diff --git a/manifests/client/match_block.pp b/manifests/client/match_block.pp new file mode 100644 index 00000000..3305ec21 --- /dev/null +++ b/manifests/client/match_block.pp @@ -0,0 +1,32 @@ +# @summary +# Add match_block to ssh client config (concat needed) +# +# @param options +# Options which should be set +# +# @param type +# Type of match_block, e.g. user, group, host, ... +# +# @param order +# Orders your settings within the config file +# +# @param target +# Sets the target file of the concat fragment +# +define ssh::client::match_block ( + Hash $options = {}, + Ssh::ClientMatch $type = 'user', + Integer $order = 50, + Stdlib::Absolutepath $target = $ssh::client::ssh_config, +) { + if $ssh::client::use_augeas { + fail('ssh::client::match_block() define not supported with use_augeas = true') + } else { + concat::fragment { "match_block ${name}": + target => $target, + # same template may be used for ssh_config & sshd_config + content => template("${module_name}/sshd_match_block.erb"), + order => 200+$order, + } + } +} diff --git a/manifests/hostkeys.pp b/manifests/hostkeys.pp index e65a27e0..19d05cec 100644 --- a/manifests/hostkeys.pp +++ b/manifests/hostkeys.pp @@ -1,62 +1,101 @@ -# Class ssh::hostkeys -class ssh::hostkeys( - $export_ipaddresses = true +# @summary +# This class manages hostkeys +# +# @param export_ipaddresses +# Whether ip addresses should be added as aliases +# +# @param storeconfigs_group +# Tag hostkeys with this group to allow segregation +# +# @param extra_aliases +# Additional aliases to set for host keys +# +# @param exclude_interfaces +# List of interfaces to exclude +# +# @param exclude_interfaces_re +# List of regular expressions to exclude interfaces +# +# @param exclude_ipaddresses +# List of ip addresses to exclude +# +# @param exclude_key_types +# List of key types to exclude from exported resources. +# +# @param use_trusted_facts +# Whether to use trusted or normal facts +# +# @param tags +# Array of custom tags +# +class ssh::hostkeys ( + Boolean $export_ipaddresses = true, + Optional[String[1]] $storeconfigs_group = undef, + Array $extra_aliases = [], + Array $exclude_interfaces = [], + Array $exclude_interfaces_re = [], + Array $exclude_ipaddresses = [], + Array[String[1]] $exclude_key_types = [], + Boolean $use_trusted_facts = false, + Optional[Array[String[1]]] $tags = undef, ) { - if $export_ipaddresses == true { - $ipaddresses = ipaddresses() - $host_aliases = flatten([ $::fqdn, $::hostname, $ipaddresses ]) + if $use_trusted_facts { + $fqdn_real = $trusted['certname'] + $hostname_real = $trusted['hostname'] } else { - $host_aliases = flatten([ $::fqdn, $::hostname ]) + # stick to legacy facts for older versions of facter + $fqdn_real = $facts['networking']['fqdn'] + $hostname_real = $facts['networking']['hostname'] } - if $::sshdsakey { - @@sshkey { "${::fqdn}_dsa": - ensure => present, - host_aliases => $host_aliases, - type => dsa, - key => $::sshdsakey, - } + if $export_ipaddresses == true { + $ipaddresses = ssh::ipaddresses($exclude_interfaces, $exclude_interfaces_re) + $ipaddresses_real = $ipaddresses - $exclude_ipaddresses + $host_aliases = sort(unique(flatten([$fqdn_real, $hostname_real, $extra_aliases, $ipaddresses_real]))) } else { - @@sshkey { "${::fqdn}_dsa": - ensure => absent, - } + $host_aliases = sort(unique(flatten([$fqdn_real, $hostname_real, $extra_aliases]))) } - if $::sshrsakey { - @@sshkey { "${::fqdn}_rsa": - ensure => present, - host_aliases => $host_aliases, - type => rsa, - key => $::sshrsakey, - } - } else { - @@sshkey { "${::fqdn}_rsa": - ensure => absent, - } + + $storeconfigs_groups = $storeconfigs_group ? { + undef => [], + default => ['hostkey_all', "hostkey_${storeconfigs_group}"], } - if $::sshecdsakey { - @@sshkey { "${::fqdn}_ecdsa": - ensure => present, - host_aliases => $host_aliases, - type => 'ecdsa-sha2-nistp256', - key => $::sshecdsakey, - } - } else { - @@sshkey { "${::fqdn}_ecdsa": - ensure => absent, - type => 'ecdsa-sha2-nistp256', - } + + $_tags = $tags ? { + undef => $storeconfigs_groups, + default => $storeconfigs_groups + $tags, } - if $::sshed25519key { - @@sshkey { "${::fqdn}_ed25519": - ensure => present, - host_aliases => $host_aliases, - type => 'ed25519', - key => $::sshed25519key, - } - } else { - @@sshkey { "${::fqdn}_ed25519": - ensure => absent, - type => 'ed25519', + + [ + 'dsa', + 'rsa', + 'ecdsa', + 'ed25519', + ].filter |String[1] $key_type| { + !($key_type in $exclude_key_types) + }.each |String[1] $key_type| { + if $key_type in $facts['ssh'] { + @@sshkey { "${fqdn_real}_${key_type}": + ensure => present, + host_aliases => $host_aliases, + type => $facts['ssh'][$key_type]['type'], + key => $facts['ssh'][$key_type]['key'], + tag => $_tags, + } + } else { + if $key_type == 'ecdsa' { + ['ecdsa-sha2-nistp256', 'ecdsa-sha2-nistp384', 'ecdsa-sha2-nistp521'].each |String[1] $kt| { + @@sshkey { "${fqdn_real}_${kt}": + ensure => absent, + type => $kt, + } + } + } else { + @@sshkey { "${fqdn_real}_${key_type}": + ensure => absent, + type => $key_type, + } + } } } } diff --git a/manifests/init.pp b/manifests/init.pp index c0a7489f..ad72a3cc 100644 --- a/manifests/init.pp +++ b/manifests/init.pp @@ -1,64 +1,220 @@ -# Main file for puppet-ssh +# @summary +# This class manages ssh client and server +# +# @example Puppet usage +# class { 'ssh': +# storeconfigs_enabled => false, +# server_options => { +# 'Match User www-data' => { +# 'ChrootDirectory' => '%h', +# 'ForceCommand' => 'internal-sftp', +# 'PasswordAuthentication' => 'yes', +# 'AllowTcpForwarding' => 'no', +# 'X11Forwarding' => 'no', +# }, +# 'Port' => [22, 2222, 2288], +# }, +# client_options => { +# 'Host *.amazonaws.com' => { +# 'User' => 'ec2-user', +# }, +# }, +# users_client_options => { +# 'bob' => { +# options => { +# 'Host *.alice.fr' => { +# 'User' => 'alice', +# }, +# }, +# }, +# }, +# 'server_instances' => { +# 'sftp_server_init' => { +# 'ensure' => 'present', +# 'options' => { +# 'sshd_config' => { +# 'Port' => 8022, +# 'Protocol' => 2, +# 'AddressFamily' => 'any', +# 'HostKey' => '/etc/ssh/ssh_host_rsa_key', +# 'SyslogFacility' => 'AUTH', +# 'LogLevel' => 'INFO', +# 'PermitRootLogin' => 'no', +# }, +# 'sshd_service_options' => '', +# 'match_blocks' => { +# '*,!ssh_exempt_ldap_authkey,!sshlokey' => { +# 'type' => 'group', +# 'options' => { +# 'AuthorizedKeysCommand' => '/usr/local/bin/getauthkey', +# 'AuthorizedKeysCommandUser' => 'nobody', +# 'AuthorizedKeysFile' => '/dev/null', +# }, +# }, +# }, +# }, +# }, +# }, +# } +# +# @example hiera usage +# ssh::storeconfigs_enabled: true +# +# ssh::server_options: +# Protocol: '2' +# ListenAddress: +# - '127.0.0.0' +# - '%{::hostname}' +# PasswordAuthentication: 'yes' +# SyslogFacility: 'AUTHPRIV' +# UsePAM: 'yes' +# X11Forwarding: 'yes' +# +# ssh::server::match_block: +# filetransfer: +# type: group +# options: +# ChrootDirectory: /home/sftp +# ForceCommand: internal-sftp +# +# ssh::client_options: +# 'Host *': +# SendEnv: 'LANG LC_*' +# ForwardX11Trusted: 'yes' +# ServerAliveInterval: '10' +# +# ssh::users_client_options: +# 'bob': +# 'options': +# 'Host *.alice.fr': +# 'User': 'alice' +# 'PasswordAuthentication': 'no' +# ssh::server::server_instances: +# sftp_server_init: +# ensure: present +# options: +# sshd_config: +# Port: 8022 +# Protocol: 2 +# AddressFamily: 'any' +# HostKey: '/etc/ssh/ssh_host_rsa_key' +# SyslogFacility: 'AUTH' +# LogLevel: INFO +# PermitRootLogin: 'no' +# sshd_service_options: '' +# match_blocks: +# '*,!ssh_exempt_ldap_authkey,!sshlokey': +# type: group +# options: +# AuthorizedKeysCommand: '/usr/local/bin/getauthkey' +# AuthorizedKeysCommandUser: 'nobody' +# AuthorizedKeysFile: '/dev/null' +# +# +# @param server_options +# Add dynamic options for ssh server config +# +# @param server_match_block +# Add match block for ssh server config +# +# @param client_options +# Add dynamic options for ssh client config +# +# @param client_match_block +# Add match block for ssh client config +# +# @param users_client_options +# Add users options for ssh client config +# +# @param version +# Define package version (package ressource) +# +# @param storeconfigs_enabled +# Default value for storeconfigs_enabled (client and server) +# +# @param validate_sshd_file +# Default value for validate_sshd_file (server) +# +# @param use_augeas +# Default value to use augeas (client and server) +# +# @param server_options_absent +# List of options to remove for server config (augeas only) +# +# @param client_options_absent +# List of options to remove for client config (augeas only) +# +# @param use_issue_net +# Use issue_net header +# +# @param purge_unmanaged_sshkeys +# Purge unmanaged sshkeys +# +# @param server_instances +# Configure SSH instances +# class ssh ( - $server_options = {}, - $server_match_block = {}, - $client_options = {}, - $users_client_options = {}, - $version = 'present', - $storeconfigs_enabled = true, - $validate_sshd_file = $::ssh::params::validate_sshd_file, -) inherits ssh::params { - - validate_hash($server_options) - validate_hash($server_match_block) - validate_hash($client_options) - validate_hash($users_client_options) - validate_bool($storeconfigs_enabled) - validate_bool($validate_sshd_file) - - # Merge hashes from multiple layer of hierarchy in hiera - $hiera_server_options = hiera_hash("${module_name}::server_options", undef) - $hiera_server_match_block = hiera_hash("${module_name}::server_match_block", undef) - $hiera_client_options = hiera_hash("${module_name}::client_options", undef) - $hiera_users_client_options = hiera_hash("${module_name}::users_client_options", undef) - - $fin_server_options = $hiera_server_options ? { - undef => $server_options, - '' => $server_options, - default => $hiera_server_options, + Optional[Hash] $server_options = undef, + Hash $server_match_block = {}, + Optional[Hash] $client_options = undef, + Hash $client_match_block = {}, + Hash $users_client_options = {}, + String $version = 'present', + Boolean $storeconfigs_enabled = true, + Boolean $validate_sshd_file = false, + Boolean $use_augeas = false, + Array $server_options_absent = [], + Array $client_options_absent = [], + Boolean $use_issue_net = false, + Boolean $purge_unmanaged_sshkeys = true, + Hash[String[1],Hash[String[1],NotUndef]] $server_instances = {}, +) { + class { 'ssh::server': + ensure => $version, + storeconfigs_enabled => $storeconfigs_enabled, + options => $server_options, + validate_sshd_file => $validate_sshd_file, + use_augeas => $use_augeas, + options_absent => $server_options_absent, + use_issue_net => $use_issue_net, } - $fin_server_match_block = $hiera_server_match_block ? { - undef => $server_match_block, - '' => $server_match_block, - default => $hiera_server_match_block, + class { 'ssh::client': + ensure => $version, + storeconfigs_enabled => $storeconfigs_enabled, + options => $client_options, + use_augeas => $use_augeas, + options_absent => $client_options_absent, } - $fin_client_options = $hiera_client_options ? { - undef => $client_options, - '' => $client_options, - default => $hiera_client_options, + $server_instances.each | String $instance_name, Hash $instance_settings | { + ssh::server::instances { $instance_name: + * => $instance_settings, + } } - $fin_users_client_options = $hiera_users_client_options ? { - undef => $users_client_options, - '' => $users_client_options, - default => $hiera_users_client_options, + # If host keys are being managed, optionally purge unmanaged ones as well. + if ($storeconfigs_enabled and $purge_unmanaged_sshkeys) { + resources { 'sshkey': + purge => true, + } } - class { '::ssh::server': - ensure => $version, - storeconfigs_enabled => $storeconfigs_enabled, - options => $fin_server_options, - validate_sshd_file => $validate_sshd_file, + $users_client_options.each |String $k, Hash $v| { + ssh::client::config::user { $k: + * => $v, + } } - class { '::ssh::client': - ensure => $version, - storeconfigs_enabled => $storeconfigs_enabled, - options => $fin_client_options, + $server_match_block.each |String $k, Hash $v| { + ssh::server::match_block { $k: + * => $v, + } } - create_resources('::ssh::client::config::user', $fin_users_client_options) - create_resources('::ssh::server::match_block', $fin_server_match_block) + $client_match_block.each |String $k, Hash $v| { + ssh::client::match_block { $k: + * => $v, + } + } } diff --git a/manifests/knownhosts.pp b/manifests/knownhosts.pp index bea3a5d0..3cd113e1 100644 --- a/manifests/knownhosts.pp +++ b/manifests/knownhosts.pp @@ -1,11 +1,21 @@ -class ssh::knownhosts( - $collect_enabled = $ssh::params::collect_enabled, -) inherits ssh::params { +# @summary +# This class manages knownhosts if collection is enabled. +# +# @param collect_enabled +# Enable collection +# +# @param storeconfigs_group +# Define the hostkeys group storage +# +class ssh::knownhosts ( + Boolean $collect_enabled = $ssh::knownhosts::collect_enabled, + Optional[String[1]] $storeconfigs_group = undef, +) { if ($collect_enabled) { - resources { 'sshkey': - purge => true, + if $storeconfigs_group { + Sshkey <<| tag == "hostkey_${storeconfigs_group}" |>> + } else { + Sshkey <<| |>> } - - Sshkey <<| |>> } } diff --git a/manifests/params.pp b/manifests/params.pp deleted file mode 100644 index 1a3386fc..00000000 --- a/manifests/params.pp +++ /dev/null @@ -1,205 +0,0 @@ -class ssh::params { - case $::osfamily { - 'Debian': { - $server_package_name = 'openssh-server' - $client_package_name = 'openssh-client' - $sshd_dir = '/etc/ssh' - $sshd_config = '/etc/ssh/sshd_config' - $ssh_config = '/etc/ssh/ssh_config' - $ssh_known_hosts = '/etc/ssh/ssh_known_hosts' - $service_name = 'ssh' - $sftp_server_path = '/usr/lib/openssh/sftp-server' - } - 'RedHat': { - $server_package_name = 'openssh-server' - $client_package_name = 'openssh-clients' - $sshd_dir = '/etc/ssh' - $sshd_config = '/etc/ssh/sshd_config' - $ssh_config = '/etc/ssh/ssh_config' - $ssh_known_hosts = '/etc/ssh/ssh_known_hosts' - $service_name = 'sshd' - $sftp_server_path = '/usr/libexec/openssh/sftp-server' - } - 'FreeBSD': { - $server_package_name = undef - $client_package_name = undef - $sshd_dir = '/etc/ssh' - $sshd_config = '/etc/ssh/sshd_config' - $ssh_config = '/etc/ssh/ssh_config' - $ssh_known_hosts = '/etc/ssh/ssh_known_hosts' - $service_name = 'sshd' - $sftp_server_path = '/usr/libexec/sftp-server' - } - 'OpenBSD': { - $server_package_name = undef - $client_package_name = undef - $sshd_dir = '/etc/ssh' - $sshd_config = '/etc/ssh/sshd_config' - $ssh_config = '/etc/ssh/ssh_config' - $ssh_known_hosts = '/etc/ssh/ssh_known_hosts' - $service_name = 'sshd' - $sftp_server_path = '/usr/libexec/sftp-server' - } - 'Darwin': { - $server_package_name = undef - $client_package_name = undef - $sshd_dir = '/etc/ssh' - $sshd_config = '/etc/ssh/sshd_config' - $ssh_config = '/etc/ssh/ssh_config' - $ssh_known_hosts = '/etc/ssh/ssh_known_hosts' - $service_name = 'com.openssh.sshd' - $sftp_server_path = '/usr/libexec/sftp-server' - } - 'ArchLinux': { - $server_package_name = 'openssh' - $client_package_name = 'openssh' - $sshd_dir = '/etc/ssh' - $sshd_config = '/etc/ssh/sshd_config' - $ssh_config = '/etc/ssh/ssh_config' - $ssh_known_hosts = '/etc/ssh/ssh_known_hosts' - $service_name = 'sshd.service' - $sftp_server_path = '/usr/lib/ssh/sftp-server' - } - 'Suse': { - $server_package_name = 'openssh' - $client_package_name = 'openssh' - $sshd_dir = '/etc/ssh' - $sshd_config = '/etc/ssh/sshd_config' - $ssh_config = '/etc/ssh/ssh_config' - $ssh_known_hosts = '/etc/ssh/ssh_known_hosts' - case $::operatingsystem { - 'SLES': { - $service_name = 'sshd' - $sftp_server_path = '/usr/lib64/ssh/sftp-server' - } - 'OpenSuse': { - $service_name = 'sshd' - $sftp_server_path = '/usr/lib/ssh/sftp-server' - } - default: { - fail("Unsupported platform: ${::osfamily}/${::operatingsystem}") - } - } - } - 'Solaris': { - case $::operatingsystem { - 'SmartOS': { - $server_package_name = undef - $client_package_name = undef - $sshd_dir = '/etc/ssh' - $sshd_config = '/etc/ssh/sshd_config' - $ssh_config = '/etc/ssh/ssh_config' - $ssh_known_hosts = '/etc/ssh/ssh_known_hosts' - $service_name = 'svc:/network/ssh:default' - $sftp_server_path = 'internal-sftp' - } - default: { - $sshd_dir = '/etc/ssh' - $sshd_config = '/etc/ssh/sshd_config' - $ssh_config = '/etc/ssh/ssh_config' - $ssh_known_hosts = '/etc/ssh/ssh_known_hosts' - $service_name = 'svc:/network/ssh:default' - $sftp_server_path = 'internal-sftp' - case versioncmp($::kernelrelease, '5.10') { - 1: { - # Solaris 11 and later - $server_package_name = '/service/network/ssh' - $client_package_name = '/network/ssh' - } - 0: { - # Solaris 10 - $server_package_name = 'SUNWsshdu' - $client_package_name = 'SUNWsshu' - } - default: { - # Solaris 9 and earlier not supported - fail("Unsupported platform: ${::osfamily}/${::kernelrelease}") - } - } - } - } - } - default: { - case $::operatingsystem { - 'Gentoo': { - $server_package_name = 'openssh' - $client_package_name = 'openssh' - $sshd_dir = '/etc/ssh' - $sshd_config = '/etc/ssh/sshd_config' - $ssh_config = '/etc/ssh/ssh_config' - $ssh_known_hosts = '/etc/ssh/ssh_known_hosts' - $service_name = 'sshd' - $sftp_server_path = '/usr/lib/misc/sftp-server' - } - 'Amazon': { - $server_package_name = 'openssh-server' - $client_package_name = 'openssh-clients' - $sshd_dir = '/etc/ssh' - $sshd_config = '/etc/ssh/sshd_config' - $ssh_config = '/etc/ssh/ssh_config' - $ssh_known_hosts = '/etc/ssh/ssh_known_hosts' - $service_name = 'sshd' - $sftp_server_path = '/usr/libexec/openssh/sftp-server' - } - default: { - fail("Unsupported platform: ${::osfamily}/${::operatingsystem}") - } - } - } - } - - # ssh & sshd default options: - # - OpenBSD doesn't know about UsePAM - # - Sun_SSH doesn't know about UsePAM & AcceptEnv; SendEnv & HashKnownHosts - case $::osfamily { - 'OpenBSD': { - $sshd_default_options = { - 'ChallengeResponseAuthentication' => 'no', - 'X11Forwarding' => 'yes', - 'PrintMotd' => 'no', - 'AcceptEnv' => 'LANG LC_*', - 'Subsystem' => "sftp ${sftp_server_path}", - } - $ssh_default_options = { - 'Host *' => { - 'SendEnv' => 'LANG LC_*', - 'HashKnownHosts' => 'yes', - }, - } - } - 'Solaris': { - $sshd_default_options = { - 'ChallengeResponseAuthentication' => 'no', - 'X11Forwarding' => 'yes', - 'PrintMotd' => 'no', - 'Subsystem' => "sftp ${sftp_server_path}", - 'HostKey' => [ - "${sshd_dir}/ssh_host_rsa_key", - "${sshd_dir}/ssh_host_dsa_key", - ], - } - $ssh_default_options = { } - } - default: { - $sshd_default_options = { - 'ChallengeResponseAuthentication' => 'no', - 'X11Forwarding' => 'yes', - 'PrintMotd' => 'no', - 'AcceptEnv' => 'LANG LC_*', - 'Subsystem' => "sftp ${sftp_server_path}", - 'UsePAM' => 'yes', - } - $ssh_default_options = { - 'Host *' => { - 'SendEnv' => 'LANG LC_*', - 'HashKnownHosts' => 'yes', - }, - } - } - } - - $validate_sshd_file = false - $user_ssh_directory_default_mode = '0700' - $user_ssh_config_default_mode = '0600' - $collect_enabled = true # Collect sshkey resources -} diff --git a/manifests/server.pp b/manifests/server.pp index 97c807ac..3e040a80 100644 --- a/manifests/server.pp +++ b/manifests/server.pp @@ -1,46 +1,150 @@ -class ssh::server( - $ensure = present, - $storeconfigs_enabled = true, - $options = {}, - $validate_sshd_file = false, -) inherits ssh::params { - - # Merge hashes from multiple layer of hierarchy in hiera - $hiera_options = hiera_hash("${module_name}::server::options", undef) - - $fin_options = $hiera_options ? { - undef => $options, - '' => $options, - default => $hiera_options, +# @summary +# This class managed ssh server +# +# @example Puppet usage +# class { 'ssh::server': +# ensure => present, +# storeconfigs_enabled => true, +# use_issue_net => false, +# } +# +# @param service_name +# Name of the sshd service +# +# @param sshd_config +# Path to the sshd_config file +# +# @param sshd_dir +# Path to the sshd dir (e.g. /etc/ssh) +# +# @param sshd_binary +# Path to the sshd binary +# +# @param sshd_config_mode +# Mode to set on the sshd config file +# +# @param host_priv_key_user +# Numeric id or name of the user for the private host key +# +# @param host_priv_key_group +# Numeric id or name of the group for the private host key +# +# @param host_priv_key_mode +# Mode of the private host key +# +# @param config_user +# Numeric id or name of the user for the sshd config file +# +# @param config_group +# Numeric id or name of the group for the sshd config file +# +# @param default_options +# Default options to set, will be merged with options parameter +# +# @param ensure +# Ensurable param to ssh server +# +# @param include_dir +# Path to sshd include directory. +# +# @param include_dir_mode +# Mode to set on the sshd include directory. +# +# @param include_dir_purge +# Purge the include directory if true. +# +# @param config_files +# Hash of config files to add to the ssh include directory. +# +# @param storeconfigs_enabled +# Host keys will be collected and distributed unless storeconfigs_enabled is false. +# +# @param options +# Dynamic hash for openssh server option +# +# @param validate_sshd_file +# Add sshd file validate cmd +# +# @param use_augeas +# Use augeas for configuration (default concat) +# +# @param options_absent +# Remove options (with augeas style) +# +# @param match_block +# Add sshd match_block (with concat) +# +# @param use_issue_net +# Add issue_net banner +# +# @param sshd_environments_file +# Path to a sshd environments file (e.g. /etc/defaults/ssh on Debian) +# +# @param server_package_name +# Name of the server package to install +# +class ssh::server ( + String[1] $service_name, + Stdlib::Absolutepath $sshd_config, + Stdlib::Absolutepath $sshd_dir, + Stdlib::Absolutepath $sshd_binary, + Stdlib::Filemode $sshd_config_mode, + Variant[Integer, String[1]] $host_priv_key_user, + Variant[Integer, String[1]] $host_priv_key_group, + Stdlib::Filemode $host_priv_key_mode, + Variant[Integer, String[1]] $config_user, + Variant[Integer, String[1]] $config_group, + Hash $default_options, + String $ensure = present, + Optional[Stdlib::Absolutepath] $include_dir = undef, + Stdlib::Filemode $include_dir_mode = '0700', + Boolean $include_dir_purge = true, + Hash[String, Hash] $config_files = {}, + Boolean $storeconfigs_enabled = true, + Hash $options = {}, + Boolean $validate_sshd_file = false, + Boolean $use_augeas = false, + Array $options_absent = [], + Hash $match_block = {}, + Boolean $use_issue_net = false, + Optional[Stdlib::Absolutepath] $sshd_environments_file = undef, + Optional[String[1]] $server_package_name = undef, +) { + if $use_augeas { + $merged_options = sshserver_options_to_augeas_sshd_config($options, $options_absent, { 'target' => $ssh::server::sshd_config }) + } else { + if $facts['ssh_server_version_release'] and versioncmp($facts['ssh_server_version_release'], '8.6') >= 0 { + $default_options_real = $default_options + { 'KbdInteractiveAuthentication' => 'no' } + } else { + $default_options_real = $default_options + { 'ChallengeResponseAuthentication' => 'no' } + } + $merged_options = deep_merge($default_options_real, $options) } - $merged_options = merge($ssh::params::sshd_default_options, $fin_options) - - include ::ssh::server::install - include ::ssh::server::config - include ::ssh::server::service - - anchor { 'ssh::server::start': } - anchor { 'ssh::server::end': } + contain ssh::server::install + contain ssh::server::config + contain ssh::server::service # Provide option to *not* use storeconfigs/puppetdb, which means not managing # hostkeys and knownhosts if ($storeconfigs_enabled) { - include ::ssh::hostkeys - include ::ssh::knownhosts + contain ssh::hostkeys + contain ssh::knownhosts - Anchor['ssh::server::start'] -> - Class['ssh::server::install'] -> - Class['ssh::server::config'] ~> - Class['ssh::server::service'] -> - Class['ssh::hostkeys'] -> - Class['ssh::knownhosts'] -> - Anchor['ssh::server::end'] + Class['ssh::server::install'] + -> Class['ssh::server::config'] + ~> Class['ssh::server::service'] + -> Class['ssh::hostkeys'] + -> Class['ssh::knownhosts'] } else { - Anchor['ssh::server::start'] -> - Class['ssh::server::install'] -> - Class['ssh::server::config'] ~> - Class['ssh::server::service'] -> - Anchor['ssh::server::end'] + Class['ssh::server::install'] + -> Class['ssh::server::config'] + ~> Class['ssh::server::service'] + } + + $match_block.each |String $k, Hash $v| { + ssh::server::match_block { $k: + * => $v, + } } } diff --git a/manifests/server/config.pp b/manifests/server/config.pp index 74d9256d..ee6c0e9c 100644 --- a/manifests/server/config.pp +++ b/manifests/server/config.pp @@ -1,26 +1,84 @@ +# @summary +# Managed ssh server configuration +# +# @api private +# class ssh::server::config { + assert_private() + + $options = $ssh::server::merged_options + $include_dir = $ssh::server::include_dir case $ssh::server::validate_sshd_file { true: { - $sshd_validate_cmd = '/usr/sbin/sshd -tf %' + $sshd_validate_cmd = "${ssh::server::sshd_binary} -tf %" } default: { $sshd_validate_cmd = undef } } - concat { $ssh::params::sshd_config: - ensure => present, - owner => '0', - group => '0', - mode => '0600', - validate_cmd => $sshd_validate_cmd, - notify => Service[$ssh::params::service_name], + if $ssh::server::use_augeas { + $options.each |String $k, Hash $v| { + if $k.downcase == 'subsystem' { + $_v = $v.match(/(^(\w+)\s+(.*)$)/) + sshd_config_subsystem { $v[2]: + command => $v[3], + } + } else { + sshd_config { $k: + * => $v, + } + } + } + } else { + concat { $ssh::server::sshd_config: + ensure => present, + owner => $ssh::server::config_user, + group => $ssh::server::config_group, + mode => $ssh::server::sshd_config_mode, + validate_cmd => $sshd_validate_cmd, + notify => Service[$ssh::server::service_name], + } + + concat::fragment { 'global config': + target => $ssh::server::sshd_config, + content => template("${module_name}/sshd_config.erb"), + order => '00', + } } - concat::fragment { 'global config': - target => $ssh::params::sshd_config, - content => template("${module_name}/sshd_config.erb"), - order => '00', + if $ssh::server::include_dir { + file { $ssh::server::include_dir: + ensure => directory, + owner => $ssh::server::config_user, + group => $ssh::server::config_group, + mode => $ssh::server::include_dir_mode, + purge => $ssh::server::include_dir_purge, + recurse => $ssh::server::include_dir_purge, + } + + $ssh::server::config_files.each |$file, $params| { + ssh::server::config_file { $file: + * => $params, + } + } + } + + if $ssh::server::use_issue_net { + file { $ssh::server::issue_net: + ensure => file, + owner => $ssh::server::config_user, + group => $ssh::server::config_group, + mode => $ssh::server::sshd_config_mode, + content => template("${module_name}/issue.net.erb"), + notify => Service[$ssh::server::service_name], + } + + concat::fragment { 'banner file': + target => $ssh::server::sshd_config, + content => "Banner ${ssh::server::issue_net}\n", + order => '01', + } } } diff --git a/manifests/server/config/setting.pp b/manifests/server/config/setting.pp index 9232bb60..de9fe9f2 100644 --- a/manifests/server/config/setting.pp +++ b/manifests/server/config/setting.pp @@ -1,28 +1,36 @@ +# @summary +# Internal define to managed ssh server param +# +# @param key +# Key of the value which should be set +# +# @param value +# Value which should be set +# +# @param order +# Orders your setting within the config file +# define ssh::server::config::setting ( - $key, - $value, - $order = '10' + String[1] $key, + Variant[Boolean, Array, Hash, String] $value, + Variant[String[1], Integer] $order = '10' ) { - include ::ssh::params + contain ssh::server - if is_bool($value) { - $real_value = $value ? { + $real_value = $value ? { + Boolean => $value ? { true => 'yes', false => 'no', default => undef - } - } elsif is_array($value) { - $real_value = join($value, ' ') - } elsif is_hash($value) { - fail('Hash values are not supported') - } else { - $real_value = $value + }, + Array => join($value, ' '), + Hash => fail('Hash values are not supported'), + default => $value, } concat::fragment { "ssh_setting_${name}_${key}": - target => $ssh::params::sshd_config, + target => $ssh::server::sshd_config, content => "\n# added by Ssh::Server::Config::Setting[${name}]\n${key} ${real_value}\n", order => $order, } - } diff --git a/manifests/server/config_file.pp b/manifests/server/config_file.pp new file mode 100644 index 00000000..69970c24 --- /dev/null +++ b/manifests/server/config_file.pp @@ -0,0 +1,46 @@ +# @summary Resource type for managing a config file in the include dir. +# +# @param mode +# File mode for the config file. +# +# @param include +# Absolute path to config file to include at the top of the config file. This +# is intended for including files not managed by this module (crypto policies). +# +# @param options +# Dynamic hash for openssh server option +# +define ssh::server::config_file ( + Stdlib::Absolutepath $path = "${ssh::server::include_dir}/${name}.conf", + Stdlib::Filemode $mode = $ssh::server::sshd_config_mode, + Optional[Stdlib::Absolutepath] $include = undef, + Hash $options = {}, +) { + if !$ssh::server::include_dir { + fail('ssh::server::config_file() define not supported if ssh::server::include_dir not set') + } + + case $ssh::server::validate_sshd_file { + true: { + $sshd_validate_cmd = "${ssh::server::sshd_binary} -tf %" + } + default: { + $sshd_validate_cmd = undef + } + } + + concat { $path: + ensure => present, + owner => $ssh::server::config_user, + group => $ssh::server::config_group, + mode => $mode, + validate_cmd => $sshd_validate_cmd, + notify => Service[$ssh::server::service_name], + } + + concat::fragment { "sshd_config_file ${title}": + target => $path, + content => template("${module_name}/sshd_config.erb"), + order => '00', + } +} diff --git a/manifests/server/host_key.pp b/manifests/server/host_key.pp index a8960684..309bc6ed 100644 --- a/manifests/server/host_key.pp +++ b/manifests/server/host_key.pp @@ -1,84 +1,154 @@ -# == Define: ssh::server::host_key +# @summary +# Manage a ssh host key # -# This module install a ssh host key in the server (basically, it is -# a file resource but it also notifies to the ssh service) +# This module install a ssh host key in the server (basically, it is +# a file resource but it also notifies to the ssh service) # -# Important! This define does not modify any option in sshd_config, so -# you have to manually define the HostKey option in the server options -# if you haven't done yet. +# Important! This define does not modify any option in sshd_config, so +# you have to manually define the HostKey option in the server options +# if you haven't done yet. # -# == Parameters -# -# [*ensure*] +# @param ensure # Set to 'absent' to remove host_key files # -# [*public_key_source*] +# @param public_key_source # Sets the content of the source parameter for the public key file # Note public_key_source and public_key_content are mutually exclusive. # -# [*public_key_content*] +# @param public_key_content # Sets the content for the public key file. # Note public_key_source and public_key_content are mutually exclusive. # -# [*private_key_source*] +# @param private_key_source # Sets the content of the source parameter for the private key file # Note private_key_source and private_key_content are mutually exclusive. # -# [*private_key_content*] +# @param private_key_content # Sets the content for the private key file. # Note private_key_source and private_key_content are mutually exclusive. # +# @param certificate_source +# Sets the content of the source parameter for the host key certificate. +# Note certificate_source and certificate_content are mutually exclusive. +# +# @param certificate_content +# Sets the content for the host key certificate. +# Note certificate_source and certificate_content are mutually exclusive. +# define ssh::server::host_key ( - $ensure = 'present', - $public_key_source = '', - $public_key_content = '', - $private_key_source = '', - $private_key_content = '', + Enum[present, absent] $ensure = 'present', + Optional[String[1]] $public_key_source = undef, + Optional[String[1]] $public_key_content = undef, + Optional[String[1]] $private_key_source = undef, + Optional[String[1]] $private_key_content = undef, + Optional[String[1]] $certificate_source = undef, + Optional[String[1]] $certificate_content = undef, ) { - if $public_key_source == '' and $public_key_content == '' { - fail('You must provide either public_key_source or public_key_content parameter') - } - if $private_key_source == '' and $private_key_content == '' { - fail('You must provide either private_key_source or private_key_content parameter') + # Ensure the ssh::server class is included in the manifest + contain ssh::server + + if $ensure == 'present' { + if ! $public_key_source and ! $public_key_content { + fail('You must provide either public_key_source or public_key_content parameter') + } + + if ! $private_key_source and ! $private_key_content { + fail('You must provide either private_key_source or private_key_content parameter') + } } $manage_pub_key_content = $public_key_source ? { - '' => $public_key_content, + undef => $public_key_content, default => undef, } $manage_pub_key_source = $public_key_source ? { - '' => undef, + undef => undef, default => $public_key_source, } $manage_priv_key_content = $private_key_source ? { - '' => $private_key_content, + undef => $private_key_content, default => undef, } $manage_priv_key_source = $private_key_source ? { - '' => undef, + undef => undef, default => $private_key_source, } - file {"${name}_pub": - ensure => $ensure, - owner => 'root', - group => 'root', - mode => '0644', - path => "${::ssh::params::sshd_dir}/${name}.pub", - source => $manage_pub_key_source, - content => $manage_pub_key_content, - notify => Class['ssh::server::service'], + $manage_cert_content = $certificate_source ? { + undef => $certificate_content, + default => undef, + } + $manage_cert_source = $certificate_source ? { + undef => undef, + default => $certificate_source, + } + + if $ensure == 'present' { + file { "${name}_pub": + ensure => $ensure, + owner => $ssh::server::config_user, + group => $ssh::server::config_group, + mode => '0644', + path => "${ssh::server::sshd_dir}/${name}.pub", + source => $manage_pub_key_source, + content => $manage_pub_key_content, + notify => Class['ssh::server::service'], + } + + file { "${name}_priv": + ensure => $ensure, + owner => $ssh::server::host_priv_key_user, + group => $ssh::server::host_priv_key_group, + mode => $ssh::server::host_priv_key_mode, + path => "${ssh::server::sshd_dir}/${name}", + source => $manage_priv_key_source, + content => $manage_priv_key_content, + show_diff => false, + notify => Class['ssh::server::service'], + } + } else { + file { "${name}_pub": + ensure => $ensure, + owner => $ssh::server::config_user, + group => $ssh::server::config_group, + mode => '0644', + path => "${ssh::server::sshd_dir}/${name}.pub", + notify => Class['ssh::server::service'], + } + + file { "${name}_priv": + ensure => $ensure, + owner => $ssh::server::host_priv_key_user, + group => $ssh::server::host_priv_key_group, + mode => $ssh::server::host_priv_key_mode, + path => "${ssh::server::sshd_dir}/${name}", + show_diff => false, + notify => Class['ssh::server::service'], + } } - file {"${name}_priv": - ensure => $ensure, - owner => 'root', - group => 'root', - mode => '0600', - path => "${::ssh::params::sshd_dir}/${name}", - source => $manage_priv_key_source, - content => $manage_priv_key_content, - notify => Class['ssh::server::service'], + if !empty($certificate_source) or !empty($certificate_content) { + if $ensure == 'present' { + file { "${name}_cert": + ensure => $ensure, + owner => $ssh::server::config_user, + group => $ssh::server::config_group, + mode => '0644', + path => "${ssh::server::sshd_dir}/${name}-cert.pub", + source => $manage_cert_source, + content => $manage_cert_content, + notify => Class['ssh::server::service'], + } + } else { + file { "${name}_cert": + ensure => $ensure, + owner => $ssh::server::config_user, + group => $ssh::server::config_group, + mode => '0644', + path => "${ssh::server::sshd_dir}/${name}-cert.pub", + notify => Class['ssh::server::service'], + } + } } } diff --git a/manifests/server/install.pp b/manifests/server/install.pp index 6841bf51..c8f8df8c 100644 --- a/manifests/server/install.pp +++ b/manifests/server/install.pp @@ -1,10 +1,16 @@ +# @summary +# Install ssh server package +# +# @api private +# class ssh::server::install { - include ::ssh::params - if $ssh::params::server_package_name { - if !defined(Package[$ssh::params::server_package_name]) { - package { $ssh::params::server_package_name: - ensure => $ssh::server::ensure, - } - } + assert_private() + + if $ssh::server::server_package_name { + stdlib::ensure_packages ([ + $ssh::server::server_package_name, + ], { + 'ensure' => $ssh::server::ensure, + }) } } diff --git a/manifests/server/instances.pp b/manifests/server/instances.pp new file mode 100644 index 00000000..51dced39 --- /dev/null +++ b/manifests/server/instances.pp @@ -0,0 +1,86 @@ +# @summary +# Configure separate ssh server instances +# +# @param ensure +# Specifies whether the instance should be added or removed +# +# @param options +# Set options for the instance +# +# @param service_ensure +# Whether this instance service should be running or stopped, defaults to true when ensure is set to present, otherwise false +# +# @param service_enable +# Whether this instance service should be started at boot. Will be added automatically if ensure is running/removed if ensure is stopped +# +# @param validate_config_file +# Validate config file before applying +# +# @param sshd_instance_config_file +# Path of the instance sshd config +# +# @param sshd_binary +# Path to sshd binary +# +# @param sshd_environments_file +# Path to environments file, if any +# +define ssh::server::instances ( + Enum[present, absent] $ensure = present, + Hash $options = {}, + Stdlib::Ensure::Service $service_ensure = $ensure ? { 'present' => 'running', 'absent' => 'stopped' }, + Boolean $service_enable = ($service_ensure == 'running'), + Boolean $validate_config_file = false, + Stdlib::Absolutepath $sshd_instance_config_file = "${ssh::server::sshd_dir}/sshd_config.${title}", + Stdlib::Absolutepath $sshd_binary = $ssh::server::sshd_binary, + Optional[Stdlib::Absolutepath] $sshd_environments_file = $ssh::server::sshd_environments_file, +) { + contain ssh::server + + $sshd_instance_config = assert_type(Hash, pick($options['sshd_config'], {})) + $sshd_instance_matchblocks = assert_type(Hash, pick($options['match_blocks'], {})) + $sshd_service_options = $options['sshd_service_options'] + $sshd_additional_service_options = $options['sshd_additional_service_options'] + + #check if server is a linux + if $facts['kernel'] == 'Linux' { + case $validate_config_file { + true: { + $validate_cmd = "${ssh::server::sshd_binary} -tf %" + } + default: { + $validate_cmd = undef + } + } + + concat { $sshd_instance_config_file: + ensure => $ensure, + owner => $ssh::server::config_user, + group => $ssh::server::config_group, + mode => '0600', + validate_cmd => $validate_cmd, + notify => Service["${title}.service"], + } + + concat::fragment { "sshd instance ${title} config": + target => $sshd_instance_config_file, + content => template("${module_name}/ssh_instance.erb"), + order => '00', + } + + $sshd_instance_matchblocks.each |String $matchblock_name, Hash $matchblock_options| { + ssh::server::match_block { $matchblock_name: + * => $matchblock_options, + target => $sshd_instance_config_file, + } + } + + systemd::unit_file { "${title}.service": + content => template("${module_name}/ssh_instance_service.erb"), + active => ($service_ensure == 'running'), + enable => $service_enable, + } + } else { + fail ("Operating System ${facts['os']['name']} not supported, because Systemd is not available") + } +} diff --git a/manifests/server/match_block.pp b/manifests/server/match_block.pp index c7372c0e..2a80b4ff 100644 --- a/manifests/server/match_block.pp +++ b/manifests/server/match_block.pp @@ -1,7 +1,31 @@ -define ssh::server::match_block ($options, $type = 'user', $order = 50,) { - concat::fragment { "match_block ${name}": - target => $ssh::params::sshd_config, - content => template("${module_name}/sshd_match_block.erb"), - order => 200+$order, +# @summary +# Add match_block to ssh server config +# +# @param options +# Options which should be set +# +# @param type +# Type of match_block, e.g. user, group, host, ... +# +# @param order +# Orders your settings within the config file +# +# @param target +# Sets the target file of the concat fragment +# +define ssh::server::match_block ( + Hash $options = {}, + String[1] $type = 'user', + Integer $order = 50, + Stdlib::Absolutepath $target = $ssh::server::sshd_config, +) { + if $ssh::server::use_augeas { + fail('ssh::server::match_block() define not supported with use_augeas = true') + } else { + concat::fragment { "match_block ${name}": + target => $target, + content => template("${module_name}/sshd_match_block.erb"), + order => 200+$order, + } } } diff --git a/manifests/server/options.pp b/manifests/server/options.pp index 6246bd90..ce687c08 100644 --- a/manifests/server/options.pp +++ b/manifests/server/options.pp @@ -1,6 +1,18 @@ -define ssh::server::options ($options, $order = 50) { +# @summary +# This defined type manages ssh server options +# +# @param options +# Options which should be set +# +# @param order +# Orders your settings within the config file +# +define ssh::server::options ( + Hash $options = {}, + Integer $order = 50 +) { concat::fragment { "options ${name}": - target => $ssh::params::sshd_config, + target => $ssh::server::sshd_config, content => template("${module_name}/options.erb"), order => 100+$order, } diff --git a/manifests/server/service.pp b/manifests/server/service.pp index 619a1982..990ec3cd 100644 --- a/manifests/server/service.pp +++ b/manifests/server/service.pp @@ -1,11 +1,21 @@ +# @summary +# This class managed ssh server service +# +# @api private +# +# @param ensure +# Ensurable service param +# +# @param enable +# Define if service is enable +# class ssh::server::service ( - $ensure = 'running', - $enable = true -){ - include ::ssh::params - include ::ssh::server + Stdlib::Ensure::Service $ensure = 'running', + Boolean $enable = true, +) { + assert_private() - service { $ssh::params::service_name: + service { $ssh::server::service_name: ensure => $ssh::server::service::ensure, hasstatus => true, hasrestart => true, diff --git a/manifests/site.pp b/manifests/site.pp deleted file mode 100644 index e69de29b..00000000 diff --git a/metadata.json b/metadata.json index e12043e6..a9338916 100644 --- a/metadata.json +++ b/metadata.json @@ -1,62 +1,112 @@ { + "name": "saz-ssh", + "version": "14.1.0", + "author": "saz", + "summary": "Manage SSH client and server via Puppet.", + "license": "Apache-2.0", + "source": "https://github.com/saz/puppet-ssh.git", + "project_page": "https://github.com/saz/puppet-ssh", + "dependencies": [ + { + "name": "puppetlabs/stdlib", + "version_requirement": ">= 9.0.0 < 10.0.0" + }, + { + "name": "puppetlabs/concat", + "version_requirement": ">= 2.2.0 < 10.0.0" + }, + { + "name": "puppet/systemd", + "version_requirement": ">= 3.7.0 < 10.0.0" + } + ], "operatingsystem_support": [ { - "operatingsystem": "RedHat" + "operatingsystem": "RedHat", + "operatingsystemrelease": [ + "8", + "9" + ] + }, + { + "operatingsystem": "CentOS", + "operatingsystemrelease": [ + "9" + ] }, { - "operatingsystem": "CentOS" + "operatingsystem": "OracleLinux", + "operatingsystemrelease": [ + "8", + "9" + ] }, { - "operatingsystem": "OracleLinux" + "operatingsystem": "Debian", + "operatingsystemrelease": [ + "11", + "12" + ] }, { - "operatingsystem": "Scientific" + "operatingsystem": "Ubuntu", + "operatingsystemrelease": [ + "20.04", + "22.04", + "24.04" + ] }, { - "operatingsystem": "Debian" + "operatingsystem": "SLES", + "operatingsystemrelease": [ + "12", + "15" + ] }, { - "operatingsystem": "Ubuntu" + "operatingsystem": "OpenSuSE", + "operatingsystemrelease": [ + "42" + ] }, { - "operatingsystem": "FreeBSD" + "operatingsystem": "FreeBSD", + "operatingsystemrelease": [ + "13" + ] }, { - "operatingsystem": "OpenBSD" + "operatingsystem": "DragonFly", + "operatingsystemrelease": [ + "6" + ] + }, + { + "operatingsystem": "OpenBSD", + "operatingsystemrelease": [ + "7" + ] }, { "operatingsystem": "Gentoo" }, { - "operatingsystem": "ArchLinux" - } - ], - "requirements": [ + "operatingsystem": "Solaris", + "operatingsystemrelease": [ + "11" + ] + }, { - "name": "pe", - "version_requirement": "3.2.x" + "operatingsystem": "Archlinux" }, { - "name": "puppet", - "version_requirement": "3.x" + "operatingsystem": "AIX" } ], - "name": "saz-ssh", - "version": "2.9.0", - "source": "git://github.com/saz/puppet-ssh.git", - "author": "saz", - "license": "Apache-2.0", - "summary": "Manage SSH client and server via Puppet.", - "description": "Manage SSH client and server via puppet", - "project_page": "https://github.com/saz/puppet-ssh", - "dependencies": [ - { - "name": "puppetlabs/stdlib", - "version_requirement": ">= 2.2.1" - }, + "requirements": [ { - "name": "puppetlabs/concat", - "version_requirement": ">= 1.0.0" + "name": "openvox", + "version_requirement": ">= 8.19.0 < 9.0.0" } ] } diff --git a/spec/acceptance/client_spec.rb b/spec/acceptance/client_spec.rb new file mode 100644 index 00000000..474a5b56 --- /dev/null +++ b/spec/acceptance/client_spec.rb @@ -0,0 +1,63 @@ +# frozen_string_literal: true + +require 'spec_helper_acceptance' + +describe 'ssh' do + context 'with client_match_block' do + it_behaves_like 'an idempotent resource' do + let(:manifest) do + <<~PP + class { 'ssh': + client_options => { + 'GlobalKnownHostsFile' => "/var/lib/sss/pubconf/known_hosts", + 'PubkeyAuthentication' => "yes", + 'GSSAPIAuthentication' => "yes", + 'GSSAPIDelegateCredentials' => "yes", + }, + client_match_block => { + 'foo' => { + 'type' => '!localuser', + 'options' => { + 'ProxyCommand' => '/usr/bin/sss_ssh_knownhostsproxy -p %p %h', + }, + }, + 'bar' => { + 'type' => 'host', + 'options' => { + 'ForwardX11' => 'no', + 'PasswordAuthentication' => 'yes', + }, + }, + }, + } + PP + end + + describe file('/etc/ssh/ssh_config') do + it { is_expected.to be_file } + it { is_expected.to be_owned_by 'root' } + it { is_expected.to be_grouped_into 'root' } + it { is_expected.to be_mode '644' } # serverspec does not like a leading 0 + + its(:content) do + is_expected.to match <<~SSH + # File managed by Puppet + + GlobalKnownHostsFile /var/lib/sss/pubconf/known_hosts + PubkeyAuthentication yes + GSSAPIAuthentication yes + GSSAPIDelegateCredentials yes + Host * + HashKnownHosts yes + SendEnv LANG LC_* + Match host bar + ForwardX11 no + PasswordAuthentication yes + Match !localuser foo + ProxyCommand /usr/bin/sss_ssh_knownhostsproxy -p %p %h + SSH + end + end + end + end +end diff --git a/spec/acceptance/init_spec.rb b/spec/acceptance/init_spec.rb new file mode 100644 index 00000000..b8a1e443 --- /dev/null +++ b/spec/acceptance/init_spec.rb @@ -0,0 +1,74 @@ +# frozen_string_literal: true + +require 'spec_helper_acceptance' + +describe 'ssh' do + package_name = case fact('os.family') + when 'Archlinux' + 'openssh' + else + 'openssh-server' + end + context 'with defaults' do + it_behaves_like 'an idempotent resource' do + let(:manifest) do + 'include ssh' + end + + describe package(package_name) do + it { is_expected.to be_installed } + end + + describe port(22) do + it { is_expected.to be_listening } + end + + describe service('sshd') do + it { is_expected.to be_enabled } + it { is_expected.to be_running } + end + end + end + + context 'Server with a seperate sftp_server_init instance on Port 8022' do + it_behaves_like 'an idempotent resource' do + let(:manifest) do + <<-PUPPET + class { 'ssh': + server_instances => { + 'sftp_server_init' => { + 'ensure' => 'present', + 'options' => { + 'sshd_config' => { + 'Port' => 8022, + 'Protocol' => 2, + 'AddressFamily' => 'any', + 'HostKey' => '/etc/ssh/ssh_host_rsa_key', + 'SyslogFacility' => 'AUTH', + 'LogLevel' => 'INFO', + 'PermitRootLogin' => 'no', + }, + 'sshd_service_options' => '', + 'match_blocks' => {}, + }, + }, + }, + } + PUPPET + end + + describe package(package_name) do + it { is_expected.to be_installed } + end + + describe port(8022) do + it { is_expected.to be_listening } + end + + describe service('sftp_server_init') do + it { is_expected.to be_enabled } + it { is_expected.to be_running } + end + end + end +end diff --git a/spec/classes/client_spec.rb b/spec/classes/client_spec.rb index b0b9d4b5..b950a5d3 100644 --- a/spec/classes/client_spec.rb +++ b/spec/classes/client_spec.rb @@ -1,39 +1,39 @@ +# frozen_string_literal: true + require 'spec_helper' describe 'ssh::client', type: 'class' do - context 'On Debian with no other parameters' do - let :facts do - { - osfamily: 'Debian', - interfaces: 'eth0', - ipaddress_eth0: '192.168.1.1', - ipaddress6_eth0: '::1', - concat_basedir: '/tmp', - puppetversion: '3.7.0' - } - end - it do - should contain_package('openssh-client').with(ensure: 'present') - end - end - context 'On Debian with custom ensure' do - let :facts do - { - osfamily: 'Debian', - interfaces: 'eth0', - ipaddress_eth0: '192.168.1.1', - ipaddress6_eth0: '::1', - concat_basedir: '/tmp', - puppetversion: '3.7.0' - } - end - let :params do - { - ensure: 'latest' - } - end - it do - should contain_package('openssh-client').with(ensure: 'latest') + on_supported_os.each do |os, os_facts| + context "on #{os}" do + let(:facts) { os_facts } + + context 'with no other parameters' do + it { is_expected.to compile.with_all_deps } + it { is_expected.to contain_class('ssh::knownhosts') } + it { is_expected.to contain_class('ssh::client::config') } + it { is_expected.to contain_class('ssh::client::install') } + it { is_expected.to contain_concat('/etc/ssh/ssh_config') } + end + + context 'with a different ssh_config location' do + let :params do + { + ssh_config: '/etc/ssh/another_ssh_config' + } + end + + it { is_expected.to contain_concat('/etc/ssh/another_ssh_config') } + end + + context 'with storeconfigs_enabled set to false' do + let :params do + { + storeconfigs_enabled: false + } + end + + it { is_expected.not_to contain_class('ssh::knownhosts') } + end end end end diff --git a/spec/classes/hostkeys_spec.rb b/spec/classes/hostkeys_spec.rb new file mode 100644 index 00000000..e341077d --- /dev/null +++ b/spec/classes/hostkeys_spec.rb @@ -0,0 +1,79 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'ssh::hostkeys', type: 'class' do + _, os_facts = on_supported_os.first + + let(:facts) { os_facts } + + context 'with tags' do + let(:params) do + { + tags: %w[group1 group2] + } + end + + %w[rsa].each do |key_type| + it { + expect(exported_resources).to contain_sshkey("foo.example.com_#{key_type}"). + with( + ensure: 'present', + type: %r{^ssh-#{key_type}}, + tag: %w[group1 group2] + ) + } + end + end + + context 'with storeconfigs_group' do + let(:params) do + { + storeconfigs_group: 'server_group', + } + end + + %w[rsa].each do |key_type| + it { + expect(exported_resources).to contain_sshkey("foo.example.com_#{key_type}"). + with( + ensure: 'present', + type: %r{^ssh-#{key_type}}, + tag: %w[hostkey_all hostkey_server_group] + ) + } + end + end + + context 'with storeconfigs_group and tags' do + let(:params) do + { + storeconfigs_group: 'server_group', + tags: %w[group1 group2], + } + end + + %w[rsa].each do |key_type| + it { + expect(exported_resources).to contain_sshkey("foo.example.com_#{key_type}"). + with( + ensure: 'present', + type: %r{^ssh-#{key_type}}, + tag: %w[hostkey_all hostkey_server_group group1 group2] + ) + } + end + end + + context 'when filtering a key type' do + let(:params) do + { + exclude_key_types: ['ed25519'], + } + end + + it do + expect(exported_resources).not_to contain_sshkey('foo.example.com_ed25519') + end + end +end diff --git a/spec/classes/init_spec.rb b/spec/classes/init_spec.rb index ce791dfc..2246e219 100644 --- a/spec/classes/init_spec.rb +++ b/spec/classes/init_spec.rb @@ -1,62 +1,255 @@ +# frozen_string_literal: true + require 'spec_helper' describe 'ssh', type: 'class' do - context 'On Debian with no other parameters' do - let :facts do - { - osfamily: 'Debian', - interfaces: 'eth0', - ipaddress_eth0: '192.168.1.1', - ipaddress6_eth0: '::1', - concat_basedir: '/tmp', - puppetversion: '3.7.0', - sshdsakey: 'AAAAB3NzaC1kc3MAAACBAODCvvUUnv2imW4cfuLBWVJTLMzds89MtCUXGl3+7Gza5QYJmp7GSkKBnV8+7XI+JAmjv0RKQM1RAn7mV5UplRTtg3CYbeNkX4IakZmNJLTdL4vUyIehhaxBobpOtBaJfFewCJE1plIaWvoWfEDrShcjIUbUbJMfR8YWweIIqp9bAAAAFQCr8+KRfOUZbS9Dz1t15A/Owl61VQAAAIBr/7hNPCvjzAl5+rde6jUR5k20pxAE+z2wsaZxlhrs6ZhhplyCKIXKq4rCx4QuFVPh/c+WJRPO56iH/rSh5Y5cpT1LUk66wNJcOBPprjvDEHfQUHUmfYXzNJ2BHkRL78lfzQr52YyowV6dHfktv0VsIctm13KcMr2KQygZtV6EqgAAAIEAjNC4PRdzYpWfxu268CJDpexlhBwIkIx+ovEibtYeke55qAQcF9UWko4A1c8Wf4nLLxlQYCf501Bt5lb6GmZd0xfpg27fPIfzZPL8o+E756D3ZcNXUaLj4HPRKnwNcdAtChL2jESH3fm8PyNwBI7tV6IOjmOGpyQKtmJq3IyNgms=', - sshrsakey: 'AAAAB3NzaC1yc2EAAAADAQABAAABAQDzA57hAMwz6pywCgxNUcloWeNMvBo2PDPxK2RCegst+9tYaf4S3shnM9a1j2PGBoeRXTuUG6mYB32fJm6/37UUUJA4lT+8CZ3hNnDZU9aitpukkKon7RIlvY1PWO8wT4A5mEa0hfdQg6Um8KZZUs+jrB+8zMJO/X0fmleY54r/JKrP3hNcpaJpTUVQEvMmKacW7nYez/PvWKAz8d02uAOXuauGKhZ9K2AHYKlQFqJ4S1jLiduoGFWxFQ2vQybbN/O0PQQU7EZlHIjSzwoowZLzlxCKCZcKnoDsbGCtYHArbjxTb+m5e7nvsamz7TXLoY90Srmc5QGMxrLUlSvkYsm5', - sshecdsakey: 'AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFDrof0LPA0hGuwODy+5uTynV7rgPJspvZo2TzykBu5mSANJvdL1z5/JS3x16/c/cDjx2lfEkRoVDnon4/NjKEM=', - sshed25519key: '', - id: 'root', - is_pe: false, - path: '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games' - } - end - it do - should contain_class('ssh::client') - end - it do - should contain_class('ssh::server') - end - it do - should contain_concat('/etc/ssh/sshd_config').with_validate_cmd(nil) - end + on_supported_os.each do |os, os_facts| + context "on #{os}" do + let(:facts) { os_facts } + + case os_facts[:os]['family'] + when 'Debian' + client_package = 'openssh-client' + server_package = 'openssh-server' + sftp_server_path = '/usr/lib/openssh/sftp-server' + when 'Archlinux' + client_package = 'openssh' + server_package = 'openssh' + sftp_server_path = '/usr/lib/ssh/sftp-server' + when 'Amazon', 'RedHat' + client_package = 'openssh-clients' + server_package = 'openssh-server' + sftp_server_path = '/usr/libexec/openssh/sftp-server' + when 'Gentoo' + client_package = 'openssh' + server_package = 'openssh' + sftp_server_path = '/usr/lib64/misc/sftp-server' + when 'Solaris' + case os_facts[:os]['release']['major'] + when 10 + client_package = 'SUNWsshu' + server_package = 'SUNWsshdu' + else + client_package = '/network/ssh' + server_package = '/service/network/ssh' + end + sftp_server_path = 'internal-sftp' + when 'SmartOS' + sftp_server_path = 'internal-sftp' + when 'Suse' + client_package = 'openssh' + server_package = 'openssh' + case os_facts[:os]['name'] + when 'OpenSuSE' + sftp_server_path = '/usr/lib/ssh/sftp-server' + when 'SLES' + sftp_server_path = case os_facts[:os]['release']['major'] + when 10, 11 + '/usr/lib64/ssh/sftp-server' + else + '/usr/lib/ssh/sftp-server' + end + end + else + client_package = nil + server_package = nil + sftp_server_path = '/usr/libexec/sftp-server' + end + + case os_facts[:os]['family'] + when 'Solaris' + ssh_config_expected_default = "# File managed by Puppet\n\n" + ssh_config_expected_custom = "# File managed by Puppet\n\nHostFoo\n HostName bar\nSomeOtherKey someValue\n" + sshd_config_default = "# File is managed by Puppet\n\nChallengeResponseAuthentication no\nHostKey /etc/ssh/ssh_host_rsa_key\nHostKey /etc/ssh/ssh_host_dsa_key\nPrintMotd no\nSubsystem sftp #{sftp_server_path}\nX11Forwarding yes\n" + sshd_config_custom = "# File is managed by Puppet\n\nChallengeResponseAuthentication no\nHostKey /etc/ssh/ssh_host_rsa_key\nHostKey /etc/ssh/ssh_host_dsa_key\nPrintMotd no\nSomeOtherKey someValue\nSubsystem sftp #{sftp_server_path}\nUsePAM no\nX11Forwarding no\n" + when 'OpenBSD' + ssh_config_expected_default = "# File managed by Puppet\n\nHost *\n HashKnownHosts yes\n SendEnv LANG LC_*\n" + ssh_config_expected_custom = "# File managed by Puppet\n\nHostFoo\n HostName bar\nSomeOtherKey someValue\nHost *\n HashKnownHosts yes\n SendEnv LANG LC_*\n" + sshd_config_default = "# File is managed by Puppet\n\nAcceptEnv LANG LC_*\nChallengeResponseAuthentication no\nPrintMotd no\nSubsystem sftp #{sftp_server_path}\nX11Forwarding yes\n" + sshd_config_custom = "# File is managed by Puppet\n\nAcceptEnv LANG LC_*\nChallengeResponseAuthentication no\nPrintMotd no\nSomeOtherKey someValue\nSubsystem sftp #{sftp_server_path}\nUsePAM no\nX11Forwarding no\n" + when 'RedHat' + ssh_config_expected_default = "# File managed by Puppet\n\nHost *\n HashKnownHosts yes\n SendEnv LANG LC_*\n" + ssh_config_expected_custom = "# File managed by Puppet\n\nHostFoo\n HostName bar\nSomeOtherKey someValue\nHost *\n HashKnownHosts yes\n SendEnv LANG LC_*\n" + + if os_facts[:os]['release']['major'] == '8' + sshd_config_default = "# File is managed by Puppet\n\nAcceptEnv LANG LC_*\nChallengeResponseAuthentication no\nPrintMotd no\nSubsystem sftp #{sftp_server_path}\nUsePAM yes\nX11Forwarding yes\n" + sshd_config_custom = "# File is managed by Puppet\n\nAcceptEnv LANG LC_*\nChallengeResponseAuthentication no\nPrintMotd no\nSomeOtherKey someValue\nSubsystem sftp #{sftp_server_path}\nUsePAM no\nX11Forwarding no\n" + else + sshd_config_default = "# File is managed by Puppet\nInclude /etc/ssh/sshd_config.d/*.conf\n\nAcceptEnv LANG LC_*\nChallengeResponseAuthentication no\nPrintMotd no\nSubsystem sftp #{sftp_server_path}\nUsePAM yes\nX11Forwarding yes\n" + sshd_config_custom = "# File is managed by Puppet\nInclude /etc/ssh/sshd_config.d/*.conf\n\nAcceptEnv LANG LC_*\nChallengeResponseAuthentication no\nPrintMotd no\nSomeOtherKey someValue\nSubsystem sftp #{sftp_server_path}\nUsePAM no\nX11Forwarding no\n" + end + else + ssh_config_expected_default = "# File managed by Puppet\n\nHost *\n HashKnownHosts yes\n SendEnv LANG LC_*\n" + ssh_config_expected_custom = "# File managed by Puppet\n\nHostFoo\n HostName bar\nSomeOtherKey someValue\nHost *\n HashKnownHosts yes\n SendEnv LANG LC_*\n" + sshd_config_default = "# File is managed by Puppet\n\nAcceptEnv LANG LC_*\nChallengeResponseAuthentication no\nPrintMotd no\nSubsystem sftp #{sftp_server_path}\nUsePAM yes\nX11Forwarding yes\n" + sshd_config_custom = "# File is managed by Puppet\n\nAcceptEnv LANG LC_*\nChallengeResponseAuthentication no\nPrintMotd no\nSomeOtherKey someValue\nSubsystem sftp #{sftp_server_path}\nUsePAM no\nX11Forwarding no\n" + end + + if os_facts[:kernel] == 'Linux' + context 'Server with a separate sftp_server_init instance on Port 8022' do + let :params do + { + 'server_instances' => { + 'sftp_server_init' => { + 'ensure' => 'present', + 'options' => { + 'sshd_config' => { + 'Port' => 8022, + 'Protocol' => 2, + 'AddressFamily' => 'any', + 'HostKey' => '/etc/ssh/ssh_host_rsa_key', + 'SyslogFacility' => 'AUTH', + 'LogLevel' => 'INFO', + 'PermitRootLogin' => 'no', + }, + 'sshd_service_options' => '', + 'match_blocks' => {}, + }, + }, + }, + } + end + + it { is_expected.to compile.with_all_deps } + it { is_expected.to contain_concat('/etc/ssh/sshd_config.sftp_server_init') } + it { is_expected.to contain_concat__fragment('sshd instance sftp_server_init config').with_content("# File is managed by Puppet\nAddressFamily any\nPort 8022\n\nHostKey /etc/ssh/ssh_host_rsa_key\nLogLevel INFO\nPermitRootLogin no\nProtocol 2\nSyslogFacility AUTH\n") } + it { is_expected.to contain_systemd__unit_file('sftp_server_init.service') } + it { is_expected.to contain_service('sftp_server_init.service') } + it { is_expected.to contain_ssh__server__instances('sftp_server_init') } + it { is_expected.to contain_class('ssh::client') } + it { is_expected.to contain_class('ssh::server') } + it { is_expected.to contain_concat('/etc/ssh/sshd_config').with_validate_cmd(nil) } + it { is_expected.to contain_resources('sshkey').with_purge(true) } + end + end + + context 'with all defaults' do + it { is_expected.to compile.with_all_deps } + end + + context 'with the validate_sshd_file setting' do + let :params do + { + validate_sshd_file: true + } + end + + sshd_binary = case os_facts[:os]['family'] + when 'FreeBSD' + '/usr/local/sbin/sshd' + when 'Archlinux' + '/usr/bin/sshd' + else + '/usr/sbin/sshd' + end + it { is_expected.to contain_concat('/etc/ssh/sshd_config').with_validate_cmd("#{sshd_binary} -tf %") } + end + + context 'without resource purging' do + let :params do + { + purge_unmanaged_sshkeys: false + } + end + + it { is_expected.not_to contain_resources('sshkey') } + end + + context 'with no other parameters' do + it { is_expected.to contain_class('ssh::client') } + it { is_expected.to contain_class('ssh::server') } + it { is_expected.to contain_concat('/etc/ssh/ssh_config') } + it { is_expected.to contain_concat('/etc/ssh/sshd_config').with_validate_cmd(nil) } + it { is_expected.to contain_resources('sshkey').with_purge(true) } + it { is_expected.to contain_concat__fragment('global config').with_content(sshd_config_default) } + it { is_expected.to contain_concat__fragment('ssh_config global config').with_content(ssh_config_expected_default) } + + it { is_expected.to contain_package(client_package).with_ensure('installed') } if client_package + it { is_expected.to contain_package(server_package).with_ensure('installed') } if server_package + end + + context 'with custom server options' do + let :params do + { + server_options: { + X11Forwarding: 'no', + UsePAM: 'no', + SomeOtherKey: 'someValue' + } + } + end + + it { is_expected.to contain_concat__fragment('global config').with_content(sshd_config_custom) } + end + + context 'with custom client options' do + let :params do + { + client_options: { + HostFoo: { + HostName: 'bar' + }, + SomeOtherKey: 'someValue' + } + } + end + + it { is_expected.to contain_concat__fragment('ssh_config global config').with_content(ssh_config_expected_custom) } + end + + context 'with storeconfigs_enabled set to false' do + let :params do + { + storeconfigs_enabled: false + } + end + + it { is_expected.not_to contain_class('ssh::knownhosts') } + end + + context 'with client_match_block' do + let :params do + { + client_match_block: { + 'foo' => { + 'type' => '!localuser', + 'options' => { + 'ProxyCommand' => '/usr/bin/sss_ssh_knownhostsproxy -p %p %h', + }, + }, + 'bar' => { + 'type' => 'host', + 'options' => { + 'ForwardX11' => 'no', + 'PasswordAuthentication' => 'yes', + }, + }, + }, + } + end + + it do + is_expected.not_to contain_ssh__client__matchblock('foo').with( + type: '!localuser', + options: { + 'ProxyCommand' => '/usr/bin/sss_ssh_knownhostsproxy -p %p %h', + }, + target: '/etc/ssh/ssh_config_foo' + ) + end + + it do + is_expected.not_to contain_ssh__client__matchblock('bar').with( + type: 'host', + options: { + 'FowardX11' => 'no', + 'PasswordAuthentication' => 'yes', + }, + target: '/etc/ssh/ssh_config_foo' + ) + end - context 'On Debian with the validate_sshd_file setting' do - let :facts do - { - osfamily: 'Debian', - interfaces: 'eth0', - ipaddress_eth0: '192.168.1.1', - ipaddress6_eth0: '::1', - concat_basedir: '/tmp', - puppetversion: '3.7.0', - sshdsakey: 'AAAAB3NzaC1kc3MAAACBAODCvvUUnv2imW4cfuLBWVJTLMzds89MtCUXGl3+7Gza5QYJmp7GSkKBnV8+7XI+JAmjv0RKQM1RAn7mV5UplRTtg3CYbeNkX4IakZmNJLTdL4vUyIehhaxBobpOtBaJfFewCJE1plIaWvoWfEDrShcjIUbUbJMfR8YWweIIqp9bAAAAFQCr8+KRfOUZbS9Dz1t15A/Owl61VQAAAIBr/7hNPCvjzAl5+rde6jUR5k20pxAE+z2wsaZxlhrs6ZhhplyCKIXKq4rCx4QuFVPh/c+WJRPO56iH/rSh5Y5cpT1LUk66wNJcOBPprjvDEHfQUHUmfYXzNJ2BHkRL78lfzQr52YyowV6dHfktv0VsIctm13KcMr2KQygZtV6EqgAAAIEAjNC4PRdzYpWfxu268CJDpexlhBwIkIx+ovEibtYeke55qAQcF9UWko4A1c8Wf4nLLxlQYCf501Bt5lb6GmZd0xfpg27fPIfzZPL8o+E756D3ZcNXUaLj4HPRKnwNcdAtChL2jESH3fm8PyNwBI7tV6IOjmOGpyQKtmJq3IyNgms=', - sshrsakey: 'AAAAB3NzaC1yc2EAAAADAQABAAABAQDzA57hAMwz6pywCgxNUcloWeNMvBo2PDPxK2RCegst+9tYaf4S3shnM9a1j2PGBoeRXTuUG6mYB32fJm6/37UUUJA4lT+8CZ3hNnDZU9aitpukkKon7RIlvY1PWO8wT4A5mEa0hfdQg6Um8KZZUs+jrB+8zMJO/X0fmleY54r/JKrP3hNcpaJpTUVQEvMmKacW7nYez/PvWKAz8d02uAOXuauGKhZ9K2AHYKlQFqJ4S1jLiduoGFWxFQ2vQybbN/O0PQQU7EZlHIjSzwoowZLzlxCKCZcKnoDsbGCtYHArbjxTb+m5e7nvsamz7TXLoY90Srmc5QGMxrLUlSvkYsm5', - sshecdsakey: 'AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFDrof0LPA0hGuwODy+5uTynV7rgPJspvZo2TzykBu5mSANJvdL1z5/JS3x16/c/cDjx2lfEkRoVDnon4/NjKEM=', - sshed25519key: '', - id: 'root', - is_pe: false, - path: '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games' - } - end - let :params do - { - validate_sshd_file: true - } - end - it do - should contain_class('ssh::client') - end - it do - should contain_concat('/etc/ssh/sshd_config').with_validate_cmd('/usr/sbin/sshd -tf %') + it { is_expected.not_to have_ssh__client__matchblock_resource_count(2) } end end end diff --git a/spec/classes/server_spec.rb b/spec/classes/server_spec.rb index 0c674b88..6a843890 100644 --- a/spec/classes/server_spec.rb +++ b/spec/classes/server_spec.rb @@ -1,194 +1,131 @@ +# frozen_string_literal: true + require 'spec_helper' -describe 'ssh::server' do - let :default_params do - { - ensure: 'present', - storeconfigs_enabled: true - } - end - describe 'providing options' do - let :params do - { - options: { - 'TestString' => '/usr/bin', - 'TestBoolean' => true - } - } - end +describe 'ssh::server', type: 'class' do + on_supported_os.each do |os, os_facts| + context "on #{os}" do + let(:facts) { os_facts } - let :facts do - { - osfamily: 'RedHat', - interfaces: 'eth0', - ipaddress_eth0: '192.168.1.1', - ipaddress6_eth0: '::1', - concat_basedir: '/tmp', - puppetversion: '3.7.0', - sshdsakey: 'AAAAB3NzaC1kc3MAAACBAODCvvUUnv2imW4cfuLBWVJTLMzds89MtCUXGl3+7Gza5QYJmp7GSkKBnV8+7XI+JAmjv0RKQM1RAn7mV5UplRTtg3CYbeNkX4IakZmNJLTdL4vUyIehhaxBobpOtBaJfFewCJE1plIaWvoWfEDrShcjIUbUbJMfR8YWweIIqp9bAAAAFQCr8+KRfOUZbS9Dz1t15A/Owl61VQAAAIBr/7hNPCvjzAl5+rde6jUR5k20pxAE+z2wsaZxlhrs6ZhhplyCKIXKq4rCx4QuFVPh/c+WJRPO56iH/rSh5Y5cpT1LUk66wNJcOBPprjvDEHfQUHUmfYXzNJ2BHkRL78lfzQr52YyowV6dHfktv0VsIctm13KcMr2KQygZtV6EqgAAAIEAjNC4PRdzYpWfxu268CJDpexlhBwIkIx+ovEibtYeke55qAQcF9UWko4A1c8Wf4nLLxlQYCf501Bt5lb6GmZd0xfpg27fPIfzZPL8o+E756D3ZcNXUaLj4HPRKnwNcdAtChL2jESH3fm8PyNwBI7tV6IOjmOGpyQKtmJq3IyNgms=', - sshrsakey: 'AAAAB3NzaC1yc2EAAAADAQABAAABAQDzA57hAMwz6pywCgxNUcloWeNMvBo2PDPxK2RCegst+9tYaf4S3shnM9a1j2PGBoeRXTuUG6mYB32fJm6/37UUUJA4lT+8CZ3hNnDZU9aitpukkKon7RIlvY1PWO8wT4A5mEa0hfdQg6Um8KZZUs+jrB+8zMJO/X0fmleY54r/JKrP3hNcpaJpTUVQEvMmKacW7nYez/PvWKAz8d02uAOXuauGKhZ9K2AHYKlQFqJ4S1jLiduoGFWxFQ2vQybbN/O0PQQU7EZlHIjSzwoowZLzlxCKCZcKnoDsbGCtYHArbjxTb+m5e7nvsamz7TXLoY90Srmc5QGMxrLUlSvkYsm5', - sshecdsakey: 'AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFDrof0LPA0hGuwODy+5uTynV7rgPJspvZo2TzykBu5mSANJvdL1z5/JS3x16/c/cDjx2lfEkRoVDnon4/NjKEM=', - sshed25519key: '', - id: 'root', - is_pe: false, - path: '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games' - } - end + case os_facts[:os]['name'] + when 'Debian' + context 'with ssh_server_version_release set to 10.0', if: os_facts[:os]['release']['major'] == '12' do + let(:facts) { os_facts.merge(ssh_server_version_release: '10.0') } - it do - should contain_concat__fragment('global config').with( - target: '/etc/ssh/sshd_config', - content: '# File is managed by Puppet - -AcceptEnv LANG LC_* -ChallengeResponseAuthentication no -PrintMotd no -Subsystem sftp /usr/libexec/openssh/sftp-server -TestBoolean yes -TestString /usr/bin -UsePAM yes -X11Forwarding yes -' - ) - end - end + sshd_config = "# File is managed by Puppet\n\nAcceptEnv LANG LC_*\nKbdInteractiveAuthentication no\nPrintMotd no\nSubsystem sftp /usr/lib/openssh/sftp-server\nUsePAM yes\nX11Forwarding yes\n" + it { is_expected.to contain_concat__fragment('global config').with_content(sshd_config) } + end + end - [{}, - { - ensure: 'latest', - storeconfigs_enabled: true - }, - { - ensure: 'present', - storeconfigs_enabled: false - }].each do |param_set| - describe "when #{param_set == {} ? 'using default' : 'specifying'} class parameters" do - let :param_hash do - default_params.merge(param_set) + svc_name = case os_facts[:os]['family'] + when 'Debian' + 'ssh' + when 'Archlinux' + 'sshd.service' + when 'Darwin' + 'com.openssh.sshd' + when 'Solaris', 'SmartOS' + 'svc:/network/ssh:default' + else + 'sshd' + end + + sshd_config_custom = case os_facts[:os]['family'] + when 'Solaris' + "# File is managed by Puppet\n\nChallengeResponseAuthentication no\nHostKey /etc/ssh/ssh_host_rsa_key\nHostKey /etc/ssh/ssh_host_dsa_key\nPrintMotd no\nSomeOtherKey someValue\nSubsystem sftp /some/path\nUsePAM no\nX11Forwarding no\n" + when 'RedHat' + if os_facts[:os]['release']['major'] == '8' + "# File is managed by Puppet\n\nAcceptEnv LANG LC_*\nChallengeResponseAuthentication no\nPrintMotd no\nSomeOtherKey someValue\nSubsystem sftp /some/path\nUsePAM no\nX11Forwarding no\n" + else + "# File is managed by Puppet\nInclude /etc/ssh/sshd_config.d/*.conf\n\nAcceptEnv LANG LC_*\nChallengeResponseAuthentication no\nPrintMotd no\nSomeOtherKey someValue\nSubsystem sftp /some/path\nUsePAM no\nX11Forwarding no\n" + end + else + "# File is managed by Puppet\n\nAcceptEnv LANG LC_*\nChallengeResponseAuthentication no\nPrintMotd no\nSomeOtherKey someValue\nSubsystem sftp /some/path\nUsePAM no\nX11Forwarding no\n" + end + + context 'with no other parameters' do + it { is_expected.to compile.with_all_deps } + it { is_expected.to contain_class('ssh::knownhosts') } + it { is_expected.to contain_class('ssh::server::config') } + it { is_expected.to contain_class('ssh::server::install') } + it { is_expected.to contain_class('ssh::server::service') } + it { is_expected.to contain_service(svc_name) } + it { is_expected.to contain_concat('/etc/ssh/sshd_config').with_validate_cmd(nil) } + it { is_expected.to contain_concat__fragment('global config') } end - let :params do - param_set + context 'with custom options' do + let :params do + { + options: { + Subsystem: 'sftp /some/path', + X11Forwarding: 'no', + UsePAM: 'no', + SomeOtherKey: 'someValue' + } + } + end + + it { is_expected.to contain_concat__fragment('global config').with_content(sshd_config_custom) } end - ['Debian'].each do |osfamily| - let :facts do + context 'with a custom service_name' do + let :params do { - osfamily: osfamily, - interfaces: 'eth0', - ipaddress_eth0: '192.168.1.1', - ipaddress6_eth0: '::1', - concat_basedir: '/tmp', - puppetversion: '3.7.0', - sshdsakey: 'AAAAB3NzaC1kc3MAAACBAODCvvUUnv2imW4cfuLBWVJTLMzds89MtCUXGl3+7Gza5QYJmp7GSkKBnV8+7XI+JAmjv0RKQM1RAn7mV5UplRTtg3CYbeNkX4IakZmNJLTdL4vUyIehhaxBobpOtBaJfFewCJE1plIaWvoWfEDrShcjIUbUbJMfR8YWweIIqp9bAAAAFQCr8+KRfOUZbS9Dz1t15A/Owl61VQAAAIBr/7hNPCvjzAl5+rde6jUR5k20pxAE+z2wsaZxlhrs6ZhhplyCKIXKq4rCx4QuFVPh/c+WJRPO56iH/rSh5Y5cpT1LUk66wNJcOBPprjvDEHfQUHUmfYXzNJ2BHkRL78lfzQr52YyowV6dHfktv0VsIctm13KcMr2KQygZtV6EqgAAAIEAjNC4PRdzYpWfxu268CJDpexlhBwIkIx+ovEibtYeke55qAQcF9UWko4A1c8Wf4nLLxlQYCf501Bt5lb6GmZd0xfpg27fPIfzZPL8o+E756D3ZcNXUaLj4HPRKnwNcdAtChL2jESH3fm8PyNwBI7tV6IOjmOGpyQKtmJq3IyNgms=', - sshrsakey: 'AAAAB3NzaC1yc2EAAAADAQABAAABAQDzA57hAMwz6pywCgxNUcloWeNMvBo2PDPxK2RCegst+9tYaf4S3shnM9a1j2PGBoeRXTuUG6mYB32fJm6/37UUUJA4lT+8CZ3hNnDZU9aitpukkKon7RIlvY1PWO8wT4A5mEa0hfdQg6Um8KZZUs+jrB+8zMJO/X0fmleY54r/JKrP3hNcpaJpTUVQEvMmKacW7nYez/PvWKAz8d02uAOXuauGKhZ9K2AHYKlQFqJ4S1jLiduoGFWxFQ2vQybbN/O0PQQU7EZlHIjSzwoowZLzlxCKCZcKnoDsbGCtYHArbjxTb+m5e7nvsamz7TXLoY90Srmc5QGMxrLUlSvkYsm5', - sshecdsakey: 'AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFDrof0LPA0hGuwODy+5uTynV7rgPJspvZo2TzykBu5mSANJvdL1z5/JS3x16/c/cDjx2lfEkRoVDnon4/NjKEM=', - sshed25519key: '', - id: 'root', - is_pe: false, - path: '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games' + service_name: 'custom_sshd_name' } end - describe "on supported osfamily: #{osfamily}" do - it { should contain_class('ssh::params') } - it { should contain_package('openssh-server').with_ensure(param_hash[:ensure]) } - - it do - should contain_file('/etc/ssh/sshd_config').with( - 'owner' => 0, - 'group' => 0 - ) - end - - it do - should contain_service('ssh').with( - 'ensure' => 'running', - 'enable' => true, - 'hasrestart' => true, - 'hasstatus' => true - ) - end - - it { should contain_class('concat::setup') } - it { should contain_concat('/etc/ssh/sshd_config') } - it do - should contain_concat__fragment('global config').with( - target: '/etc/ssh/sshd_config', - content: '# File is managed by Puppet - -AcceptEnv LANG LC_* -ChallengeResponseAuthentication no -PrintMotd no -Subsystem sftp /usr/lib/openssh/sftp-server -UsePAM yes -X11Forwarding yes -' - ) - end + it { is_expected.to contain_service('custom_sshd_name') } + end + + context 'with the validate_sshd_file setting' do + let :params do + { + validate_sshd_file: true + } end - describe 'on Arch' do - let :facts do - { - osfamily: 'Archlinux', - lsbdistdescription: 'Arch Linux', - lsbdistid: 'Arch', - interfaces: 'enp4s0', - ipaddress_enp4s0: '192.168.1.1', - ipaddress6_enp4s0: '::1', - concat_basedir: '/tmp', - puppetversion: '3.7.0', - sshdsakey: 'AAAAB3NzaC1kc3MAAACBAODCvvUUnv2imW4cfuLBWVJTLMzds89MtCUXGl3+7Gza5QYJmp7GSkKBnV8+7XI+JAmjv0RKQM1RAn7mV5UplRTtg3CYbeNkX4IakZmNJLTdL4vUyIehhaxBobpOtBaJfFewCJE1plIaWvoWfEDrShcjIUbUbJMfR8YWweIIqp9bAAAAFQCr8+KRfOUZbS9Dz1t15A/Owl61VQAAAIBr/7hNPCvjzAl5+rde6jUR5k20pxAE+z2wsaZxlhrs6ZhhplyCKIXKq4rCx4QuFVPh/c+WJRPO56iH/rSh5Y5cpT1LUk66wNJcOBPprjvDEHfQUHUmfYXzNJ2BHkRL78lfzQr52YyowV6dHfktv0VsIctm13KcMr2KQygZtV6EqgAAAIEAjNC4PRdzYpWfxu268CJDpexlhBwIkIx+ovEibtYeke55qAQcF9UWko4A1c8Wf4nLLxlQYCf501Bt5lb6GmZd0xfpg27fPIfzZPL8o+E756D3ZcNXUaLj4HPRKnwNcdAtChL2jESH3fm8PyNwBI7tV6IOjmOGpyQKtmJq3IyNgms=', - sshrsakey: 'AAAAB3NzaC1yc2EAAAADAQABAAABAQDzA57hAMwz6pywCgxNUcloWeNMvBo2PDPxK2RCegst+9tYaf4S3shnM9a1j2PGBoeRXTuUG6mYB32fJm6/37UUUJA4lT+8CZ3hNnDZU9aitpukkKon7RIlvY1PWO8wT4A5mEa0hfdQg6Um8KZZUs+jrB+8zMJO/X0fmleY54r/JKrP3hNcpaJpTUVQEvMmKacW7nYez/PvWKAz8d02uAOXuauGKhZ9K2AHYKlQFqJ4S1jLiduoGFWxFQ2vQybbN/O0PQQU7EZlHIjSzwoowZLzlxCKCZcKnoDsbGCtYHArbjxTb+m5e7nvsamz7TXLoY90Srmc5QGMxrLUlSvkYsm5', - sshecdsakey: 'AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFDrof0LPA0hGuwODy+5uTynV7rgPJspvZo2TzykBu5mSANJvdL1z5/JS3x16/c/cDjx2lfEkRoVDnon4/NjKEM=', - sshed25519key: '', - id: 'root', - is_pe: false, - path: '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games' - } - end - - it { should contain_class('ssh::params') } - it do - should contain_package('openssh').with( - ensure: param_hash[:ensure], - name: 'openssh' - ) - end - - it do - should contain_file('/etc/ssh/sshd_config').with( - 'owner' => 0, - 'group' => 0 - ) - end - - it do - should contain_service('sshd.service').with( - 'ensure' => 'running', - 'enable' => true, - 'hasrestart' => true, - 'hasstatus' => true - ) - end - - it { should contain_class('concat::setup') } - it { should contain_concat('/etc/ssh/sshd_config') } - it do - should contain_concat__fragment('global config').with( - target: '/etc/ssh/sshd_config', - content: '# File is managed by Puppet - -AcceptEnv LANG LC_* -ChallengeResponseAuthentication no -PrintMotd no -Subsystem sftp /usr/lib/ssh/sftp-server -UsePAM yes -X11Forwarding yes -' - ) - end + + sshd_binary = case os_facts[:os]['family'] + when 'FreeBSD' + '/usr/local/sbin/sshd' + when 'Archlinux' + '/usr/bin/sshd' + else + '/usr/sbin/sshd' + end + it { is_expected.to contain_concat('/etc/ssh/sshd_config').with_validate_cmd("#{sshd_binary} -tf %") } + end + + context 'with a different sshd_binary location' do + let :params do + { + validate_sshd_file: true, + sshd_binary: '/usr/another_bin/sshd' + } + end + + it { is_expected.to contain_concat('/etc/ssh/sshd_config').with_validate_cmd('/usr/another_bin/sshd -tf %') } + end + + context 'with a different sshd_config location' do + let :params do + { + sshd_config: '/etc/ssh/another_sshd_config' + } end + + it { is_expected.to contain_concat('/etc/ssh/another_sshd_config') } + end + + context 'with storeconfigs_enabled set to false' do + let :params do + { + storeconfigs_enabled: false + } + end + + it { is_expected.not_to contain_class('ssh::knownhosts') } end end end diff --git a/spec/defines/client/config/user_spec.rb b/spec/defines/client/config/user_spec.rb index 8fb37798..598f06a4 100644 --- a/spec/defines/client/config/user_spec.rb +++ b/spec/defines/client/config/user_spec.rb @@ -1,150 +1,161 @@ +# frozen_string_literal: true + require 'spec_helper' -describe 'ssh::client::config::user', type: :define do - let :title do - 'riton' - end +describe 'ssh::client::config::user' do + on_supported_os.each do |os, os_facts| + let(:facts) { os_facts } - let :ssh_options do - { - 'HashKnownHosts' => 'yes', - 'Host *.in2p3.fr' => { - 'User' => 'riton', - 'GSSAPIAuthentication' => 'no' - } - } - end - - let :facts do - { - osfamily: 'RedHat', - concat_basedir: '/tmp' - } - end + context "on #{os}" do + let(:title) { 'riton' } - describe 'with invalid parameters' do - params = { - ensure: ['somestate', 'does not'], - target: ['./somedir', 'is not an absolute path'], - user_home_dir: ['./somedir', 'is not an absolute path'], - manage_user_ssh_dir: ['maybe', 'is not a boolean'], - options: ['the_options', 'is not a Hash'] - } - - params.each do |param, value| - context "with invalid value for #{param}" do - let :params do - { - param => value[0] + let :ssh_options do + { + 'HashKnownHosts' => 'yes', + 'Host *.in2p3.fr' => { + 'User' => 'riton', + 'GSSAPIAuthentication' => 'no' } - end - - it 'fails' do - expect do - should compile - end.to raise_error(%r{#{value[1]}}) - end + } end - end # params.each - end # describe 'with invalid parameters' - describe 'with correct values' do - describe 'with a user provided target' do - let :target do - '/root/.ssh/config' + context 'with all defaults' do + it { is_expected.to compile.with_all_deps } end - let :params do - { - target: target + describe 'with invalid parameters' do + params = { + ensure: ['somestate', 'expects a match for Enum'], + target: ['./somedir', 'Pattern'], + user_home_dir: ['./somedir', 'Pattern'], + manage_user_ssh_dir: ['maybe', 'expects a Boolean'], + options: ['the_options', 'Hash value'] } - end - it do - should contain_file(target).with(ensure: 'file', - owner: title, - mode: '0600') - end - end # describe 'with a user provided target' + params.each do |param, value| + context "with invalid value for #{param}" do + let :params do + { + param => value[0] + } + end - describe 'user_home_dir behavior' do - context 'with a user provided user_home_dir' do - let :user_home_dir do - '/path/to/home' - end - - context 'with manage_user_ssh_dir default value' do - let :params do - { - user_home_dir: user_home_dir - } + it { is_expected.not_to compile } end + end + end + # describe 'with invalid parameters' - it 'contains ssh directory and ssh config' do - should contain_file("#{user_home_dir}/.ssh").with(ensure: 'directory', - owner: title, - mode: '0700').that_comes_before("File[#{user_home_dir}/.ssh/config]") + describe 'with correct values' do + describe 'with a user provided target' do + let(:target) { '/root/.ssh/config' } - should contain_file("#{user_home_dir}/.ssh/config").with(ensure: 'file', - owner: title, - mode: '0600') - end - end # context 'with manage_user_ssh_dir default value' - - context 'with manage_user_ssh_dir set to false' do let :params do { - user_home_dir: user_home_dir, - manage_user_ssh_dir: false + target: target } end - it do - should_not contain_file("#{user_home_dir}/.ssh") + it { + is_expected.to contain_concat_file(target).with(ensure: 'present') + is_expected.to contain_concat_fragment(title).with(target: target) + } + end + # describe 'with a user provided target' + + describe 'user_home_dir behavior' do + context 'with a user provided user_home_dir' do + let(:user_home_dir) { '/path/to/home' } + + context 'with manage_user_ssh_dir default value' do + let :params do + { + user_home_dir: user_home_dir + } + end + + it 'contains ssh directory and ssh config' do + is_expected.to contain_file("#{user_home_dir}/.ssh").with( + ensure: 'directory', + owner: title, + mode: '0700' + ).that_comes_before("Concat_file[#{user_home_dir}/.ssh/config]") + + is_expected.to contain_concat_file("#{user_home_dir}/.ssh/config").with( + ensure: 'present', + owner: title, + mode: '0600' + ) + end + end + # context 'with manage_user_ssh_dir default value' + + context 'with manage_user_ssh_dir set to false' do + let :params do + { + user_home_dir: user_home_dir, + manage_user_ssh_dir: false + } + end + + it do + is_expected.not_to contain_file("#{user_home_dir}/.ssh") + end + end + # context 'with manage_user_ssh_dir set to false' end - end # context 'with manage_user_ssh_dir set to false' - end # context 'with a user provided user_home_dir' - - context 'with no user provided user_home_dir' do - it 'with manage_user_ssh_dir default value' do - should contain_file("/home/#{title}/.ssh").that_comes_before("File[/home/#{title}/.ssh/config]") - should contain_file("/home/#{title}/.ssh/config") + # context 'with a user provided user_home_dir' + + context 'with no user provided user_home_dir' do + it 'with manage_user_ssh_dir default value' do + is_expected.to contain_file("/home/#{title}/.ssh").that_comes_before("Concat_file[/home/#{title}/.ssh/config]") + is_expected.to contain_concat_file("/home/#{title}/.ssh/config") + end + + context 'with manage_user_ssh_dir set to false' do + let :params do + { + manage_user_ssh_dir: false + } + end + + it do + is_expected.not_to contain_file("/home/#{title}/.ssh") + end + + it do + is_expected.to contain_concat_file("/home/#{title}/.ssh/config") + end + end + # context 'with manage_user_ssh_dir set to false' + end + # context 'with no user provided user_home_dir' end + # describe 'user_home_dir behavior' - context 'with manage_user_ssh_dir set to false' do + describe 'ssh configuration content' do let :params do { - manage_user_ssh_dir: false + options: ssh_options } end - it do - should_not contain_file("/home/#{title}/.ssh") + it 'has single value' do + is_expected.to contain_concat_fragment(title).with( + content: %r{HashKnownHosts\s+yes}, + target: "/home/#{title}/.ssh/config" + ) end - it do - should contain_file("/home/#{title}/.ssh/config") + it 'has Hash value' do + is_expected.to contain_concat_fragment(title).with( + content: %r{Host \*\.in2p3\.fr\s*\n\s+GSSAPIAuthentication\s+no\s*\n\s+User\s+riton}, + target: "/home/#{title}/.ssh/config" + ) end - end # context 'with manage_user_ssh_dir set to false' - end # context 'with no user provided user_home_dir' - end # describe 'user_home_dir behavior' - - describe 'ssh configuration content' do - let :params do - { - options: ssh_options - } - end - - it 'has single value' do - should contain_file("/home/#{title}/.ssh/config").with(content: %r{HashKnownHosts\s+yes}) - end - - it 'has Hash value' do - should contain_file("/home/#{title}/.ssh/config").with(content: %r{Host \*\.in2p3\.fr\s*\n\s+GSSAPIAuthentication\s+no\s*\n\s+User\s+riton}) + end end end - end # describe 'with correct values' + end end - # vim: tabstop=2 shiftwidth=2 softtabstop=2 diff --git a/spec/defines/client/match_block_spec.rb b/spec/defines/client/match_block_spec.rb new file mode 100644 index 00000000..6db9f6e5 --- /dev/null +++ b/spec/defines/client/match_block_spec.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'ssh::client::match_block' do + on_supported_os.each do |os, os_facts| + context "on #{os}" do + let(:facts) { os_facts } + let :pre_condition do + 'include ssh' + end + + context 'with !foo' do + let(:title) { '!foo' } + let(:params) do + { + 'type' => 'user', + 'options' => { + 'ProxyCommand' => '/usr/bin/sss_ssh_knownhostsproxy -p %p %h', + }, + 'target' => '/etc/ssh/ssh_config_foo', + } + end + + it { is_expected.to compile.with_all_deps } + + it do + is_expected.to contain_concat__fragment('match_block !foo').with( + target: '/etc/ssh/ssh_config_foo', + content: <<~SSH, + Match user !foo + ProxyCommand /usr/bin/sss_ssh_knownhostsproxy -p %p %h + SSH + order: 250 + ) + end + end + end + end +end diff --git a/spec/defines/server/config/setting_spec.rb b/spec/defines/server/config/setting_spec.rb index aed8e483..be520424 100644 --- a/spec/defines/server/config/setting_spec.rb +++ b/spec/defines/server/config/setting_spec.rb @@ -1,78 +1,64 @@ +# frozen_string_literal: true + require 'spec_helper' -describe 'ssh::server::config::setting', type: :define do - let :title do - 'something' - end +describe 'ssh::server::config::setting' do + on_supported_os.each do |os, os_facts| + let(:facts) { os_facts } - let :facts do - { - osfamily: 'RedHat', - concat_basedir: '/tmp', - puppetversion: '3.7.0', - sshdsakey: 'AAAAB3NzaC1kc3MAAACBAODCvvUUnv2imW4cfuLBWVJTLMzds89MtCUXGl3+7Gza5QYJmp7GSkKBnV8+7XI+JAmjv0RKQM1RAn7mV5UplRTtg3CYbeNkX4IakZmNJLTdL4vUyIehhaxBobpOtBaJfFewCJE1plIaWvoWfEDrShcjIUbUbJMfR8YWweIIqp9bAAAAFQCr8+KRfOUZbS9Dz1t15A/Owl61VQAAAIBr/7hNPCvjzAl5+rde6jUR5k20pxAE+z2wsaZxlhrs6ZhhplyCKIXKq4rCx4QuFVPh/c+WJRPO56iH/rSh5Y5cpT1LUk66wNJcOBPprjvDEHfQUHUmfYXzNJ2BHkRL78lfzQr52YyowV6dHfktv0VsIctm13KcMr2KQygZtV6EqgAAAIEAjNC4PRdzYpWfxu268CJDpexlhBwIkIx+ovEibtYeke55qAQcF9UWko4A1c8Wf4nLLxlQYCf501Bt5lb6GmZd0xfpg27fPIfzZPL8o+E756D3ZcNXUaLj4HPRKnwNcdAtChL2jESH3fm8PyNwBI7tV6IOjmOGpyQKtmJq3IyNgms=', - sshrsakey: 'AAAAB3NzaC1yc2EAAAADAQABAAABAQDzA57hAMwz6pywCgxNUcloWeNMvBo2PDPxK2RCegst+9tYaf4S3shnM9a1j2PGBoeRXTuUG6mYB32fJm6/37UUUJA4lT+8CZ3hNnDZU9aitpukkKon7RIlvY1PWO8wT4A5mEa0hfdQg6Um8KZZUs+jrB+8zMJO/X0fmleY54r/JKrP3hNcpaJpTUVQEvMmKacW7nYez/PvWKAz8d02uAOXuauGKhZ9K2AHYKlQFqJ4S1jLiduoGFWxFQ2vQybbN/O0PQQU7EZlHIjSzwoowZLzlxCKCZcKnoDsbGCtYHArbjxTb+m5e7nvsamz7TXLoY90Srmc5QGMxrLUlSvkYsm5', - sshecdsakey: 'AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFDrof0LPA0hGuwODy+5uTynV7rgPJspvZo2TzykBu5mSANJvdL1z5/JS3x16/c/cDjx2lfEkRoVDnon4/NjKEM=', - sshed25519key: '', - id: 'root', - is_pe: false, - path: '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games' - } - end + context "on #{os}" do + let(:title) { 'something' } - describe 'with key => "AllowGroups", value => "group1 group2"' do - let :params do - { - key: 'AllowGroups', - value: 'group1 group2' - } - end + context 'with all defaults' do + it { is_expected.not_to compile } + end - it do - should contain_concat__fragment('ssh_setting_something_AllowGroups').with_content(%r{\nAllowGroups group1 group2\n}) - end - end + describe 'with key => "AllowGroups", value => "group1 group2"' do + let :params do + { + key: 'AllowGroups', + value: 'group1 group2' + } + end - describe 'with key => "Somesetting", value => true' do - let :params do - { - key: 'Somesetting', - value: true - } - end + it { is_expected.to compile.with_all_deps } + it { is_expected.to contain_concat__fragment('ssh_setting_something_AllowGroups').with_content(%r{\nAllowGroups group1 group2\n}) } + end - it do - should contain_concat__fragment('ssh_setting_something_Somesetting').with_content(%r{\nSomesetting yes\n}) - end - end + describe 'with key => "Somesetting", value => true' do + let :params do + { + key: 'Somesetting', + value: true + } + end - describe 'with key => "Foo", value => [1, 2]' do - let :params do - { - key: 'Foo', - value: [1, 2] - } - end + it { is_expected.to contain_concat__fragment('ssh_setting_something_Somesetting').with_content(%r{\nSomesetting yes\n}) } + end - it do - should contain_concat__fragment('ssh_setting_something_Foo').with_content(%r{\nFoo 1 2\n}) - end - end + describe 'with key => "Foo", value => [1, 2]' do + let :params do + { + key: 'Foo', + value: [1, 2] + } + end - describe 'with key => "Bar", value => {"a" => "b"}' do - let :params do - { - key: 'Bar', - value: { - 'a' => 'b' - } - } - end + it { is_expected.to contain_concat__fragment('ssh_setting_something_Foo').with_content(%r{\nFoo 1 2\n}) } + end + + describe 'with key => "Bar", value => {"a" => "b"}' do + let :params do + { + key: 'Bar', + value: { + 'a' => 'b' + } + } + end - it 'fails' do - expect do - should compile - end.to raise_error(%r{Hash values are not supported}) + it { is_expected.to compile.and_raise_error(%r{Hash values are not supported}) } + end end end end diff --git a/spec/defines/server/host_key_spec.rb b/spec/defines/server/host_key_spec.rb new file mode 100644 index 00000000..6a717a9e --- /dev/null +++ b/spec/defines/server/host_key_spec.rb @@ -0,0 +1,152 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'ssh::server::host_key', type: :define do + on_supported_os.each do |os, os_facts| + context "on #{os}" do + let(:facts) { os_facts } + let(:title) { 'something' } + let(:pre_condition) { 'include ssh' } + + context 'with all defaults' do + it { is_expected.to compile.and_raise_error(%r{You must provide either public_key_source or public_key_content parameter}) } + end + + describe 'with public_key_content, private_key_content and certificate_content' do + let :params do + { + public_key_content: 'abc', + private_key_content: 'bcd', + certificate_content: 'cde' + } + end + + it { is_expected.to compile.with_all_deps } + + it { + is_expected.to contain_file('something_pub'). + with_content('abc'). + with_ensure('present'). + with_owner(0). + with_group(0). + with_mode('0644'). + with_path('/etc/ssh/something.pub') + is_expected.to contain_file('something_priv'). + with_content('bcd'). + with_ensure('present'). + with_owner(0). + with_group(0). + with_mode('0600'). + with_path('/etc/ssh/something') + is_expected.to contain_file('something_cert'). + with_content('cde'). + with_ensure('present'). + with_owner(0). + with_group(0). + with_mode('0644'). + with_path('/etc/ssh/something-cert.pub') + } + end + + describe 'with public_key_content and private_key_content' do + let :params do + { + public_key_content: 'abc', + private_key_content: 'bcd' + } + end + + it { + is_expected.to contain_file('something_pub'). + with_content('abc'). + with_ensure('present'). + with_owner(0). + with_group(0). + with_mode('0644'). + with_path('/etc/ssh/something.pub') + is_expected.to contain_file('something_priv'). + with_content('bcd'). + with_ensure('present'). + with_owner(0). + with_group(0). + with_mode('0600'). + with_path('/etc/ssh/something') + is_expected.not_to contain_file('something_cert') + } + end + + describe 'with *_key_content and *_key_source, *_key_source takes precedence' do + let :params do + { + public_key_content: 'abc', + public_key_source: 'a', + private_key_content: 'bcd', + private_key_source: 'b' + } + end + + it { + is_expected.to contain_file('something_pub'). + without_content. + with_source('a'). + with_ensure('present'). + with_owner(0). + with_group(0). + with_mode('0644'). + with_path('/etc/ssh/something.pub') + is_expected.to contain_file('something_priv'). + without_content. + with_source('b'). + with_ensure('present'). + with_owner(0). + with_group(0). + with_mode('0600'). + with_path('/etc/ssh/something') + is_expected.not_to contain_file('something_cert') + } + end + + describe 'with private_key_content and no public_key_content' do + let :params do + { + private_key_content: 'bcd' + } + end + + it { is_expected.to compile.and_raise_error(%r{You must provide either public_key_source or public_key_content parameter}) } + end + + describe 'with public_key_content and no private_key_content' do + let :params do + { + public_key_content: 'abc' + } + end + + it { is_expected.to compile.and_raise_error(%r{You must provide either private_key_source or private_key_content parameter}) } + end + + describe 'with private_key_source and no public_key_source' do + let :params do + { + private_key_source: 'bcd' + } + end + + it { is_expected.to compile.and_raise_error(%r{You must provide either public_key_source or public_key_content parameter}) } + end + + describe 'with public_key_source and no private_key_source' do + let :params do + { + public_key_source: 'abc' + } + end + + it { is_expected.to compile.and_raise_error(%r{You must provide either private_key_source or private_key_content parameter}) } + end + end + end +end +# vim: tabstop=2 shiftwidth=2 softtabstop=2 diff --git a/spec/defines/server/instances_spec.rb b/spec/defines/server/instances_spec.rb new file mode 100644 index 00000000..8ac85a2e --- /dev/null +++ b/spec/defines/server/instances_spec.rb @@ -0,0 +1,157 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'ssh::server::instances' do + on_supported_os.each do |os, os_facts| + context "on #{os}", if: os_facts[:kernel] == 'Linux' do + let(:facts) { os_facts } + let(:title) { 'sftp_server' } + let :pre_condition do + 'include ssh' + end + + context 'with sftp_server present' do + let(:params) do + { + 'ensure' => 'present', + 'options' => { + 'sshd_config' => { + 'Port' => 8022, + 'Protocol' => 2, + 'AddressFamily' => 'any', + 'HostKey' => '/etc/ssh/ssh_host_rsa_key', + 'SyslogFacility' => 'AUTH', + 'LogLevel' => 'INFO', + 'LoginGraceTime' => 120, + 'PermitRootLogin' => 'no', + 'StrictModes' => 'yes', + 'PubkeyAuthentication' => 'yes', + 'HostbasedAuthentication' => 'no', + 'IgnoreUserKnownHosts' => 'no', + 'IgnoreRhosts' => 'yes', + 'PasswordAuthentication' => 'yes', + 'ChallengeResponseAuthentication' => 'no', + 'GSSAPIAuthentication' => 'no', + 'GSSAPIKeyExchange' => 'no', + 'GSSAPICleanupCredentials' => 'yes', + 'UsePAM' => 'yes', + 'AcceptEnv' => %w[LANG LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT LC_IDENTIFICATION LC_ALL], + 'AllowTcpForwarding' => 'no', + 'X11Forwarding' => 'no', + 'X11UseLocalhost' => 'yes', + 'PrintMotd' => 'yes', + 'TCPKeepAlive' => 'yes', + 'ClientAliveInterval' => 0, + 'ClientAliveCountMax' => 0, + 'UseDNS' => 'no', + 'PermitTunnel' => 'no', + 'Banner' => '/etc/ssh/sshd_banner.txt', + 'XAuthLocation' => '/usr/bin/xauth', + 'Subsystem' => 'sftp /usr/libexec/openssh/sftp-server', + 'Ciphers' => %w[aes128-ctr aes192-ctr aes256-ctr aes128-cbc 3des-cbc aes192-cbc aes256-cbc], + 'AllowGroups' => 'root lclssh ssh_all_systems VmAdmins', + }, + 'sshd_service_options' => '', + 'match_blocks' => { + '*,!ssh_exempt_ldap_authkey,!sshlokey' => { + 'type' => 'group', + 'options' => { + 'AuthorizedKeysCommand' => '/usr/local/bin/getauthkey', + 'AuthorizedKeysCommandUser' => 'nobody', + 'AuthorizedKeysFile' => '/dev/null', + }, + }, + 'ssh_deny_pw_auth,sshdnypw' => { + 'type' => 'group', + 'options' => { + 'KbdInteractiveAuthentication' => 'no', + 'PasswordAuthentication' => 'no', + }, + }, + }, + }, + 'service_ensure' => 'running', + 'service_enable' => true, + 'validate_config_file' => true, + } + end + + it { is_expected.to compile.with_all_deps } + it { is_expected.to contain_concat('/etc/ssh/sshd_config.sftp_server') } + it { is_expected.to contain_concat__fragment('sshd instance sftp_server config') } + it { is_expected.to contain_ssh__server__match_block('ssh_deny_pw_auth,sshdnypw') } + it { is_expected.to contain_ssh__server__match_block('*,!ssh_exempt_ldap_authkey,!sshlokey') } + it { is_expected.to contain_systemd__unit_file('sftp_server.service') } + it { is_expected.to contain_service('sftp_server.service') } + end + + context 'with minimal params' do + let(:params) do + { + 'ensure' => 'present', + 'options' => { + 'sshd_config' => { + 'Port' => 8022, + 'Protocol' => 2, + 'AddressFamily' => 'any', + 'HostKey' => '/etc/ssh/ssh_host_rsa_key', + 'SyslogFacility' => 'AUTH', + 'LogLevel' => 'INFO', + 'PermitRootLogin' => 'no', + }, + 'sshd_service_options' => '', + 'match_blocks' => {}, + }, + } + end + + it { is_expected.to compile.with_all_deps } + it { is_expected.to contain_concat__fragment('sshd instance sftp_server config') } + it { is_expected.to contain_systemd__unit_file('sftp_server.service').with_enable(true).with_active(true) } + it { is_expected.to contain_service('sftp_server.service').with_ensure(true).with_enable(true) } + end + + context 'with minimal example and ensure stopped' do + let(:params) do + { + 'ensure' => 'absent', + } + end + + it { is_expected.to compile.with_all_deps } + it { is_expected.to contain_concat__fragment('sshd instance sftp_server config') } + it { is_expected.to contain_systemd__unit_file('sftp_server.service').with_enable(false).with_active(false) } + it { is_expected.to contain_service('sftp_server.service').with_enable(false).with_ensure(false) } + end + + context 'with minimal example and service stopped' do + let(:params) do + { + 'service_ensure' => 'stopped', + 'service_enable' => true, + } + end + + it { is_expected.to compile.with_all_deps } + it { is_expected.to contain_concat__fragment('sshd instance sftp_server config') } + it { is_expected.to contain_systemd__unit_file('sftp_server.service').with_enable(true).with_active(false) } + it { is_expected.to contain_service('sftp_server.service').with_enable(true).with_ensure(false) } + end + + context 'with minimal example and service running but not in autostart' do + let(:params) do + { + 'ensure' => 'present', + 'service_enable' => false, + } + end + + it { is_expected.to compile.with_all_deps } + it { is_expected.to contain_concat__fragment('sshd instance sftp_server config') } + it { is_expected.to contain_systemd__unit_file('sftp_server.service').with_enable(false).with_active(true) } + it { is_expected.to contain_service('sftp_server.service').with_enable(false).with_ensure(true) } + end + end + end +end diff --git a/spec/defines/server/match_block_spec.rb b/spec/defines/server/match_block_spec.rb new file mode 100644 index 00000000..14b306cc --- /dev/null +++ b/spec/defines/server/match_block_spec.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'ssh::server::match_block' do + on_supported_os.each do |os, os_facts| + context "on #{os}" do + let(:facts) { os_facts } + let :pre_condition do + 'include ssh' + end + + context 'with *,!ssh_exempt_ldap_authkey,!sshlokey present' do + let(:title) { '*,!ssh_exempt_ldap_authkey,!sshlokey' } + let(:params) do + { + 'type' => 'group', + 'options' => { + 'AuthorizedKeysCommand' => '/usr/local/bin/getauthkey', + 'AuthorizedKeysCommandUser' => 'nobody', + 'AuthorizedKeysFile' => '/dev/null', + }, + 'target' => '/etc/ssh/sshd_config_sftp_server', + } + end + + it { is_expected.to compile.with_all_deps } + it { is_expected.to contain_concat__fragment('match_block *,!ssh_exempt_ldap_authkey,!sshlokey') } + end + + context 'with ssh_deny_pw_auth,sshdnypw' do + let(:title) { 'ssh_deny_pw_auth,sshdnypw' } + let(:params) do + { + 'type' => 'group', + 'options' => { + 'KbdInteractiveAuthentication' => 'no', + 'PasswordAuthentication' => 'no', + }, + 'target' => '/etc/ssh/sshd_config_sftp_server', + } + end + + it { is_expected.to compile.with_all_deps } + it { is_expected.to contain_concat__fragment('match_block ssh_deny_pw_auth,sshdnypw') } + end + end + end +end diff --git a/spec/fixtures/.gitignore b/spec/fixtures/.gitignore index 0616a13e..81820739 100644 --- a/spec/fixtures/.gitignore +++ b/spec/fixtures/.gitignore @@ -1,5 +1,6 @@ # Ignore everything in this directory * -# Except this file +# Except these files !.gitignore !site.pp +!mock-interface-fact.json diff --git a/spec/fixtures/mock-interface-fact.json b/spec/fixtures/mock-interface-fact.json new file mode 100644 index 00000000..b6ed4ccc --- /dev/null +++ b/spec/fixtures/mock-interface-fact.json @@ -0,0 +1,89 @@ +{ + "networking": { + "interfaces": { + "docker0": { + "bindings": [ + { + "address": "172.17.0.1", + "netmask": "255.255.0.0", + "network": "172.17.0.0" + } + ], + "bindings6": [ + { + "address": "fe80::42:2fff:fea3:f2b7", + "netmask": "ffff:ffff:ffff:ffff::", + "network": "fe80::" + } + ], + "ip": "172.17.0.1", + "ip6": "fe80::42:2fff:fea3:f2b7", + "mac": "02:42:2f:a3:f2:b7", + "mtu": 1500, + "netmask": "255.255.0.0", + "netmask6": "ffff:ffff:ffff:ffff::", + "network": "172.17.0.0", + "network6": "fe80::" + }, + "eno1": { + "bindings": [ + { + "address": "10.13.42.61", + "netmask": "255.255.255.0", + "network": "10.13.42.0" + }, + { + "address": "10.0.0.110", + "netmask": "255.255.255.255", + "network": "10.0.0.110" + }, + { + "address": "10.0.0.104" + }, + { + "address": "10.0.0.109" + } + ], + "bindings6": [ + { + "address": "fe80::6544:473a:6ea4:c385", + "netmask": "ffff:ffff:ffff:ffff::", + "network": "fe80::" + } + ], + "dhcp": "10.13.42.1", + "ip": "10.13.42.61", + "ip6": "fe80::6544:473a:6ea4:c385", + "mac": "08:00:20:97:23:d1", + "mtu": 1500, + "netmask": "255.255.255.0", + "netmask6": "ffff:ffff:ffff:ffff::", + "network": "10.13.42.0", + "network6": "fe80::" + }, + "lo": { + "bindings": [ + { + "address": "127.0.0.1", + "netmask": "255.0.0.0", + "network": "127.0.0.0" + } + ], + "bindings6": [ + { + "address": "::1", + "netmask": "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", + "network": "::1" + } + ], + "ip": "127.0.0.1", + "ip6": "::1", + "mtu": 65536, + "netmask": "255.0.0.0", + "netmask6": "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", + "network": "127.0.0.0", + "network6": "::1" + } + } + } +} diff --git a/spec/functions/ssh/ipaddresses_spec.rb b/spec/functions/ssh/ipaddresses_spec.rb new file mode 100644 index 00000000..7490ac77 --- /dev/null +++ b/spec/functions/ssh/ipaddresses_spec.rb @@ -0,0 +1,75 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'ssh::ipaddresses', type: :puppet_function do + it 'exists' do + is_expected.not_to be_nil + end + + context 'with dummy structured fact data' do + let(:facts) do + JSON.parse File.read(File.join(File.dirname(__FILE__), '../../fixtures/mock-interface-fact.json')) + end + + describe 'without parameters' do + it 'returns all IPs other than localhost' do + is_expected.to run.with_params([], []).and_return(['172.17.0.1', '10.13.42.61', '10.0.0.110', '10.0.0.104', '10.0.0.109']) + end + end + + describe 'with excluded interface' do + it 'doesn\'t return the IPs of excluded interface' do + is_expected.to run.with_params(['docker0'], []).and_return(['10.13.42.61', '10.0.0.110', '10.0.0.104', '10.0.0.109']) + end + end + + describe 'with excluded interfaces' do + it 'doesn\'t return the IPs of those interfaces' do + is_expected.to run.with_params(%w[docker0 eno1], []).and_return([]) + end + end + + describe 'with excluded re interface' do + it 'doesn\'t return the IPs of excluded interface' do + is_expected.to run.with_params([], [%r{^docker}]).and_return(['10.13.42.61', '10.0.0.110', '10.0.0.104', '10.0.0.109']) + end + end + + describe 'with excluded re interfaces' do + it 'doesn\'t return the IPs of those interfaces' do + is_expected.to run.with_params([], [%r{docker0}, %r{no1$}]).and_return([]) + end + end + end + + context 'with dummy legacy fact data' do + let(:facts) do + { + networking: {}, + interfaces: 'lo,docker0,eno1', + ipaddress_lo: '127.0.0.1', + ipaddress_eno1: '10.13.42.61', + ipaddress_docker0: '172.17.0.1' + } + end + + describe 'without parameters' do + it 'returns all IPs other than localhost' do + is_expected.to run.with_params([], []).and_return(['172.17.0.1', '10.13.42.61']) + end + end + + describe 'with excluded interface' do + it 'doesn\'t return the IPs of excluded interface' do + is_expected.to run.with_params(['docker0'], []).and_return(['10.13.42.61']) + end + end + + describe 'with excluded interfaces' do + it 'doesn\'t return the IPs of those interfaces' do + is_expected.to run.with_params(%w[docker0 eno1], []).and_return([]) + end + end + end +end diff --git a/spec/setup_acceptance_node.pp b/spec/setup_acceptance_node.pp new file mode 100644 index 00000000..756baef2 --- /dev/null +++ b/spec/setup_acceptance_node.pp @@ -0,0 +1,5 @@ +if fact('os.name') == 'Debian' and !fact('aio_agent_version') { + package { ['puppet-module-puppetlabs-sshkeys-core']: + ensure => present, + } +} diff --git a/spec/spec.opts b/spec/spec.opts deleted file mode 100644 index 91cd6427..00000000 --- a/spec/spec.opts +++ /dev/null @@ -1,6 +0,0 @@ ---format -s ---colour ---loadby -mtime ---backtrace diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 6e1d9681..58c9b66a 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,2 +1,24 @@ -require 'rspec-puppet' -require 'puppetlabs_spec_helper/module_spec_helper' +# frozen_string_literal: true + +# Managed by modulesync - DO NOT EDIT +# https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ + +# puppetlabs_spec_helper will set up coverage if the env variable is set. +# We want to do this if lib exists and it hasn't been explicitly set. +ENV['COVERAGE'] ||= 'yes' if Dir.exist?(File.expand_path('../lib', __dir__)) + +require 'voxpupuli/test/spec_helper' + +RSpec.configure do |c| + c.facterdb_string_keys = false +end + +add_mocked_facts! + +if File.exist?(File.join(__dir__, 'default_module_facts.yml')) + facts = YAML.safe_load(File.read(File.join(__dir__, 'default_module_facts.yml'))) + facts&.each do |name, value| + add_custom_fact name.to_sym, value + end +end +Dir['./spec/support/spec/**/*.rb'].sort.each { |f| require f } diff --git a/spec/spec_helper_acceptance.rb b/spec/spec_helper_acceptance.rb new file mode 100644 index 00000000..42ecdb41 --- /dev/null +++ b/spec/spec_helper_acceptance.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +require 'voxpupuli/acceptance/spec_helper_acceptance' + +configure_beaker + +Dir['./spec/support/acceptance/**/*.rb'].sort.each { |f| require f } diff --git a/spec/type_aliases/sshclientmatch_spec.rb b/spec/type_aliases/sshclientmatch_spec.rb new file mode 100644 index 00000000..4072e208 --- /dev/null +++ b/spec/type_aliases/sshclientmatch_spec.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'Ssh::ClientMatch' do + known_criteria = %w[ + !all + all + !canonical + canonical + !exec + exec + !final + final + !host + host + !localuser + localuser + !originalhost + originalhost + !user + user + ] + it { is_expected.to allow_values(*known_criteria) } + it { is_expected.not_to allow_value(nil) } + it { is_expected.not_to allow_value('foo') } +end diff --git a/spec/unit/facter/ssh_client_version_spec.rb b/spec/unit/facter/ssh_client_version_spec.rb deleted file mode 100644 index d5ef3d14..00000000 --- a/spec/unit/facter/ssh_client_version_spec.rb +++ /dev/null @@ -1,15 +0,0 @@ -require 'spec_helper' - -describe 'ssh_client_version_full' do - before do - Facter.fact(:kernel).stubs(:value).returns('linux') - end - context 'on a Linux host' do - before do - Facter::Util::Resolution.stubs(:exec).with('ssh -V 2>&1').returns('OpenSSH_6.6.1p1 Ubuntu-2ubuntu2.8, OpenSSL 1.0.1f 6 Jan 2014') - end - it 'execs ssh -V and returns full version number' do - expect(Facter.fact(:ssh_client_version_full).value).to eq('6.6.1p1') - end - end -end diff --git a/spec/unit/facter/ssh_server_version_spec.rb b/spec/unit/facter/ssh_server_version_spec.rb deleted file mode 100644 index f96bcd6f..00000000 --- a/spec/unit/facter/ssh_server_version_spec.rb +++ /dev/null @@ -1,15 +0,0 @@ -require 'spec_helper' - -describe 'ssh_server_version_full' do - before do - Facter.fact(:kernel).stubs(:value).returns('linux') - end - context 'on a Linux host' do - before do - Facter::Util::Resolution.stubs(:exec).with('sshd -V 2>&1').returns('OpenSSH_6.6.1p1 Ubuntu-2ubuntu2.8, OpenSSL 1.0.1f 6 Jan 2014') - end - it 'execs sshd -V' do - expect(Facter.fact(:ssh_server_version_full).value).to eq('6.6.1p1') - end - end -end diff --git a/spec/unit/facter/util/fact_ssh_client_version_spec.rb b/spec/unit/facter/util/fact_ssh_client_version_spec.rb new file mode 100644 index 00000000..7daa3ecc --- /dev/null +++ b/spec/unit/facter/util/fact_ssh_client_version_spec.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'ssh_client_version_full' do + before { Facter.clear } + + after { Facter.clear } + + context 'when on a Linux host' do + before do + allow(Facter.fact(:kernel)).to receive(:value).and_return('linux') + allow(Facter::Util::Resolution).to receive(:which).with('ssh').and_return('/usr/bin/ssh') + allow(Facter::Util::Resolution).to receive(:exec).with('ssh -V 2>&1').and_return('OpenSSH_6.6.1p1 Ubuntu-2ubuntu2.8, OpenSSL 1.0.1f 6 Jan 2014') + end + + it 'execs ssh -V and returns full version number' do + expect(Facter.fact(:ssh_client_version_full).value).to eq('6.6.1p1') + end + end + + context 'when on a SunOS host' do + before do + allow(Facter.fact(:kernel)).to receive(:value).and_return('SunOS') + allow(Facter::Util::Resolution).to receive(:which).with('ssh').and_return('/usr/bin/ssh') + allow(Facter::Util::Resolution).to receive(:exec).with('ssh -V 2>&1').and_return('Sun_SSH_2.4, SSH protocols 1.5/2.0, OpenSSL 0x100020bf') + end + + it 'execs ssh -V and returns full version number' do + expect(Facter.fact(:ssh_client_version_full).value).to eq('2.4') + end + end +end diff --git a/spec/unit/facter/util/fact_ssh_server_version_major_spec.rb b/spec/unit/facter/util/fact_ssh_server_version_major_spec.rb new file mode 100644 index 00000000..59b26f6f --- /dev/null +++ b/spec/unit/facter/util/fact_ssh_server_version_major_spec.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Facter::Util::Fact do + before do + Facter.clear + allow(Facter.fact(:kernel)).to receive(:value).and_return('linux') + end + + describe 'ssh_server_version_major' do + context 'with 3 point semver syntax (6.6.1p1)' do + context 'with ssh_server_version_full fact present returns major version' do + before do + allow(Facter.fact(:ssh_server_version_full)).to receive(:value).and_return('6.6.1p1') + end + + it do + expect(Facter.fact(:ssh_server_version_major).value).to eq('6') + end + end + end + + context 'with 2 point semver syntax (7.2p2)' do + context 'with ssh_server_version_full fact present returns major version' do + before do + allow(Facter.fact(:ssh_server_version_full)).to receive(:value).and_return('7.2p2') + end + + it do + expect(Facter.fact(:ssh_server_version_major).value).to eq('7') + end + end + end + + context 'without ssh_server_version_full fact present returns nil' do + before do + allow(Facter.fact(:ssh_server_version_full)).to receive(:value).and_return(nil) + end + + it do + expect(Facter.fact(:ssh_server_version_major).value).to be_nil + end + end + end +end diff --git a/spec/unit/facter/util/fact_ssh_server_version_spec.rb b/spec/unit/facter/util/fact_ssh_server_version_spec.rb new file mode 100644 index 00000000..116dcfb4 --- /dev/null +++ b/spec/unit/facter/util/fact_ssh_server_version_spec.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'ssh_server_version_full' do + before { Facter.clear } + + after { Facter.clear } + + context 'when on a Linux host' do + before do + allow(Facter.fact(:kernel)).to receive(:value).and_return('linux') + allow(Facter::Util::Resolution).to receive(:which).with('sshd').and_return('/usr/bin/sshd') + allow(Facter::Util::Resolution).to receive(:exec).with('sshd -V 2>&1').and_return('OpenSSH_6.6.1p1 Ubuntu-2ubuntu2.8, OpenSSL 1.0.1f 6 Jan 2014') + end + + it 'execs sshd -V and returns full version number' do + expect(Facter.fact(:ssh_server_version_full).value).to eq('6.6.1p1') + end + end + + context 'when on a SunOS host' do + before do + allow(Facter.fact(:kernel)).to receive(:value).and_return('SunOS') + allow(Facter::Util::Resolution).to receive(:which).with('sshd').and_return('/usr/bin/sshd') + allow(Facter::Util::Resolution).to receive(:exec).with('sshd -V 2>&1').and_return('Sun_SSH_2.4, SSH protocols 1.5/2.0, OpenSSL 0x100020bf') + end + + it 'execs sshd -V and returns full version number' do + expect(Facter.fact(:ssh_server_version_full).value).to eq('2.4') + end + end +end diff --git a/templates/issue.net.erb b/templates/issue.net.erb new file mode 100644 index 00000000..9d81a42b --- /dev/null +++ b/templates/issue.net.erb @@ -0,0 +1,16 @@ + <%= @facts['networking']['hostname'] %> +********* ****************************************** +* This system is a restricted resource and property. * +* Use your administrator assigned user-id to access. * +* Unauthorized use and/or misuse of this system and services may constitute * +* a breach of International criminal law, and could lead to legal * +* disciplinary actions. * +* * +* Individuals using this system without authority or in excess of their * +* authority are subject to having all their activities on this system * +* monitored and recorded or examined by any authorized person, including law * +* enforcement, as system personnel deem appropriate. * +* * +* LOG OFF IMMEDIATELY * +* * +******************************************************************************* diff --git a/templates/options.erb b/templates/options.erb index 83b86cfa..e7f30be1 100644 --- a/templates/options.erb +++ b/templates/options.erb @@ -18,22 +18,22 @@ <%- value = v[key] -%> <%- if value.is_a?(Array) -%> <%- value.each do |a| -%> - <%- if a != '' -%> + <%- if a != '' && a != nil -%> <%= key %> <%= bool2str(a) %> <%- end -%> <%- end -%> - <%- elsif value != '' -%> + <%- elsif value != '' && value != nil -%> <%= key %> <%= bool2str(value) %> <%- end -%> <%- end -%> <%- else -%> <%- if v.is_a?(Array) -%> <%- v.each do |a| -%> -<%- if a != '' -%> +<%- if a != '' && a != nil -%> <%= k %> <%= bool2str(a) %> <%- end -%> <%- end -%> -<%- elsif v != :undef and v != '' -%> +<%- elsif v != nil and v != '' -%> <%= k %> <%= bool2str(v) %> <%- end -%> <%- end -%> diff --git a/templates/ssh_config.erb b/templates/ssh_config.erb index 9c1a0a1c..5546a106 100644 --- a/templates/ssh_config.erb +++ b/templates/ssh_config.erb @@ -12,7 +12,7 @@ end -%> -<%- @options.sort.each do |k, v| -%> +<%- @options.each do |k, v| -%> <%- if v.is_a?(Hash) -%> <%- if k.length > 1024 -%> <%- fail("Line exceeds 1024 characters: #{k}") -%> @@ -21,7 +21,7 @@ <%- v.sort.each do |key, value| -%> <%- if value.is_a?(Array) -%> <%- value.each do |a| -%> - <%- if a != '' -%> + <%- if a != '' && a != nil -%> <%- line_content = "#{key} #{bool2str(a)}" -%> <%- if line_content.length > 1020 -%> <%- fail("Line exceeds 1024 characters: #{line_content}") -%> @@ -30,7 +30,7 @@ <%- end -%> <%- end -%> <%- end -%> - <%- elsif value != '' -%> + <%- elsif value != '' && value != nil -%> <%- line_content = "#{key} #{bool2str(value)}" -%> <%- if line_content.length > 1020 -%> <%- fail("Line exceeds 1024 characters: #{line_content}") -%> @@ -41,7 +41,7 @@ <%- else -%> <%- if v.is_a?(Array) -%> <%- v.each do |a| -%> -<%- if a != '' -%> +<%- if a != '' && a != nil -%> <%- line_content = "#{k} #{bool2str(a)}" -%> <%- if line_content.length > 1024 -%> <%- fail("Line exceeds 1024 characters: #{line_content}") -%> @@ -49,7 +49,7 @@ <%= k %> <%= bool2str(a) %> <%- end -%> <%- end -%> -<%- elsif v != :undef and v != '' -%> +<%- elsif v != :undef && v != '' && v != nil -%> <%- line_content = "#{k} #{bool2str(v)}" -%> <%- if line_content.length > 1024 -%> <%- fail("Line exceeds 1024 characters: #{line_content}") -%> diff --git a/templates/ssh_instance.erb b/templates/ssh_instance.erb new file mode 100644 index 00000000..8a9a4d3c --- /dev/null +++ b/templates/ssh_instance.erb @@ -0,0 +1,71 @@ +# File is managed by Puppet +<%- + def bool2str(v) + case v + when true + 'yes' + when false + 'no' + else + v + end + end +-%> +<%- if addressfamily = @sshd_instance_config.delete('AddressFamily') -%> +AddressFamily <%= addressfamily %> +<%- end -%> +<%- if port = @sshd_instance_config.delete('Port') -%> +<%- if port.is_a?(Array) -%> +<%- port.reject{ |x| x.to_s.strip.empty? }.each do |p| -%> +Port <%= p %> +<%- end -%> +<%- elsif not port.to_s.strip.empty? -%> +Port <%= port %> +<%- end -%> +<%- end -%> +<%- if listen = @sshd_instance_config.delete('ListenAddress') -%> +<%- if listen.is_a?(Array) -%> +<%- listen.reject{ |x| x.strip.empty? }.each do |l| -%> +ListenAddress <%= l %> +<%- end -%> +<%- elsif not listen.strip.empty? -%> +ListenAddress <%= listen %> +<%- end -%> +<%- end -%> + +<%- @sshd_instance_config.keys.sort_by{ |sk| (sk.to_s.downcase.include? "match") ? 'zzz' + sk.to_s : sk.to_s }.each do |k| -%> +<%- v = @sshd_instance_config[k] -%> +<%- if v.is_a?(Hash) -%> +<%= k %> +<%- v.keys.sort.each do |key| -%> + <%- value = v[key] -%> + <%- if value.is_a?(Array) -%> + <%- if ['ciphers', 'macs', 'kexalgorithms', 'gssapikexalgorithms', 'hostbasedacceptedkeytypes', 'hostbasedacceptedalgorithms', 'hostkeyalgorithms', 'pubkeyacceptedkeytypes', 'pubkeyacceptedalgorithms'].include?(key.downcase) -%> + <%= key %> <%= value.join(',') %> + <%- else -%> + <%- value.each do |a| -%> + <%- if a != '' && a != nil -%> + <%= key %> <%= bool2str(a) %> + <%- end -%> + <%- end -%> + <%- end -%> + <%- elsif value != '' && value != nil -%> + <%= key %> <%= bool2str(value) %> + <%- end -%> +<%- end -%> +<%- else -%> +<%- if v.is_a?(Array) -%> +<%- if ['ciphers', 'macs', 'kexalgorithms', 'gssapikexalgorithms', 'hostbasedacceptedkeytypes', 'hostbasedacceptedalgorithms', 'hostkeyalgorithms', 'pubkeyacceptedkeytypes', 'pubkeyacceptedalgorithms'].include?(k.downcase) -%> +<%= k %> <%= v.join(',') %> +<%- else -%> +<%- v.each do |a| -%> +<%- if a != '' && a != nil -%> +<%= k %> <%= bool2str(a) %> +<%- end -%> +<%- end -%> +<%- end -%> +<%- elsif v != nil and v != '' -%> +<%= k %> <%= bool2str(v) %> +<%- end -%> +<%- end -%> +<%- end -%> diff --git a/templates/ssh_instance_service.erb b/templates/ssh_instance_service.erb new file mode 100644 index 00000000..f59164f6 --- /dev/null +++ b/templates/ssh_instance_service.erb @@ -0,0 +1,35 @@ +# <%= @sshd_instance_service_name %> +# This file is managed by Puppet. +# DO NOT EDIT + +[Unit] +Description=SSHD Instance <%= @title %> +Documentation=man:sshd(8) man:sshd_config(5) +After=network.target sshd-keygen.service +Wants=sshd-keygen.service + +[Service] +<%- if @sshd_additional_service_options -%> +<%- @sshd_additional_service_options.each do |k,v| -%> +<%- if v.is_a?(Array) -%> +<%- v.each do |a| -%> +<%- if a != '' && a != nil -%> +<%= k %>=<%= bool2str(a) %> +<%- end -%> +<%- end -%> +<%- elsif v != '' && v != nil -%> +<%= k %>=<%= bool2str(v) %> +<%- end -%> +<%- end -%> +<%- end -%> +<% if @sshd_environments_file %> +EnvironmentFile=<%= @sshd_environments_file -%> +<% end %> +ExecStart=<%= @sshd_binary %> -f <%= @sshd_instance_config_file %> -D $OPTIONS <%= @sshd_service_options %> +ExecReload=/bin/kill -HUP $MAINPID +KillMode=process +Restart=on-failure +RestartSec=15s + +[Install] +WantedBy=multi-user.target diff --git a/templates/sshd_config.erb b/templates/sshd_config.erb index bd594a67..e924c216 100644 --- a/templates/sshd_config.erb +++ b/templates/sshd_config.erb @@ -11,11 +11,16 @@ end end -%> -<%- options = scope.lookupvar('ssh::server::merged_options') -%> -<%- if addressfamily = options.delete('AddressFamily') -%> +<%- if @include_dir -%> +Include <%= @include_dir %>/*.conf +<%- end -%> +<%- if @include -%> +Include <%= @include %> +<%- end -%> +<%- if addressfamily = @options.delete('AddressFamily') -%> AddressFamily <%= addressfamily %> <%- end -%> -<%- if port = options.delete('Port') -%> +<%- if port = @options.delete('Port') -%> <%- if port.is_a?(Array) -%> <%- port.reject{ |x| x.to_s.strip.empty? }.each do |p| -%> Port <%= p %> @@ -24,7 +29,7 @@ Port <%= p %> Port <%= port %> <%- end -%> <%- end -%> -<%- if listen = options.delete('ListenAddress') -%> +<%- if listen = @options.delete('ListenAddress') -%> <%- if listen.is_a?(Array) -%> <%- listen.reject{ |x| x.strip.empty? }.each do |l| -%> ListenAddress <%= l %> @@ -34,30 +39,38 @@ ListenAddress <%= listen %> <%- end -%> <%- end -%> -<%- options.keys.sort_by{ |sk| (sk.to_s.downcase.include? "match") ? 'zzz' + sk.to_s : sk.to_s }.each do |k| -%> -<%- v = options[k] -%> +<%- @options.keys.sort_by{ |sk| (sk.to_s.downcase.include? "match") ? 'zzz' + sk.to_s : sk.to_s }.each do |k| -%> +<%- v = @options[k] -%> <%- if v.is_a?(Hash) -%> <%= k %> <%- v.keys.sort.each do |key| -%> <%- value = v[key] -%> <%- if value.is_a?(Array) -%> + <%- if ['ciphers', 'macs', 'kexalgorithms'].include?(key.downcase) -%> + <%= key %> <%= value.join(',') %> + <%- else -%> <%- value.each do |a| -%> - <%- if a != '' -%> + <%- if a != '' && a != nil -%> <%= key %> <%= bool2str(a) %> <%- end -%> <%- end -%> - <%- elsif value != '' -%> + <%- end -%> + <%- elsif value != '' && value != nil -%> <%= key %> <%= bool2str(value) %> <%- end -%> <%- end -%> <%- else -%> <%- if v.is_a?(Array) -%> +<%- if ['ciphers', 'macs', 'kexalgorithms'].include?(k.downcase) -%> +<%= k %> <%= v.join(',') %> +<%- else -%> <%- v.each do |a| -%> -<%- if a != '' -%> +<%- if a != '' && a != nil -%> <%= k %> <%= bool2str(a) %> <%- end -%> <%- end -%> -<%- elsif v != :undef and v != '' -%> +<%- end -%> +<%- elsif v != nil and v != '' -%> <%= k %> <%= bool2str(v) %> <%- end -%> <%- end -%> diff --git a/tests/init.pp b/tests/init.pp deleted file mode 100644 index 6687c2c7..00000000 --- a/tests/init.pp +++ /dev/null @@ -1 +0,0 @@ -class { '::ssh::server': } diff --git a/tests/server.pp b/tests/server.pp deleted file mode 100644 index 93a23812..00000000 --- a/tests/server.pp +++ /dev/null @@ -1 +0,0 @@ -include ::ssh::server diff --git a/types/clientmatch.pp b/types/clientmatch.pp new file mode 100644 index 00000000..5acbae87 --- /dev/null +++ b/types/clientmatch.pp @@ -0,0 +1,19 @@ +# OpenSSH client `Match` criteria. See `ssh_config(5)` +type Ssh::ClientMatch = Enum[ + '!all', + 'all', + '!canonical', + 'canonical', + '!exec', + 'exec', + '!final', + 'final', + '!host', + 'host', + '!localuser', + 'localuser', + '!originalhost', + 'originalhost', + '!user', + 'user', +]