├── .coveralls.yml ├── spec ├── fixtures │ └── unit │ │ └── puppet │ │ └── provider │ │ └── pam │ │ └── augeas │ │ ├── empty │ │ ├── ubuntu2404 │ │ ├── broken │ │ └── full ├── spec_helper.rb └── unit │ └── puppet │ └── provider │ └── pam │ └── augeas_spec.rb ├── .gitmodules ├── .fixtures.yml ├── .msync.yml ├── .github ├── labeler.yml ├── workflows │ ├── labeler.yml │ ├── ci.yml │ ├── release.yml │ └── prepare_release.yml ├── PULL_REQUEST_TEMPLATE.md ├── ISSUE_TEMPLATE.md └── release.yml ├── .puppet-lint.rc ├── .rubocop.yml ├── .editorconfig ├── .gitignore ├── .sync.yml ├── .pmtignore ├── LICENSE ├── .rubocop_todo.yml ├── HISTORY.md ├── Gemfile ├── Rakefile ├── .travis.sh ├── metadata.json ├── .overcommit.yml ├── lib └── puppet │ ├── type │ └── pam.rb │ └── provider │ └── pam │ └── augeas.rb ├── CHANGELOG.md └── README.md /.coveralls.yml: -------------------------------------------------------------------------------- 1 | service_name: travis-ci 2 | -------------------------------------------------------------------------------- /spec/fixtures/unit/puppet/provider/pam/augeas/empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "augeas"] 2 | path = augeas 3 | url = git://github.com/hercules-team/augeas.git 4 | -------------------------------------------------------------------------------- /.fixtures.yml: -------------------------------------------------------------------------------- 1 | --- 2 | fixtures: 3 | repositories: 4 | augeasproviders_core: https://github.com/voxpupuli/puppet-augeasproviders_core 5 | -------------------------------------------------------------------------------- /.msync.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Managed by modulesync - DO NOT EDIT 3 | # https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ 4 | 5 | modulesync_config_version: '10.4.0' 6 | -------------------------------------------------------------------------------- /.github/labeler.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Managed by modulesync - DO NOT EDIT 3 | # https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ 4 | 5 | skip-changelog: 6 | - head-branch: ['^release-*', 'release'] 7 | -------------------------------------------------------------------------------- /.puppet-lint.rc: -------------------------------------------------------------------------------- 1 | # Managed by modulesync - DO NOT EDIT 2 | # https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ 3 | 4 | --fail-on-warnings 5 | --no-parameter_documentation-check 6 | --no-parameter_types-check 7 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Managed by modulesync - DO NOT EDIT 3 | # https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ 4 | 5 | inherit_from: .rubocop_todo.yml 6 | inherit_gem: 7 | voxpupuli-test: rubocop.yml 8 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | 3 | # Managed by modulesync - DO NOT EDIT 4 | # https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ 5 | 6 | root = true 7 | 8 | [*] 9 | charset = utf-8 10 | end_of_line = lf 11 | indent_size = 2 12 | tab_width = 2 13 | indent_style = space 14 | insert_final_newline = true 15 | trim_trailing_whitespace = true 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Managed by modulesync - DO NOT EDIT 2 | # https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ 3 | 4 | /pkg/ 5 | /Gemfile.lock 6 | /Gemfile.local 7 | /vendor/ 8 | /.vendor/ 9 | /spec/fixtures/manifests/ 10 | /spec/fixtures/modules/ 11 | /.vagrant/ 12 | /.bundle/ 13 | /.ruby-version 14 | /coverage/ 15 | /log/ 16 | /.idea/ 17 | /.dependencies/ 18 | /.librarian/ 19 | /Puppetfile.lock 20 | *.iml 21 | .*.sw? 22 | /.yardoc/ 23 | /Guardfile 24 | bolt-debug.log 25 | .rerun.json 26 | -------------------------------------------------------------------------------- /.github/workflows/labeler.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Managed by modulesync - DO NOT EDIT 3 | # https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ 4 | 5 | name: "Pull Request Labeler" 6 | 7 | # yamllint disable-line rule:truthy 8 | on: 9 | pull_request_target: {} 10 | 11 | permissions: 12 | contents: read 13 | pull-requests: write 14 | 15 | jobs: 16 | labeler: 17 | permissions: 18 | contents: read 19 | pull-requests: write 20 | runs-on: ubuntu-latest 21 | steps: 22 | - uses: actions/labeler@v5 23 | -------------------------------------------------------------------------------- /.sync.yml: -------------------------------------------------------------------------------- 1 | --- 2 | .github/workflows/ci.yml: 3 | with: 4 | additional_packages: libaugeas-dev augeas-tools 5 | Gemfile: 6 | optional: 7 | ':test': 8 | - gem: ruby-augeas 9 | spec/spec_helper.rb: 10 | spec_overrides: 11 | - "require 'fixtures/modules/augeasproviders_core/spec/support/spec/psh_fixtures'" 12 | - "require 'augeas_spec'" 13 | - "# augeasproviders: setting $LOAD_PATH to work around broken type autoloading" 14 | - "$LOAD_PATH.unshift(File.join(__dir__, 'fixtures/modules/augeasproviders_core/lib'))" 15 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Managed by modulesync - DO NOT EDIT 3 | # https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ 4 | 5 | name: CI 6 | 7 | # yamllint disable-line rule:truthy 8 | on: 9 | pull_request: {} 10 | push: 11 | branches: 12 | - main 13 | - master 14 | 15 | concurrency: 16 | group: ${{ github.ref_name }} 17 | cancel-in-progress: true 18 | 19 | permissions: 20 | contents: read 21 | 22 | jobs: 23 | puppet: 24 | name: Puppet 25 | uses: voxpupuli/gha-puppet/.github/workflows/basic.yml@v4 26 | with: 27 | additional_packages: 'libaugeas-dev augeas-tools' 28 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 9 | #### Pull Request (PR) description 10 | 13 | 14 | #### This Pull Request (PR) fixes the following issues 15 | 21 | -------------------------------------------------------------------------------- /.pmtignore: -------------------------------------------------------------------------------- 1 | # Managed by modulesync - DO NOT EDIT 2 | # https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ 3 | 4 | /docs/ 5 | /pkg/ 6 | /Gemfile 7 | /Gemfile.lock 8 | /Gemfile.local 9 | /vendor/ 10 | /.vendor/ 11 | /spec/ 12 | /Rakefile 13 | /.vagrant/ 14 | /.bundle/ 15 | /.ruby-version 16 | /coverage/ 17 | /log/ 18 | /.idea/ 19 | /.dependencies/ 20 | /.github/ 21 | /.librarian/ 22 | /Puppetfile.lock 23 | /Puppetfile 24 | *.iml 25 | /.editorconfig 26 | /.fixtures.yml 27 | /.gitignore 28 | /.msync.yml 29 | /.overcommit.yml 30 | /.pmtignore 31 | /.rspec 32 | /.rspec_parallel 33 | /.rubocop.yml 34 | /.sync.yml 35 | .*.sw? 36 | /.yardoc/ 37 | /.yardopts 38 | /Dockerfile 39 | /HISTORY.md 40 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Managed by modulesync - DO NOT EDIT 3 | # https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ 4 | 5 | name: Release 6 | 7 | # yamllint disable-line rule:truthy 8 | on: 9 | push: 10 | tags: 11 | - '*' 12 | 13 | permissions: 14 | contents: write 15 | 16 | jobs: 17 | release: 18 | name: Release 19 | uses: voxpupuli/gha-puppet/.github/workflows/release.yml@v3 20 | with: 21 | allowed_owner: 'voxpupuli' 22 | secrets: 23 | # Configure secrets here: 24 | # https://docs.github.com/en/actions/security-guides/encrypted-secrets 25 | username: ${{ secrets.PUPPET_FORGE_USERNAME }} 26 | api_key: ${{ secrets.PUPPET_FORGE_API_KEY }} 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | augeasproviders: alternative Augeas-based providers for Puppet 2 | 3 | Copyright (c) 2012-2016 Dominic Cleal and Raphaël Pinson 4 | Copyright (c) 2017-2020 Raphaël Pinson 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 10 | 11 | ## Affected Puppet, Ruby, OS and module versions/distributions 12 | 13 | - Puppet: 14 | - Ruby: 15 | - Distribution: 16 | - Module version: 17 | 18 | ## How to reproduce (e.g Puppet code you use) 19 | 20 | ## What are you seeing 21 | 22 | ## What behaviour did you expect instead 23 | 24 | ## Output log 25 | 26 | ## Any additional information you'd like to impart 27 | -------------------------------------------------------------------------------- /.rubocop_todo.yml: -------------------------------------------------------------------------------- 1 | # This configuration was generated by 2 | # `rubocop --auto-gen-config` 3 | # on 2023-08-17 21:29:47 UTC using RuboCop version 1.50.2. 4 | # The point is for the user to remove these configuration records 5 | # one by one as the offenses are removed from the code base. 6 | # Note that changes in the inspected code, or installation of new 7 | # versions of RuboCop, may require this file to be generated again. 8 | 9 | # Offense count: 2 10 | # This cop supports unsafe autocorrection (--autocorrect-all). 11 | RSpec/BeEq: 12 | Exclude: 13 | - 'spec/unit/puppet/provider/pam/augeas_spec.rb' 14 | 15 | # Offense count: 1 16 | # This cop supports unsafe autocorrection (--autocorrect-all). 17 | Style/SlicingWithRange: 18 | Exclude: 19 | - 'lib/puppet/provider/pam/augeas.rb' 20 | -------------------------------------------------------------------------------- /HISTORY.md: -------------------------------------------------------------------------------- 1 | ## [2.3.0](https://github.com/voxpupuli/puppet-augeasproviders_pam/tree/2.3.0) (2021-08-19) 2 | 3 | - allow augeasproviders_core 3.x 4 | 5 | ## 2.2.1 6 | 7 | - Fix puppet requirement to < 7.0.0 8 | 9 | ## 2.2.0 10 | 11 | - Add support for Puppet 6 12 | - Drop support for Puppet < 5 13 | - Update supported OSes 14 | 15 | ## 2.1.1 16 | 17 | - Upped supported Puppet versions to include Puppet 5 18 | 19 | ## 2.1.0 20 | 21 | - Support forced positioning (thanks to Michael Marod) 22 | - Do not version Gemfile.lock 23 | - Sudo needed on Travis 24 | - Update copyright 25 | - Improve README 26 | 27 | ## 2.0.3 28 | 29 | - Add requirements to metadata.json 30 | 31 | ## 2.0.2 32 | 33 | - Fix metadata.json 34 | 35 | ## 2.0.1 36 | 37 | - Remove erratic symlink in spec directory 38 | 39 | ## 2.0.0 40 | 41 | - First release of split module. 42 | -------------------------------------------------------------------------------- /spec/fixtures/unit/puppet/provider/pam/augeas/ubuntu2404: -------------------------------------------------------------------------------- 1 | #%PAM-1.0 2 | # common-auth - authentication settings common to all services 3 | auth [success=1 default=ignore] pam_unix.so nullok 4 | auth requisite pam_deny.so 5 | auth required pam_permit.so 6 | auth optional pam_cap.so 7 | 8 | # account 9 | account [success=1 new_authtok_reqd=done default=ignore] pam_unix.so 10 | account requisite pam_deny.so 11 | account required pam_permit.so 12 | 13 | # password 14 | password [success=1 default=ignore] pam_unix.so obscure use_authtok try_first_pass yescrypt 15 | password requisite pam_deny.so 16 | password required pam_permit.so 17 | 18 | # session 19 | session [default=1] pam_permit.so 20 | session requisite pam_deny.so 21 | session required pam_permit.so 22 | session optional pam_umask.so 23 | session required pam_unix.so 24 | session optional pam_systemd.so 25 | -------------------------------------------------------------------------------- /.github/workflows/prepare_release.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Managed by modulesync - DO NOT EDIT 3 | # https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ 4 | 5 | name: 'Prepare Release' 6 | 7 | on: 8 | workflow_dispatch: 9 | inputs: 10 | version: 11 | description: 'Module version to be released. Must be a valid semver string without leading v. (1.2.3)' 12 | required: false 13 | 14 | permissions: 15 | contents: write 16 | pull-requests: write 17 | 18 | jobs: 19 | release_prep: 20 | uses: 'voxpupuli/gha-puppet/.github/workflows/prepare_release.yml@v3' 21 | with: 22 | version: ${{ github.event.inputs.version }} 23 | allowed_owner: 'voxpupuli' 24 | secrets: 25 | # Configure secrets here: 26 | # https://docs.github.com/en/actions/security-guides/encrypted-secrets 27 | github_pat: '${{ secrets.PCCI_PAT_RELEASE_PREP }}' 28 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # Managed by modulesync - DO NOT EDIT 2 | # https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ 3 | 4 | source ENV['GEM_SOURCE'] || 'https://rubygems.org' 5 | 6 | group :test do 7 | gem 'voxpupuli-test', '~> 13.0', :require => false 8 | gem 'puppet_metadata', '~> 5.0', :require => false 9 | gem 'ruby-augeas', :require => false 10 | end 11 | 12 | group :development do 13 | gem 'guard-rake', :require => false 14 | gem 'overcommit', '>= 0.39.1', :require => false 15 | end 16 | 17 | group :system_tests do 18 | gem 'voxpupuli-acceptance', '~> 4.0', :require => false 19 | end 20 | 21 | group :release do 22 | gem 'voxpupuli-release', '~> 5.0', :require => false 23 | end 24 | 25 | gem 'rake', :require => false 26 | 27 | gem 'openvox', ENV.fetch('OPENVOX_GEM_VERSION', [">= 7", "< 9"]), :require => false, :groups => [:test] 28 | 29 | # vim: syntax=ruby 30 | -------------------------------------------------------------------------------- /.github/release.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Managed by modulesync - DO NOT EDIT 3 | # https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ 4 | 5 | # https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes 6 | 7 | changelog: 8 | exclude: 9 | labels: 10 | - duplicate 11 | - invalid 12 | - modulesync 13 | - question 14 | - skip-changelog 15 | - wont-fix 16 | - wontfix 17 | 18 | categories: 19 | - title: Breaking Changes 🛠 20 | labels: 21 | - backwards-incompatible 22 | 23 | - title: New Features 🎉 24 | labels: 25 | - enhancement 26 | 27 | - title: Bug Fixes 🐛 28 | labels: 29 | - bug 30 | 31 | - title: Documentation Updates 📚 32 | labels: 33 | - documentation 34 | - docs 35 | 36 | - title: Dependency Updates ⬆️ 37 | labels: 38 | - dependencies 39 | 40 | - title: Other Changes 41 | labels: 42 | - "*" 43 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # Managed by modulesync - DO NOT EDIT 2 | # https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ 3 | 4 | begin 5 | require 'voxpupuli/test/rake' 6 | rescue LoadError 7 | # only available if gem group test is installed 8 | end 9 | 10 | begin 11 | require 'voxpupuli/acceptance/rake' 12 | rescue LoadError 13 | # only available if gem group acceptance is installed 14 | end 15 | 16 | begin 17 | require 'voxpupuli/release/rake_tasks' 18 | rescue LoadError 19 | # only available if gem group releases is installed 20 | else 21 | GCGConfig.user = 'voxpupuli' 22 | GCGConfig.project = 'puppet-augeasproviders_pam' 23 | end 24 | 25 | desc "Run main 'test' task and report merged results to coveralls" 26 | task test_with_coveralls: [:test] do 27 | if Dir.exist?(File.expand_path('../lib', __FILE__)) 28 | require 'coveralls/rake/task' 29 | Coveralls::RakeTask.new 30 | Rake::Task['coveralls:push'].invoke 31 | else 32 | puts 'Skipping reporting to coveralls. Module has no lib dir' 33 | end 34 | end 35 | 36 | # vim: syntax=ruby 37 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Managed by modulesync - DO NOT EDIT 4 | # https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ 5 | 6 | # puppetlabs_spec_helper will set up coverage if the env variable is set. 7 | # We want to do this if lib exists and it hasn't been explicitly set. 8 | ENV['COVERAGE'] ||= 'yes' if Dir.exist?(File.expand_path('../lib', __dir__)) 9 | 10 | require 'voxpupuli/test/spec_helper' 11 | 12 | RSpec.configure do |c| 13 | c.facterdb_string_keys = false 14 | end 15 | 16 | add_mocked_facts! 17 | 18 | if File.exist?(File.join(__dir__, 'default_module_facts.yml')) 19 | facts = YAML.safe_load(File.read(File.join(__dir__, 'default_module_facts.yml'))) 20 | facts&.each do |name, value| 21 | add_custom_fact name.to_sym, value 22 | end 23 | end 24 | 25 | require 'fixtures/modules/augeasproviders_core/spec/support/spec/psh_fixtures' 26 | 27 | require 'augeas_spec' 28 | 29 | # augeasproviders: setting $LOAD_PATH to work around broken type autoloading 30 | 31 | $LOAD_PATH.unshift(File.join(__dir__, 'fixtures/modules/augeasproviders_core/lib')) 32 | Dir['./spec/support/spec/**/*.rb'].sort.each { |f| require f } 33 | -------------------------------------------------------------------------------- /.travis.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -xe 3 | 4 | # Clone submodules in tree 5 | git submodule update --init 6 | 7 | if [ -z $AUGEAS ]; then 8 | # Use latest version of lenses 9 | cd augeas && git pull origin master 10 | PKG_VERSION="" 11 | else 12 | if [ -z $LENSES ]; then 13 | # Use matching version of lenses 14 | cd augeas && git fetch && git checkout release-${AUGEAS} 15 | else 16 | cd augeas && git fetch && git checkout $LENSES 17 | fi 18 | 19 | PKG_VERSION="=${AUGEAS}*" 20 | # Add PPA 21 | sudo add-apt-repository -y ppa:raphink/augeas-1.0.0 22 | sudo add-apt-repository -y ppa:raphink/augeas-1.1.0 23 | sudo add-apt-repository -y ppa:raphink/augeas-1.2.0 24 | sudo add-apt-repository -y ppa:raphink/augeas-1.3.0 25 | fi 26 | sudo add-apt-repository -y ppa:raphink/augeas 27 | sudo apt-get update 28 | sudo apt-get install augeas-tools${PKG_VERSION} \ 29 | augeas-lenses${PKG_VERSION} \ 30 | libaugeas0${PKG_VERSION} \ 31 | libaugeas-dev${PKG_VERSION} \ 32 | libxml2-dev 33 | 34 | # Install gems 35 | gem install bundler 36 | bundle install 37 | 38 | # Reporting only 39 | bundle show 40 | puppet --version 41 | augtool --version 42 | -------------------------------------------------------------------------------- /spec/fixtures/unit/puppet/provider/pam/augeas/broken: -------------------------------------------------------------------------------- 1 | #%PAM-1.0 2 | # This file is auto-generated. 3 | # User changes will be destroyed the next time authconfig is run. 4 | auth 5 | auth sufficient pam_unix.so nullok try_first_pass 6 | auth requisite pam_succeed_if.so uid >= 1000 quiet_success 7 | auth sufficient pam_sss.so use_first_pass 8 | auth required pam_deny.so 9 | 10 | account required pam_unix.so broken_shadow 11 | account sufficient pam_localuser.so 12 | account sufficient pam_succeed_if.so uid < 1000 quiet 13 | account [default=bad success=ok user_unknown=ignore] pam_sss.so 14 | account required pam_permit.so 15 | 16 | password requisite pam_pwquality.so try_first_pass retry=3 type= 17 | password sufficient pam_unix.so sha512 shadow nullok try_first_pass use_authtok 18 | password sufficient pam_sss.so use_authtok 19 | password required pam_deny.so 20 | 21 | session optional pam_keyinit.so revoke 22 | session required pam_limits.so 23 | -session optional pam_systemd.so 24 | session optional pam_mkhomedir.so 25 | session [success=1 default=ignore] pam_succeed_if.so service in crond quiet use_uid 26 | session required pam_unix.so 27 | session optional pam_sss.so 28 | -------------------------------------------------------------------------------- /spec/fixtures/unit/puppet/provider/pam/augeas/full: -------------------------------------------------------------------------------- 1 | #%PAM-1.0 2 | # This file is auto-generated. 3 | # User changes will be destroyed the next time authconfig is run. 4 | auth required pam_env.so 5 | auth sufficient pam_unix.so nullok try_first_pass 6 | auth requisite pam_succeed_if.so uid >= 1000 quiet_success 7 | auth sufficient pam_sss.so use_first_pass 8 | auth required pam_deny.so 9 | 10 | account required pam_unix.so broken_shadow 11 | account sufficient pam_localuser.so 12 | account sufficient pam_succeed_if.so uid < 1000 quiet 13 | account [default=bad success=ok user_unknown=ignore] pam_sss.so 14 | account required pam_permit.so 15 | 16 | password requisite pam_pwquality.so try_first_pass retry=3 type= 17 | password sufficient pam_unix.so sha512 shadow nullok try_first_pass use_authtok 18 | password sufficient pam_sss.so use_authtok 19 | password required pam_deny.so 20 | 21 | session optional pam_keyinit.so revoke 22 | session required pam_limits.so 23 | -session optional pam_systemd.so 24 | session optional pam_mkhomedir.so 25 | session [success=1 default=ignore] pam_succeed_if.so service in crond quiet use_uid 26 | session required pam_unix.so 27 | session optional pam_sss.so 28 | -------------------------------------------------------------------------------- /metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "puppet-augeasproviders_pam", 3 | "version": "5.0.1-rc0", 4 | "author": "Vox Pupuli", 5 | "summary": "Augeas-based PAM type and provider for Puppet", 6 | "license": "Apache-2.0", 7 | "source": "https://github.com/voxpupuli/puppet-augeasproviders_pam", 8 | "project_page": "https://github.com/voxpupuli/puppet-augeasproviders_pam", 9 | "issues_url": "https://github.com/voxpupuli/puppet-augeasproviders_pam/issues", 10 | "description": "This module provides type/providers for PAM using the Augeas configuration API library.", 11 | "dependencies": [ 12 | { 13 | "name": "puppet/augeasproviders_core", 14 | "version_requirement": ">= 2.4.0 < 5.0.0" 15 | } 16 | ], 17 | "operatingsystem_support": [ 18 | { 19 | "operatingsystem": "Debian", 20 | "operatingsystemrelease": [ 21 | "10", 22 | "11", 23 | "12" 24 | ] 25 | }, 26 | { 27 | "operatingsystem": "Ubuntu", 28 | "operatingsystemrelease": [ 29 | "18.04", 30 | "20.04", 31 | "22.04", 32 | "24.04" 33 | ] 34 | }, 35 | { 36 | "operatingsystem": "RedHat", 37 | "operatingsystemrelease": [ 38 | "7", 39 | "8" 40 | ] 41 | } 42 | ], 43 | "requirements": [ 44 | { 45 | "name": "openvox", 46 | "version_requirement": ">= 8.19.0 < 9.0.0" 47 | } 48 | ] 49 | } 50 | -------------------------------------------------------------------------------- /.overcommit.yml: -------------------------------------------------------------------------------- 1 | # Managed by modulesync - DO NOT EDIT 2 | # https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ 3 | # 4 | # Hooks are only enabled if you take action. 5 | # 6 | # To enable the hooks run: 7 | # 8 | # ``` 9 | # bundle exec overcommit --install 10 | # # ensure .overcommit.yml does not harm to you and then 11 | # bundle exec overcommit --sign 12 | # ``` 13 | # 14 | # (it will manage the .git/hooks directory): 15 | # 16 | # Examples howto skip a test for a commit or push: 17 | # 18 | # ``` 19 | # SKIP=RuboCop git commit 20 | # SKIP=PuppetLint git commit 21 | # SKIP=RakeTask git push 22 | # ``` 23 | # 24 | # Don't invoke overcommit at all: 25 | # 26 | # ``` 27 | # OVERCOMMIT_DISABLE=1 git commit 28 | # ``` 29 | # 30 | # Read more about overcommit: https://github.com/brigade/overcommit 31 | # 32 | # To manage this config yourself in your module add 33 | # 34 | # ``` 35 | # .overcommit.yml: 36 | # unmanaged: true 37 | # ``` 38 | # 39 | # to your modules .sync.yml config 40 | --- 41 | PreCommit: 42 | RuboCop: 43 | enabled: true 44 | description: 'Runs rubocop on modified files only' 45 | command: ['bundle', 'exec', 'rubocop'] 46 | RakeTarget: 47 | enabled: true 48 | description: 'Runs lint on modified files only' 49 | targets: 50 | - 'lint' 51 | command: ['bundle', 'exec', 'rake'] 52 | YamlSyntax: 53 | enabled: true 54 | JsonSyntax: 55 | enabled: true 56 | TrailingWhitespace: 57 | enabled: true 58 | 59 | PrePush: 60 | RakeTarget: 61 | enabled: true 62 | description: 'Run rake targets' 63 | targets: 64 | - 'validate' 65 | - 'test' 66 | - 'rubocop' 67 | command: ['bundle', 'exec', 'rake'] 68 | -------------------------------------------------------------------------------- /lib/puppet/type/pam.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Manages settings in PAM service files 4 | # 5 | # Copyright (c) 2012 Greg Swift 6 | # Licensed under the Apache License, Version 2.0 7 | 8 | Puppet::Type.newtype(:pam) do 9 | @doc = "Manages settings in an PAM service files. 10 | 11 | The resource name is a descriptive string only due to the non-uniqueness of any single paramter." 12 | 13 | ensurable do 14 | defaultvalues 15 | block if block_given? 16 | 17 | newvalue(:positioned) do 18 | current = retrieve 19 | if current == :absent 20 | provider.create 21 | elsif !provider.in_position? 22 | provider.destroy 23 | provider.create 24 | end 25 | end 26 | 27 | def insync?(is) 28 | return true if (should == :positioned) && (is == :present) && provider.in_position? 29 | 30 | super 31 | end 32 | end 33 | 34 | def munge_boolean(value) 35 | case value 36 | when true, 'true', :true 37 | :true 38 | when false, 'false', :false 39 | :false 40 | else 41 | raise('munge_boolean only takes booleans') 42 | end 43 | end 44 | 45 | newparam(:name) do 46 | desc 'The name of the resource, has no bearing on anything' 47 | isnamevar 48 | end 49 | 50 | newparam(:service) do 51 | desc "The PAM service this entry will be placed in. Typically this is the same as the 52 | filename under /etc/pam.d" 53 | end 54 | 55 | newparam(:type) do 56 | desc 'The PAM service type of the setting: account, auth, password, session.' 57 | newvalues(:account, :auth, :password, :session) 58 | end 59 | 60 | newparam(:module) do 61 | desc 'The name of the specific PAM module to load.' 62 | end 63 | 64 | newproperty(:optional, boolean: true) do 65 | desc 'Whether failure to load the module will break things' 66 | 67 | newvalue(:true) 68 | newvalue(:false) 69 | 70 | munge do |value| 71 | @resource.munge_boolean(value) 72 | end 73 | end 74 | 75 | newproperty(:arguments, array_matching: :all) do 76 | desc 'Arguments to assign for the module.' 77 | defaultto { [] } 78 | end 79 | 80 | newproperty(:control) do 81 | desc "Simple or complex definition of the module's behavior on failure." 82 | end 83 | 84 | newparam(:control_is_param, boolean: true) do 85 | desc 'Whether `control` should be considered a parameter or a property.' 86 | 87 | newvalues :false, :true 88 | defaultto :false 89 | 90 | munge do |value| 91 | @resource.munge_boolean(value) 92 | end 93 | end 94 | 95 | newparam(:position) do 96 | desc "A three part text field that providers the placement position of an entry. 97 | 98 | The field consists of `placement identifier value` 99 | 100 | Placement can be either `before` or `after` 101 | Identifier can be either `first`, `last`, `module`, or an Augeas xpath 102 | Value is matched as follows: 103 | With `first` and `last` match `value` to the `control` field, can be blank for absolute positioning. 104 | With `module` matches the `module` field of the associated line, can not be blank. 105 | With an Augeas xpath this field will be ignored, and should be blank. 106 | " 107 | defaultto('before last') 108 | validate do |value| 109 | placement, identifier, val = value.split(%r{ }) 110 | raise ArgumentError, "#{placement} is not a valid placement in position" unless %w[before after].include? placement 111 | # Don't do validation of the second field because we are supporting xpath 112 | # and thats hard to validate 113 | # unless ['first', 'last', 'module'].include? identifier or identifier =~ // 114 | # raise ArgumentError, "%s is not a valid identifier in position" % indentifier 115 | # end 116 | raise ArgumentError, 'Value must be set if you are matching on module' if val.nil? && (identifier == 'module') 117 | end 118 | end 119 | 120 | newparam(:target) do 121 | desc 'The file in which to store the settings, defaults to `/etc/pam.d/{service}`.' 122 | end 123 | end 124 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | Each new release typically also includes the latest modulesync defaults. 5 | These should not affect the functionality of the module. 6 | 7 | ## [v5.0.0](https://github.com/voxpupuli/puppet-augeasproviders_pam/tree/v5.0.0) (2025-11-11) 8 | 9 | [Full Changelog](https://github.com/voxpupuli/puppet-augeasproviders_pam/compare/v4.0.0...v5.0.0) 10 | 11 | **Breaking changes:** 12 | 13 | - Drop puppet, update openvox minimum version to 8.19 [\#64](https://github.com/voxpupuli/puppet-augeasproviders_pam/pull/64) ([TheMeier](https://github.com/TheMeier)) 14 | 15 | **Implemented enhancements:** 16 | 17 | - Add Ubuntu 24.04 [\#68](https://github.com/voxpupuli/puppet-augeasproviders_pam/pull/68) ([bwitt](https://github.com/bwitt)) 18 | - Add support for Debian 12 [\#66](https://github.com/voxpupuli/puppet-augeasproviders_pam/pull/66) ([smortex](https://github.com/smortex)) 19 | - metadata.json: Add OpenVox [\#61](https://github.com/voxpupuli/puppet-augeasproviders_pam/pull/61) ([jstraw](https://github.com/jstraw)) 20 | 21 | ## [v4.0.0](https://github.com/voxpupuli/puppet-augeasproviders_pam/tree/v4.0.0) (2023-06-22) 22 | 23 | [Full Changelog](https://github.com/voxpupuli/puppet-augeasproviders_pam/compare/v3.0.1...v4.0.0) 24 | 25 | **Breaking changes:** 26 | 27 | - Drop Puppet 6 support [\#43](https://github.com/voxpupuli/puppet-augeasproviders_pam/pull/43) ([bastelfreak](https://github.com/bastelfreak)) 28 | 29 | **Implemented enhancements:** 30 | 31 | - puppet/augeasproviders\_core: Allow 4.x [\#45](https://github.com/voxpupuli/puppet-augeasproviders_pam/pull/45) ([bastelfreak](https://github.com/bastelfreak)) 32 | - Add puppet 8 support [\#44](https://github.com/voxpupuli/puppet-augeasproviders_pam/pull/44) ([bastelfreak](https://github.com/bastelfreak)) 33 | 34 | ## [v3.0.1](https://github.com/voxpupuli/puppet-augeasproviders_pam/tree/v3.0.1) (2022-07-22) 35 | 36 | [Full Changelog](https://github.com/voxpupuli/puppet-augeasproviders_pam/compare/v3.0.0...v3.0.1) 37 | 38 | **Fixed bugs:** 39 | 40 | - update references to voxpupuli namespace [\#36](https://github.com/voxpupuli/puppet-augeasproviders_pam/pull/36) ([vchepkov](https://github.com/vchepkov)) 41 | 42 | ## [v3.0.0](https://github.com/voxpupuli/puppet-augeasproviders_pam/tree/v3.0.0) (2022-07-08) 43 | 44 | [Full Changelog](https://github.com/voxpupuli/puppet-augeasproviders_pam/compare/2.3.0...v3.0.0) 45 | 46 | **Breaking changes:** 47 | 48 | - Drop EoL OS [\#33](https://github.com/voxpupuli/puppet-augeasproviders_pam/pull/33) ([bastelfreak](https://github.com/bastelfreak)) 49 | - Drop Puppet 5 support [\#32](https://github.com/voxpupuli/puppet-augeasproviders_pam/pull/32) ([ekohl](https://github.com/ekohl)) 50 | 51 | **Implemented enhancements:** 52 | 53 | - Add Puppet 7 support [\#34](https://github.com/voxpupuli/puppet-augeasproviders_pam/pull/34) ([bastelfreak](https://github.com/bastelfreak)) 54 | 55 | **Closed issues:** 56 | 57 | - redcarpet version specified in Gemfile flagged in CVE-2020-26298 [\#31](https://github.com/voxpupuli/puppet-augeasproviders_pam/issues/31) 58 | 59 | ## [2.3.0](https://github.com/voxpupuli/puppet-augeasproviders_pam/tree/2.3.0) (2021-08-19) 60 | 61 | - allow augeasproviders_core 3.x 62 | 63 | ## 2.2.1 64 | 65 | - Fix puppet requirement to < 7.0.0 66 | 67 | ## 2.2.0 68 | 69 | - Add support for Puppet 6 70 | - Drop support for Puppet < 5 71 | - Update supported OSes 72 | 73 | ## 2.1.1 74 | 75 | - Upped supported Puppet versions to include Puppet 5 76 | 77 | ## 2.1.0 78 | 79 | - Support forced positioning (thanks to Michael Marod) 80 | - Do not version Gemfile.lock 81 | - Sudo needed on Travis 82 | - Update copyright 83 | - Improve README 84 | 85 | ## 2.0.3 86 | 87 | - Add requirements to metadata.json 88 | 89 | ## 2.0.2 90 | 91 | - Fix metadata.json 92 | 93 | ## 2.0.1 94 | 95 | - Remove erratic symlink in spec directory 96 | 97 | ## 2.0.0 98 | 99 | - First release of split module. 100 | 101 | 102 | \* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)* 103 | -------------------------------------------------------------------------------- /lib/puppet/provider/pam/augeas.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Alternative Augeas-based providers for Puppet 4 | # 5 | # Copyright (c) 2012 Greg Swift 6 | # Licensed under the Apache License, Version 2.0 7 | 8 | raise('Missing augeasproviders_core dependency') if Puppet::Type.type(:augeasprovider).nil? 9 | 10 | Puppet::Type.type(:pam).provide(:augeas, parent: Puppet::Type.type(:augeasprovider).provider(:default)) do 11 | desc 'Uses Augeas API to update an pam parameter' 12 | 13 | # Boolean is the key because they either do or do not provide a 14 | # value for control to work against. Module doesn't work against 15 | # control 16 | PAM_POSITION_ALIASES = { # rubocop:todo Lint/ConstantDefinitionInBlock 17 | true => { 'first' => "*[type='%s' and control='%s'][1]", 18 | 'last' => "*[type='%s' and control='%s'][last()]", 19 | 'module' => "*[type='%s' and module='%s'][1]", }, 20 | false => { 'first' => "*[type='%s'][1]", 21 | 'last' => "*[type='%s'][last()]", }, 22 | }.freeze 23 | 24 | confine feature: :augeas 25 | 26 | default_file { '/etc/pam.d/system-auth' } 27 | 28 | def self.target(resource = nil) 29 | if resource && resource[:service] && !(resource[:target]) 30 | "/etc/pam.d/#{resource[:service]}".chomp('/') 31 | else 32 | super 33 | end 34 | end 35 | 36 | lens do |resource| 37 | target(resource) == '/etc/pam.conf' ? 'pamconf.lns' : 'pam.lns' 38 | end 39 | 40 | resource_path do |resource| 41 | service = resource[:service] 42 | type = resource[:type] 43 | mod = resource[:module] 44 | control_cond = resource[:control_is_param] == :true ? "and control='#{resource[:control]}'" : '' 45 | if target == '/etc/pam.conf' 46 | "$target/*[service='#{service}' and type='#{type}' and module='#{mod}' #{control_cond}]" 47 | else 48 | "$target/*[type='#{type}' and module='#{mod}' #{control_cond}]" 49 | end 50 | end 51 | 52 | def self.position_path(position, type) 53 | placement, identifier, value = position.split(%r{ }) 54 | key = !value.nil? 55 | if PAM_POSITION_ALIASES[key].key? identifier 56 | expr = PAM_POSITION_ALIASES[key][identifier] 57 | expr = key ? format(expr, type, value) : format(expr, type) 58 | else 59 | # if the identifier is not in the mapping 60 | # we assume that its an xpath and so 61 | # join everything after the placement 62 | expr = position.split(%r{ })[1..-1].join(' ') 63 | end 64 | [expr, placement] 65 | end 66 | 67 | def in_position? 68 | return if resource[:position].nil? 69 | 70 | path, before = self.class.position_path(resource[:position], resource[:type]) 71 | 72 | mpath = if before == 'before' 73 | "#{resource_path}[following-sibling::#{path}]" 74 | else 75 | "#{resource_path}[preceding-sibling::#{path}]" 76 | end 77 | 78 | augopen do |aug| 79 | !aug.match(mpath).empty? 80 | end 81 | end 82 | 83 | def self.instances 84 | augopen do |aug| 85 | resources = [] 86 | aug.match("$target/*[label()!='#comment']").each do |spath| 87 | optional = aug.match("#{spath}/optional").empty?.to_s.to_sym 88 | type = aug.get("#{spath}/type") 89 | control = aug.get("#{spath}/control") 90 | mod = aug.get("#{spath}/module") 91 | arguments = aug.match("#{spath}/argument").map { |p| aug.get(p) } 92 | entry = { ensure: :present, 93 | optional: optional, 94 | type: type, 95 | control: control, 96 | module: mod, 97 | arguments: arguments } 98 | entry[:service] = aug.get("#{spath}/service") if target == '/etc/pam.conf' 99 | resources << new(entry) 100 | end 101 | resources 102 | end 103 | end 104 | 105 | define_aug_method!(:create) do |aug, resource| 106 | path = next_seq(aug.match('$target/*')) 107 | entry_path = "$target/#{path}" 108 | # we pull type, control, and position out because we actually 109 | # work with those values, not just reference them in the set section 110 | # type comes to us as a symbol, so needs to be converted to a string 111 | type = resource[:type].to_s 112 | control = resource[:control] 113 | position = resource[:position] 114 | unless position.nil? 115 | expr, placement = position_path(position, type) 116 | aug.insert("$target/#{expr}", path, placement == 'before') 117 | end 118 | aug.touch("#{entry_path}/optional") if resource[:optional] == :true 119 | aug.set("#{entry_path}/service", resource[:service]) if target == '/etc/pam.conf' 120 | aug.set("#{entry_path}/type", type) 121 | aug.set("#{entry_path}/control", control) 122 | aug.set("#{entry_path}/module", resource[:module]) 123 | resource[:arguments].each do |argument| 124 | aug.set("#{entry_path}/argument[last()+1]", argument) 125 | end 126 | end 127 | 128 | define_aug_method(:optional) do |aug, _resource| 129 | aug.match('$resource/optional').empty?.to_s.to_sym 130 | end 131 | 132 | define_aug_method!(:optional=) do |aug, resource, _value| 133 | if resource[:optional] == :true 134 | aug.clear('$resource/optional') if aug.match('$resource/optional').empty? 135 | else 136 | aug.rm('$resource/optional') 137 | end 138 | end 139 | 140 | attr_aug_accessor(:control) 141 | 142 | attr_aug_accessor(:arguments, type: :array, label: 'argument') 143 | end 144 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://github.com/voxpupuli/puppet-augeasproviders_pam/workflows/CI/badge.svg)](https://github.com/voxpupuli/puppet-augeasproviders_pam/actions?query=workflow%3ACI) 2 | [![Release](https://github.com/voxpupuli/puppet-augeasproviders_pam/actions/workflows/release.yml/badge.svg)](https://github.com/voxpupuli/puppet-augeasproviders_pam/actions/workflows/release.yml) 3 | [![Puppet Forge](https://img.shields.io/puppetforge/v/puppet/augeasproviders_pam.svg)](https://forge.puppetlabs.com/puppet/augeasproviders_pam) 4 | [![Puppet Forge - downloads](https://img.shields.io/puppetforge/dt/puppet/augeasproviders_pam.svg)](https://forge.puppetlabs.com/puppet/augeasproviders_pam) 5 | [![Puppet Forge - endorsement](https://img.shields.io/puppetforge/e/puppet/augeasproviders_pam.svg)](https://forge.puppetlabs.com/puppet/augeasproviders_pam) 6 | [![Puppet Forge - scores](https://img.shields.io/puppetforge/f/puppet/augeasproviders_pam.svg)](https://forge.puppetlabs.com/puppet/augeasproviders_pam) 7 | [![puppetmodule.info docs](http://www.puppetmodule.info/images/badge.png)](http://www.puppetmodule.info/m/puppet-augeasproviders_pam) 8 | [![Coverage Status](https://img.shields.io/coveralls/voxpupuli/puppet-augeasproviders_pam.svg)](https://coveralls.io/r/voxpupuli/puppet-augeasproviders_pam) 9 | [![Apache-2 License](https://img.shields.io/github/license/voxpupuli/puppet-augeasproviders_pam.svg)](LICENSE) 10 | 11 | # pam: type/provider for PAM files for Puppet 12 | 13 | This module provides a new type/provider for Puppet to read and modify PAM 14 | config files using the Augeas configuration library. 15 | 16 | The advantage of using Augeas over the default Puppet `parsedfile` 17 | implementations is that Augeas will go to great lengths to preserve file 18 | formatting and comments, while also failing safely when needed. 19 | 20 | This provider will hide *all* of the Augeas commands etc., you don't need to 21 | know anything about Augeas to make use of it. 22 | 23 | ## Requirements 24 | 25 | Ensure both Augeas and ruby-augeas 0.3.0+ bindings are installed and working as 26 | normal. 27 | 28 | See [Puppet/Augeas pre-requisites](http://docs.puppetlabs.com/guides/augeas.html#pre-requisites). 29 | 30 | ## Installing 31 | 32 | On Puppet 2.7.14+, the module can be installed easily ([documentation](http://docs.puppetlabs.com/puppet/latest/reference/modules_installing.html)): 33 | 34 | puppet module install puppet/augeasproviders_pam 35 | 36 | You may see an error similar to this on Puppet 2.x ([#13858](http://projects.puppetlabs.com/issues/13858)): 37 | 38 | Error 400 on SERVER: Puppet::Parser::AST::Resource failed with error ArgumentError: Invalid resource type `pam` at ... 39 | 40 | Ensure the module is present in your puppetmaster's own environment (it doesn't 41 | have to use it) and that the master has pluginsync enabled. Run the agent on 42 | the puppetmaster to cause the custom types to be synced to its local libdir 43 | (`puppet master --configprint libdir`) and then restart the puppetmaster so it 44 | loads them. 45 | 46 | ## Compatibility 47 | 48 | ### Puppet versions 49 | 50 | Minimum of Puppet 2.7. 51 | 52 | ### Augeas versions 53 | 54 | Augeas Versions | 0.10.0 | 1.0.0 | 1.1.0 | 1.2.0 | 55 | :-------------------------|:-------:|:-------:|:-------:|:-------:| 56 | **PROVIDERS** | 57 | pam | **yes** | **yes** | **yes** | **yes** | 58 | 59 | ## Documentation and examples 60 | 61 | Type documentation can be generated with `puppet doc -r type` or viewed on the 62 | [Puppet Forge page](https://forge.puppet.com/modules/puppet/augeasproviders_pam). 63 | 64 | 65 | ### manage simple entry 66 | 67 | pam { "Set sss entry to system-auth auth": 68 | ensure => present, 69 | service => 'system-auth', 70 | type => 'auth', 71 | control => 'sufficient', 72 | module => 'pam_sss.so', 73 | arguments => 'use_first_pass', 74 | position => 'before module pam_deny.so', 75 | } 76 | 77 | ### manage same entry but with Augeas xpath 78 | 79 | pam { "Set sss entry to system-auth auth": 80 | ensure => present, 81 | service => 'system-auth', 82 | type => 'auth', 83 | control => 'sufficient', 84 | module => 'pam_sss.so', 85 | arguments => 'use_first_pass', 86 | position => 'before *[type="auth" and module="pam_deny.so"]', 87 | } 88 | 89 | ### delete entry 90 | 91 | pam { "Remove sss auth entry from system-auth": 92 | ensure => absent, 93 | service => 'system-auth', 94 | type => 'auth', 95 | module => 'pam_sss.so', 96 | } 97 | 98 | ### delete all references to module in file 99 | 100 | pam { "Remove all pam_sss.so from system-auth": 101 | ensure => absent, 102 | service => 'system-auth', 103 | module => 'pam_sss.so', 104 | } 105 | 106 | ### manage entry in another pam service 107 | 108 | pam { "Set cracklib limits in password-auth": 109 | ensure => present, 110 | service => 'password-auth', 111 | type => 'password', 112 | module => 'pam_cracklib.so', 113 | arguments => ['try_first_pass','retry=3', 'minlen=10'], 114 | } 115 | 116 | ### manage entry like previous but in classic pam.conf 117 | 118 | pam { "Set cracklib limits in password-auth": 119 | ensure => present, 120 | service => 'password-auth', 121 | type => 'password', 122 | module => 'pam_cracklib.so', 123 | arguments => ['try_first_pass','retry=3', 'minlen=10'], 124 | target => '/etc/pam.conf', 125 | } 126 | 127 | ### allow multiple entries with same control value 128 | 129 | pam { "Set invalid login 3 times deny in password-auth -fail": 130 | ensure => present, 131 | service => 'password-auth', 132 | type => 'auth', 133 | control => '[default=die]', 134 | control_is_param => true, 135 | module => 'pam_faillock.so', 136 | arguments => ['authfail','deny=3','unlock_time=604800','fail_interval=900'], 137 | } 138 | 139 | ## Issues 140 | 141 | Please file any issues or suggestions [on GitHub](https://github.com/hercules-team/augeasproviders_pam/issues). 142 | -------------------------------------------------------------------------------- /spec/unit/puppet/provider/pam/augeas_spec.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env rspec 2 | # frozen_string_literal: true 3 | 4 | require 'spec_helper' 5 | 6 | provider_class = Puppet::Type.type(:pam).provider(:augeas) 7 | 8 | describe provider_class do 9 | before do 10 | allow(FileTest).to receive(:exist?).and_return(false) 11 | end 12 | 13 | context 'with empty file' do 14 | let(:tmptarget) { aug_fixture('empty') } 15 | let(:target) { tmptarget.path } 16 | 17 | it 'creates simple new entry' do 18 | apply!(Puppet::Type.type(:pam).new( 19 | title: 'Add pam_test.so to auth for system-auth', 20 | service: 'system-auth', 21 | type: 'auth', 22 | control: 'sufficient', 23 | module: 'pam_test.so', 24 | arguments: 'test_me_out', 25 | position: 'before module pam_deny.so', 26 | target: target, 27 | provider: 'augeas', 28 | ensure: 'present' 29 | )) 30 | 31 | aug_open(target, 'Pam.lns') do |aug| 32 | expect(aug.get('./1/module')).to eq('pam_test.so') 33 | expect(aug.get('./1/argument[1]')).to eq('test_me_out') 34 | end 35 | end 36 | 37 | it 'creates simple new entry without arguments' do 38 | apply!(Puppet::Type.type(:pam).new( 39 | title: 'Add pam_test.so to auth for system-auth', 40 | service: 'system-auth', 41 | type: 'auth', 42 | control: 'sufficient', 43 | module: 'pam_test.so', 44 | target: target, 45 | provider: 'augeas', 46 | ensure: 'present' 47 | )) 48 | 49 | aug_open(target, 'Pam.lns') do |aug| 50 | expect(aug.get('./1/module')).to eq('pam_test.so') 51 | expect(aug.match('./1/argument').size).to eq(0) 52 | end 53 | end 54 | 55 | it 'creates two new entries' do 56 | apply!(Puppet::Type.type(:pam).new( 57 | title: 'Add pam_test.so to auth for system-auth', 58 | service: 'system-auth', 59 | type: 'auth', 60 | control: 'sufficient', 61 | module: 'pam_test.so', 62 | arguments: 'test_me_out', 63 | target: target, 64 | provider: 'augeas', 65 | ensure: 'present' 66 | )) 67 | apply!(Puppet::Type.type(:pam).new( 68 | title: 'Add pam_test.so to auth for system-auth', 69 | service: 'system-auth', 70 | type: 'auth', 71 | control: 'required', 72 | module: 'pam_unix.so', 73 | arguments: 'broken_shadow', 74 | target: target, 75 | provider: 'augeas', 76 | ensure: 'present' 77 | )) 78 | 79 | aug_open(target, 'Pam.lns') do |aug| 80 | expect(aug.match("*[type='auth']").size).to eq(2) 81 | end 82 | end 83 | end 84 | 85 | context 'with full file' do 86 | let(:tmptarget) { aug_fixture('full') } 87 | let(:target) { tmptarget.path } 88 | 89 | it 'lists instances' do 90 | allow(provider_class).to receive(:target).and_return(target) 91 | inst = provider_class.instances.map do |p| 92 | { 93 | ensure: p.get(:ensure), 94 | service: p.get(:service), 95 | type: p.get(:type), 96 | control: p.get(:control), 97 | module: p.get(:module), 98 | arguments: p.get(:arguments), 99 | } 100 | end 101 | 102 | expect(inst.size).to eq(21) 103 | expect(inst[0]).to eq({ ensure: :present, 104 | service: :absent, 105 | type: 'auth', 106 | control: 'required', 107 | module: 'pam_env.so', 108 | arguments: [], }) 109 | expect(inst[1]).to eq({ ensure: :present, 110 | service: :absent, 111 | type: 'auth', 112 | control: 'sufficient', 113 | module: 'pam_unix.so', 114 | arguments: %w[nullok try_first_pass], }) 115 | expect(inst[5]).to eq({ ensure: :present, 116 | service: :absent, 117 | type: 'account', 118 | control: 'required', 119 | module: 'pam_unix.so', 120 | arguments: ['broken_shadow'], }) 121 | expect(inst[8]).to eq({ ensure: :present, 122 | service: :absent, 123 | type: 'account', 124 | control: '[default=bad success=ok user_unknown=ignore]', 125 | module: 'pam_sss.so', 126 | arguments: [], }) 127 | expect(inst[10]).to eq({ ensure: :present, 128 | service: :absent, 129 | type: 'password', 130 | control: 'requisite', 131 | module: 'pam_pwquality.so', 132 | arguments: ['try_first_pass', 'retry=3', 'type='], }) 133 | end 134 | 135 | describe 'when reodering settings' do 136 | it 'changes the order of an entry' do 137 | apply!(Puppet::Type.type(:pam).new( 138 | title: 'Change the order of pam_unix.so', 139 | service: 'system-auth', 140 | type: 'auth', 141 | control: 'sufficient', 142 | module: 'pam_unix.so', 143 | arguments: %w[nullok try_first_pass], 144 | target: target, 145 | provider: 'augeas', 146 | position: 'before module pam_env.so', 147 | ensure: 'positioned' 148 | )) 149 | 150 | aug_open(target, 'Pam.lns') do |aug| 151 | expect(aug.get('./1/module')).to eq('pam_unix.so') 152 | end 153 | end 154 | end 155 | 156 | describe 'when creating settings' do 157 | it 'creates simple new entry' do 158 | apply!(Puppet::Type.type(:pam).new( 159 | title: 'Add pam_test.so to auth for system-auth', 160 | service: 'system-auth', 161 | type: 'auth', 162 | control: 'sufficient', 163 | module: 'pam_test.so', 164 | arguments: 'test_me_out', 165 | position: 'before module pam_deny.so', 166 | target: target, 167 | provider: 'augeas', 168 | ensure: 'present' 169 | )) 170 | 171 | aug_open(target, 'Pam.lns') do |aug| 172 | expect(aug.get('./5/module')).to eq('pam_test.so') 173 | expect(aug.get('./5/argument[1]')).to eq('test_me_out') 174 | end 175 | end 176 | end 177 | 178 | describe 'when modifying settings' do 179 | it 'Changing the number of retries' do 180 | apply!(Puppet::Type.type(:pam).new( 181 | title: 'Set retry count for pwquality', 182 | service: 'system-auth', 183 | type: 'password', 184 | control: 'requisite', 185 | module: 'pam_pwquality.so', 186 | arguments: ['try_first_pass', 'retry=4', 'type='], 187 | target: target, 188 | provider: 'augeas', 189 | ensure: 'present' 190 | )) 191 | 192 | aug_open(target, 'Pam.lns') do |aug| 193 | expect(aug.match('./*[type="password" and module="pam_pwquality.so" and argument="retry=4"]').size).to eq(1) 194 | end 195 | end 196 | 197 | it 'removes the type= argument' do 198 | apply!(Puppet::Type.type(:pam).new( 199 | title: 'Remove type= from pwquality check', 200 | service: 'system-auth', 201 | type: 'password', 202 | control: 'requisite', 203 | module: 'pam_pwquality.so', 204 | arguments: ['try_first_pass', 'retry=4'], 205 | target: target, 206 | provider: 'augeas', 207 | ensure: 'present' 208 | )) 209 | 210 | aug_open(target, 'Pam.lns') do |aug| 211 | expect(aug.match('./*[type="password" and module="pam_pwquality.so" and argument="type="]').size).to eq(0) 212 | end 213 | end 214 | 215 | it 'changes the value of control' do 216 | apply!(Puppet::Type.type(:pam).new( 217 | title: 'Remove type= from pwquality check', 218 | service: 'system-auth', 219 | type: 'password', 220 | control: 'required', 221 | arguments: ['try_first_pass', 'retry=4'], 222 | module: 'pam_pwquality.so', 223 | target: target, 224 | provider: 'augeas', 225 | ensure: 'present' 226 | )) 227 | 228 | aug_open(target, 'Pam.lns') do |aug| 229 | expect(aug.get('./*[type="password" and module="pam_pwquality.so"]/control')).to eq('required') 230 | end 231 | end 232 | 233 | it 'adds a new entry when control_is_param is true' do 234 | apply!(Puppet::Type.type(:pam).new( 235 | title: 'Remove type= from pwquality check', 236 | service: 'system-auth', 237 | type: 'password', 238 | control: 'sufficient', 239 | control_is_param: true, 240 | arguments: ['try_first_pass', 'retry=4'], 241 | module: 'pam_pwquality.so', 242 | target: target, 243 | provider: 'augeas', 244 | ensure: 'present' 245 | )) 246 | 247 | aug_open(target, 'Pam.lns') do |aug| 248 | expect(aug.match('./*[type="password" and module="pam_pwquality.so"]/control').size).to eq(2) 249 | expect(aug.get('./*[type="password" and module="pam_pwquality.so"][1]/control')).to eq('requisite') 250 | expect(aug.get('./*[type="password" and module="pam_pwquality.so"][2]/control')).to eq('sufficient') 251 | end 252 | end 253 | 254 | it 'updates entry when control_is_param is true' do 255 | apply!(Puppet::Type.type(:pam).new( 256 | title: 'Remove type= from pwquality check', 257 | service: 'system-auth', 258 | type: 'password', 259 | control: 'requisite', 260 | control_is_param: true, 261 | arguments: ['try_first_pass', 'retry=4'], 262 | module: 'pam_pwquality.so', 263 | target: target, 264 | provider: 'augeas', 265 | ensure: 'present' 266 | )) 267 | 268 | aug_open(target, 'Pam.lns') do |aug| 269 | expect(aug.match('./*[type="password" and module="pam_pwquality.so"]/control').size).to eq(1) 270 | expect(aug.get('./*[type="password" and module="pam_pwquality.so"]/control')).to eq('requisite') 271 | end 272 | end 273 | end 274 | 275 | describe 'when removing settings' do 276 | it 'removes the entry' do 277 | apply!(Puppet::Type.type(:pam).new( 278 | title: 'Remove pwquality entry', 279 | service: 'system-auth', 280 | type: 'password', 281 | control: 'requisite', 282 | module: 'pam_pwquality.so', 283 | arguments: ['try_first_pass', 'retry=4'], 284 | target: target, 285 | provider: 'augeas', 286 | ensure: 'absent' 287 | )) 288 | 289 | aug_open(target, 'Pam.lns') do |aug| 290 | expect(aug.match('./*[type="password" and module="pam_pwquality.so"]').size).to eq(0) 291 | end 292 | end 293 | end 294 | end 295 | 296 | context 'with broken file' do 297 | let(:tmptarget) { aug_fixture('broken') } 298 | let(:target) { tmptarget.path } 299 | 300 | it 'fails to load' do 301 | txn = apply(Puppet::Type.type(:pam).new( 302 | title: 'Ensure pwquality is configured', 303 | service: 'system-auth', 304 | type: 'password', 305 | control: 'requisite', 306 | module: 'pam_pwquality.so', 307 | arguments: ['try_first_pass', 'retry=3', 'type='], 308 | target: target, 309 | provider: 'augeas', 310 | ensure: 'present' 311 | )) 312 | 313 | expect(txn.any_failed?).not_to eq(nil) 314 | expect(@logs.first.level).to eq(:err) # rubocop:todo RSpec/InstanceVariable 315 | expect(@logs.first.message.include?(target)).to eq(true) # rubocop:todo RSpec/InstanceVariable 316 | end 317 | end 318 | 319 | context 'with Ubuntu 24.04 configuration' do 320 | let(:tmptarget) { aug_fixture('ubuntu2404') } 321 | let(:target) { tmptarget.path } 322 | 323 | it 'lists instances correctly for Ubuntu 24.04 PAM format' do 324 | allow(provider_class).to receive(:target).and_return(target) 325 | inst = provider_class.instances.map do |p| 326 | { 327 | ensure: p.get(:ensure), 328 | service: p.get(:service), 329 | type: p.get(:type), 330 | control: p.get(:control), 331 | module: p.get(:module), 332 | arguments: p.get(:arguments), 333 | } 334 | end 335 | 336 | expect(inst.size).to eq(16) 337 | # Test first auth entry 338 | expect(inst[0]).to eq({ ensure: :present, 339 | service: :absent, 340 | type: 'auth', 341 | control: '[success=1 default=ignore]', 342 | module: 'pam_unix.so', 343 | arguments: ['nullok'], }) 344 | # Test password entry with yescrypt at index 7 345 | expect(inst[7]).to eq({ ensure: :present, 346 | service: :absent, 347 | type: 'password', 348 | control: '[success=1 default=ignore]', 349 | module: 'pam_unix.so', 350 | arguments: %w[obscure use_authtok try_first_pass yescrypt], }) 351 | # Test systemd session entry 352 | expect(inst[15]).to eq({ ensure: :present, 353 | service: :absent, 354 | type: 'session', 355 | control: 'optional', 356 | module: 'pam_systemd.so', 357 | arguments: [], }) 358 | end 359 | 360 | it 'can modify Ubuntu 24.04 style PAM entries' do 361 | apply!(Puppet::Type.type(:pam).new( 362 | title: 'Configure pam_faillock for Ubuntu 24.04', 363 | service: 'common-auth', 364 | type: 'auth', 365 | control: 'required', 366 | module: 'pam_faillock.so', 367 | arguments: ['preauth', 'silent', 'audit', 'deny=5', 'unlock_time=900'], 368 | position: 'before module pam_unix.so', 369 | target: target, 370 | provider: 'augeas', 371 | ensure: 'present' 372 | )) 373 | 374 | aug_open(target, 'Pam.lns') do |aug| 375 | expect(aug.get('./1/module')).to eq('pam_faillock.so') 376 | expect(aug.get('./1/control')).to eq('required') 377 | expect(aug.get('./1/argument[1]')).to eq('preauth') 378 | expect(aug.get('./1/argument[4]')).to eq('deny=5') 379 | end 380 | end 381 | 382 | it 'can add new PAM module to Ubuntu 24.04 configuration' do 383 | # Add a completely new module that doesn't exist 384 | apply!(Puppet::Type.type(:pam).new( 385 | title: 'Add pam_faillock for Ubuntu 24.04', 386 | service: 'common-auth', 387 | type: 'auth', 388 | control: 'required', 389 | module: 'pam_faillock.so', 390 | arguments: ['preauth', 'silent', 'audit', 'deny=5'], 391 | position: 'before module pam_deny.so', 392 | target: target, 393 | provider: 'augeas', 394 | ensure: 'present' 395 | )) 396 | 397 | aug_open(target, 'Pam.lns') do |aug| 398 | # Check that pam_faillock.so was added 399 | faillock_entries = aug.match('./*[type="auth" and module="pam_faillock.so"]') 400 | expect(faillock_entries.size).to eq(1) 401 | # Verify the arguments 402 | expect(aug.get("#{faillock_entries.first}/argument[1]")).to eq('preauth') 403 | expect(aug.get("#{faillock_entries.first}/argument[4]")).to eq('deny=5') 404 | end 405 | end 406 | 407 | it 'handles yescrypt password hashing correctly' do 408 | apply!(Puppet::Type.type(:pam).new( 409 | title: 'Update password hashing to yescrypt', 410 | service: 'common-password', 411 | type: 'password', 412 | control: '[success=1 default=ignore]', 413 | module: 'pam_unix.so', 414 | arguments: %w[obscure use_authtok try_first_pass yescrypt rounds=5], 415 | target: target, 416 | provider: 'augeas', 417 | ensure: 'present' 418 | )) 419 | 420 | aug_open(target, 'Pam.lns') do |aug| 421 | password_entry = aug.match('./*[type="password" and module="pam_unix.so"]').first 422 | expect(aug.get("#{password_entry}/argument[5]")).to eq('rounds=5') 423 | expect(aug.match("#{password_entry}/argument[.='yescrypt']").size).to eq(1) 424 | end 425 | end 426 | end 427 | end 428 | --------------------------------------------------------------------------------