puppetserver-ca 2.1.0 → 2.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +12 -3
- data/lib/puppetserver/ca/action/list.rb +75 -19
- data/lib/puppetserver/ca/action/prune.rb +131 -0
- data/lib/puppetserver/ca/certificate_authority.rb +9 -9
- data/lib/puppetserver/ca/cli.rb +14 -7
- data/lib/puppetserver/ca/logger.rb +9 -1
- data/lib/puppetserver/ca/utils/http_client.rb +20 -8
- data/lib/puppetserver/ca/version.rb +1 -1
- metadata +3 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: ffcdb4b7a4972842dd5f3cc03d3879998e1ab8fcba4066d49e919a1ba6c7312c
         | 
| 4 | 
            +
              data.tar.gz: d32629c393a75fa5f6291e97bc84a4620738ef97804f569e031a4f975ac7b059
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: aa99515bb8c32de7529d63bc4242bc4ef71ea8ba0c3f00137fa31cdec0a08e8b6da13d9daaaefd922e50abdbe287ced9ec2395802d4012754a4086c804d27907
         | 
| 7 | 
            +
              data.tar.gz: '059760fa95029609e65f15726944c342053f66ac092cafb2f073896d3e143caf7f7a3029642749001b3fb6a289cf78ffe26dfb56a33399c60d268a1b303609d3'
         | 
    
        data/README.md
    CHANGED
    
    | @@ -55,6 +55,16 @@ To create a new keypair and certificate for a certname: | |
| 55 55 | 
             
            puppetserver ca generate --certname foo.example.com
         | 
| 56 56 | 
             
            ```
         | 
| 57 57 |  | 
| 58 | 
            +
            To remove duplicated entries from Puppet's CRL:
         | 
| 59 | 
            +
            ```
         | 
| 60 | 
            +
            puppetserver ca prune
         | 
| 61 | 
            +
            ```
         | 
| 62 | 
            +
             | 
| 63 | 
            +
            To enable verbose mode:
         | 
| 64 | 
            +
            ```
         | 
| 65 | 
            +
            puppetserver ca --verbose <action>
         | 
| 66 | 
            +
            ```
         | 
| 67 | 
            +
             | 
| 58 68 | 
             
            For more details, see the help output:
         | 
| 59 69 | 
             
            ```
         | 
| 60 70 | 
             
            puppetserver ca --help
         | 
| @@ -68,7 +78,7 @@ for more details. | |
| 68 78 | 
             
            ## Development
         | 
| 69 79 |  | 
| 70 80 | 
             
            After checking out the repo, run `bin/setup` to install dependencies. Then,
         | 
| 71 | 
            -
            run `rake spec` to run the tests. You can also run `bin/console` for an
         | 
| 81 | 
            +
            run `bundle exec rake spec` to run the tests. You can also run `bin/console` for an
         | 
| 72 82 | 
             
            interactive prompt that will allow you to experiment.
         | 
| 73 83 |  | 
| 74 84 | 
             
            To install this gem onto your local machine, run `bundle exec rake install`.
         | 
| @@ -92,8 +102,7 @@ To test your changes on a VM: | |
| 92 102 | 
             
            1. To confirm that installation was successful, run `puppetserver ca --help`
         | 
| 93 103 |  | 
| 94 104 | 
             
            ### Releasing
         | 
| 95 | 
            -
            To release a new version, run the [release pipeline](https://jenkins- | 
| 96 | 
            -
             | 
| 105 | 
            +
            To release a new version, run the [release pipeline](https://jenkins-platform.delivery.puppetlabs.net/job/platform_puppetserver-ca_init-multijob_main/), which will bump the version, tag, build, and release the gem.
         | 
| 97 106 |  | 
| 98 107 | 
             
            ## Contributing & Support
         | 
| 99 108 |  | 
| @@ -30,6 +30,7 @@ Options: | |
| 30 30 | 
             
                  BANNER
         | 
| 31 31 |  | 
| 32 32 | 
             
                    BODY = JSON.dump({desired_state: 'signed'})
         | 
| 33 | 
            +
                    VALID_FORMAT = ['text', 'json']
         | 
| 33 34 |  | 
| 34 35 | 
             
                    def initialize(logger)
         | 
| 35 36 | 
             
                      @logger = logger
         | 
| @@ -47,6 +48,9 @@ Options: | |
| 47 48 | 
             
                        opts.on('--all', 'List all certificates') do |a|
         | 
| 48 49 | 
             
                          parsed['all'] = true
         | 
| 49 50 | 
             
                        end
         | 
| 51 | 
            +
                        opts.on('--format FORMAT', "Valid formats are: 'text' (default), 'json'") do |f|
         | 
| 52 | 
            +
                          parsed['format'] = f
         | 
| 53 | 
            +
                        end
         | 
| 50 54 | 
             
                        opts.on('--certname NAME[,NAME]', Array, 'List the specified cert(s)') do |cert|
         | 
| 51 55 | 
             
                          parsed['certname'] = cert
         | 
| 52 56 | 
             
                        end
         | 
| @@ -57,9 +61,16 @@ Options: | |
| 57 61 | 
             
                      config = input['config']
         | 
| 58 62 | 
             
                      certnames = input['certname'] || []
         | 
| 59 63 | 
             
                      all = input['all']
         | 
| 64 | 
            +
                      output_format = input['format'] || "text"
         | 
| 65 | 
            +
                      missing = []
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                      unless VALID_FORMAT.include?(output_format)
         | 
| 68 | 
            +
                        Errors.handle_with_usage(@logger, ["Unknown format flag '#{output_format}'. Valid formats are '#{VALID_FORMAT.join("', '")}'."])
         | 
| 69 | 
            +
                        return 1
         | 
| 70 | 
            +
                      end
         | 
| 60 71 |  | 
| 61 72 | 
             
                      if all && certnames.any?
         | 
| 62 | 
            -
                        Errors.handle_with_usage(@logger, ['Cannot combine use of --all and --certname'])
         | 
| 73 | 
            +
                        Errors.handle_with_usage(@logger, ['Cannot combine use of --all and --certname.'])
         | 
| 63 74 | 
             
                        return 1
         | 
| 64 75 | 
             
                      end
         | 
| 65 76 |  | 
| @@ -71,24 +82,60 @@ Options: | |
| 71 82 | 
             
                      puppet = Config::Puppet.parse(config, @logger)
         | 
| 72 83 | 
             
                      return 1 if Errors.handle_with_usage(@logger, puppet.errors)
         | 
| 73 84 |  | 
| 74 | 
            -
                       | 
| 75 | 
            -
                         | 
| 76 | 
            -
             | 
| 85 | 
            +
                      if certnames.any?
         | 
| 86 | 
            +
                        filter_names = lambda { |x| certnames.include?(x['name']) }
         | 
| 87 | 
            +
                      else
         | 
| 88 | 
            +
                        filter_names = lambda { |x| true }
         | 
| 89 | 
            +
                      end
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                      if (all || certnames.any?)
         | 
| 92 | 
            +
                        all_certs = get_certs_or_csrs(puppet.settings).select { |cert| filter_names.call(cert) }
         | 
| 93 | 
            +
                        requested, signed, revoked = separate_certs(all_certs)
         | 
| 94 | 
            +
                        missing = certnames - all_certs.map { |cert| cert['name'] }
         | 
| 95 | 
            +
                        output_certs_by_state(all, output_format, requested, signed, revoked, missing)
         | 
| 96 | 
            +
                      else
         | 
| 97 | 
            +
                        all_csrs = get_certs_or_csrs(puppet.settings, "requested")
         | 
| 98 | 
            +
                        output_certs_by_state(all, output_format, all_csrs)
         | 
| 99 | 
            +
                      end
         | 
| 77 100 |  | 
| 78 | 
            -
                       | 
| 79 | 
            -
             | 
| 80 | 
            -
                      missing = certnames - all_certs.map { |cert| cert['name'] }
         | 
| 101 | 
            +
                      return missing.any? ? 1 : 0
         | 
| 102 | 
            +
                    end
         | 
| 81 103 |  | 
| 82 | 
            -
             | 
| 83 | 
            -
             | 
| 84 | 
            -
                         | 
| 104 | 
            +
                    def output_certs_by_state(all, output_format, requested, signed = [], revoked = [], missing = [])
         | 
| 105 | 
            +
                      if output_format == 'json'
         | 
| 106 | 
            +
                        output_certs_json_format(all, requested, signed, revoked, missing)
         | 
| 107 | 
            +
                      else
         | 
| 108 | 
            +
                        output_certs_text_format(requested, signed, revoked, missing)
         | 
| 109 | 
            +
                      end
         | 
| 110 | 
            +
                    end
         | 
| 85 111 |  | 
| 86 | 
            -
             | 
| 87 | 
            -
             | 
| 88 | 
            -
             | 
| 112 | 
            +
                    def output_certs_json_format(all, requested, signed, revoked, missing)
         | 
| 113 | 
            +
                      grouped_cert = {}
         | 
| 114 | 
            +
             | 
| 115 | 
            +
                      if all
         | 
| 116 | 
            +
                        grouped_cert = { "requested" => requested,
         | 
| 117 | 
            +
                                         "signed" => signed,
         | 
| 118 | 
            +
                                         "revoked" => revoked }.to_json
         | 
| 119 | 
            +
                        @logger.inform(grouped_cert)
         | 
| 120 | 
            +
                      else
         | 
| 121 | 
            +
                        grouped_cert["requested"] = requested unless requested.empty?
         | 
| 122 | 
            +
                        grouped_cert["signed"] = signed unless signed.empty?
         | 
| 123 | 
            +
                        grouped_cert["revoked"] = revoked unless revoked.empty?
         | 
| 124 | 
            +
                        grouped_cert["missing"] = missing unless missing.empty?
         | 
| 125 | 
            +
             | 
| 126 | 
            +
                        # If neither the '--all' flag or the '--certname' flag was passed in
         | 
| 127 | 
            +
                        # and the requested cert array is empty, we output a JSON object
         | 
| 128 | 
            +
                        # with an empty 'requested' key. Otherwise, we display
         | 
| 129 | 
            +
                        # any of the classes that are currently in grouped_cert
         | 
| 130 | 
            +
                        if grouped_cert.empty?
         | 
| 131 | 
            +
                          @logger.inform({ "requested" => requested }.to_json)
         | 
| 132 | 
            +
                        else
         | 
| 133 | 
            +
                          @logger.inform(grouped_cert.to_json)
         | 
| 134 | 
            +
                        end
         | 
| 135 | 
            +
                      end
         | 
| 89 136 | 
             
                    end
         | 
| 90 137 |  | 
| 91 | 
            -
                    def  | 
| 138 | 
            +
                    def output_certs_text_format(requested, signed, revoked, missing)
         | 
| 92 139 | 
             
                      if revoked.empty? && signed.empty? && requested.empty? && missing.empty?
         | 
| 93 140 | 
             
                        @logger.inform "No certificates to list"
         | 
| 94 141 | 
             
                        return
         | 
| @@ -163,9 +210,15 @@ Options: | |
| 163 210 | 
             
                      return requested, signed, revoked
         | 
| 164 211 | 
             
                    end
         | 
| 165 212 |  | 
| 166 | 
            -
                    def  | 
| 167 | 
            -
                       | 
| 168 | 
            -
                      result  | 
| 213 | 
            +
                    def get_certs_or_csrs(settings, queried_state = nil)
         | 
| 214 | 
            +
                      query = queried_state ? { :state => queried_state } : {}
         | 
| 215 | 
            +
                      result = Puppetserver::Ca::CertificateAuthority.new(@logger, settings).get_certificate_statuses(query)
         | 
| 216 | 
            +
             | 
| 217 | 
            +
                      if result
         | 
| 218 | 
            +
                        return JSON.parse(result.body)
         | 
| 219 | 
            +
                      else
         | 
| 220 | 
            +
                        return []
         | 
| 221 | 
            +
                      end
         | 
| 169 222 | 
             
                    end
         | 
| 170 223 |  | 
| 171 224 | 
             
                    def parse(args)
         | 
| @@ -176,8 +229,11 @@ Options: | |
| 176 229 |  | 
| 177 230 | 
             
                      errors_were_handled = Errors.handle_with_usage(@logger, errors, parser.help)
         | 
| 178 231 |  | 
| 179 | 
            -
                       | 
| 180 | 
            -
             | 
| 232 | 
            +
                      if errors_were_handled
         | 
| 233 | 
            +
                        exit_code = 1
         | 
| 234 | 
            +
                      else
         | 
| 235 | 
            +
                        exit_code = nil
         | 
| 236 | 
            +
                      end
         | 
| 181 237 | 
             
                      return results, exit_code
         | 
| 182 238 | 
             
                    end
         | 
| 183 239 | 
             
                  end
         | 
| @@ -0,0 +1,131 @@ | |
| 1 | 
            +
            require 'optparse'
         | 
| 2 | 
            +
            require 'openssl'
         | 
| 3 | 
            +
            require 'puppetserver/ca/errors'
         | 
| 4 | 
            +
            require 'puppetserver/ca/utils/cli_parsing'
         | 
| 5 | 
            +
            require 'puppetserver/ca/utils/file_system'
         | 
| 6 | 
            +
            require 'puppetserver/ca/utils/config'
         | 
| 7 | 
            +
            require 'puppetserver/ca/x509_loader'
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            module Puppetserver
         | 
| 10 | 
            +
              module Ca
         | 
| 11 | 
            +
                module Action
         | 
| 12 | 
            +
                  class Prune
         | 
| 13 | 
            +
                    include Puppetserver::Ca::Utils
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                    SUMMARY = "Prune the local CRL on disk to remove any duplicated certificates"
         | 
| 16 | 
            +
                    BANNER = <<-BANNER
         | 
| 17 | 
            +
            Usage:
         | 
| 18 | 
            +
              puppetserver ca prune [--help]
         | 
| 19 | 
            +
              puppetserver ca prune [--config]
         | 
| 20 | 
            +
             | 
| 21 | 
            +
            Description:
         | 
| 22 | 
            +
              Prune the list of revoked certificates of any duplication within it.  This command
         | 
| 23 | 
            +
              will only prune the CRL issued by Puppet's CA cert.
         | 
| 24 | 
            +
             | 
| 25 | 
            +
            Options:
         | 
| 26 | 
            +
            BANNER
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                    def initialize(logger)
         | 
| 29 | 
            +
                      @logger = logger
         | 
| 30 | 
            +
                    end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                    def run(inputs)
         | 
| 33 | 
            +
                      config_path = inputs['config']
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                      # Validate the config path.
         | 
| 36 | 
            +
                      if config_path
         | 
| 37 | 
            +
                        errors = FileSystem.validate_file_paths(config_path)
         | 
| 38 | 
            +
                        return 1 if Errors.handle_with_usage(@logger, errors)
         | 
| 39 | 
            +
                      end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                      # Validate puppet config setting.
         | 
| 42 | 
            +
                      puppet = Config::Puppet.new(config_path)
         | 
| 43 | 
            +
                      puppet.load(logger: @logger)
         | 
| 44 | 
            +
                      return 1 if Errors.handle_with_usage(@logger, puppet.errors)
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                      # Validate that we are offline
         | 
| 47 | 
            +
                      return 1 if HttpClient.check_server_online(puppet.settings, @logger)
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                      # Getting the CRL(s)
         | 
| 50 | 
            +
                      loader = X509Loader.new(puppet.settings[:cacert], puppet.settings[:cakey], puppet.settings[:cacrl])
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                      puppet_crl = loader.crls.select { |crl| crl.verify(loader.key) }
         | 
| 53 | 
            +
                      number_of_removed_duplicates = prune_CRLs(puppet_crl)
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                      if number_of_removed_duplicates > 0
         | 
| 56 | 
            +
                        update_pruned_CRL(puppet_crl, loader.key)
         | 
| 57 | 
            +
                        FileSystem.write_file(puppet.settings[:cacrl], loader.crls, 0644)
         | 
| 58 | 
            +
                        @logger.inform("Removed #{number_of_removed_duplicates} duplicated certs from Puppet's CRL.")
         | 
| 59 | 
            +
                      else
         | 
| 60 | 
            +
                        @logger.inform("No duplicate revocations found in the CRL.")
         | 
| 61 | 
            +
                      end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                      return 0
         | 
| 64 | 
            +
                    end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                    def prune_CRLs(crl_list)
         | 
| 67 | 
            +
                      number_of_removed_duplicates = 0
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                      crl_list.each do |crl|
         | 
| 70 | 
            +
                        existed_serial_number = Set.new()
         | 
| 71 | 
            +
                        revoked_list = crl.revoked
         | 
| 72 | 
            +
                        @logger.debug("Pruning duplicate entries in CRL for issuer " \
         | 
| 73 | 
            +
                          "#{crl.issuer.to_s(OpenSSL::X509::Name::RFC2253)}") if @logger.debug?
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                        revoked_list.delete_if do |revoked|
         | 
| 76 | 
            +
                          if existed_serial_number.add?(revoked.serial)
         | 
| 77 | 
            +
                            false
         | 
| 78 | 
            +
                          else
         | 
| 79 | 
            +
                            number_of_removed_duplicates += 1
         | 
| 80 | 
            +
                            @logger.debug("Removing duplicate of #{revoked.serial}, " \
         | 
| 81 | 
            +
                              "revoked on #{revoked.time}\n") if @logger.debug?
         | 
| 82 | 
            +
                            true
         | 
| 83 | 
            +
                          end
         | 
| 84 | 
            +
                        end
         | 
| 85 | 
            +
                        crl.revoked=(revoked_list)
         | 
| 86 | 
            +
                      end
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                      return number_of_removed_duplicates
         | 
| 89 | 
            +
                    end
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                    def update_pruned_CRL(crl_list, pkey)
         | 
| 92 | 
            +
                      crl_list.each do |crl|
         | 
| 93 | 
            +
                        number_ext, other_ext = crl.extensions.partition{ |ext| ext.oid == "crlNumber" }
         | 
| 94 | 
            +
                        number_ext.each do |crl_number|
         | 
| 95 | 
            +
                          updated_crl_number = OpenSSL::BN.new(crl_number.value) + OpenSSL::BN.new(1)
         | 
| 96 | 
            +
                          crl_number.value=(OpenSSL::ASN1::Integer(updated_crl_number))
         | 
| 97 | 
            +
                        end
         | 
| 98 | 
            +
                        crl.extensions=(number_ext + other_ext)
         | 
| 99 | 
            +
                        crl.sign(pkey, OpenSSL::Digest::SHA256.new)
         | 
| 100 | 
            +
                      end
         | 
| 101 | 
            +
                    end
         | 
| 102 | 
            +
             | 
| 103 | 
            +
                    def self.parser(parsed = {})
         | 
| 104 | 
            +
                      OptionParser.new do |opts|
         | 
| 105 | 
            +
                        opts.banner = BANNER
         | 
| 106 | 
            +
                        opts.on('--help', 'Display this command-specific help output') do |help|
         | 
| 107 | 
            +
                          parsed['help'] = true
         | 
| 108 | 
            +
                        end
         | 
| 109 | 
            +
                        opts.on('--config CONF', 'Path to the puppet.conf file on disk') do |conf|
         | 
| 110 | 
            +
                          parsed['config'] = conf
         | 
| 111 | 
            +
                        end
         | 
| 112 | 
            +
                      end
         | 
| 113 | 
            +
                    end
         | 
| 114 | 
            +
             | 
| 115 | 
            +
                    def parse(args)
         | 
| 116 | 
            +
                      results = {}
         | 
| 117 | 
            +
                      parser = self.class.parser(results)
         | 
| 118 | 
            +
                      errors = CliParsing.parse_with_errors(parser, args)
         | 
| 119 | 
            +
                      errors_were_handled = Errors.handle_with_usage(@logger, errors, parser.help)
         | 
| 120 | 
            +
             | 
| 121 | 
            +
                      if errors_were_handled
         | 
| 122 | 
            +
                        exit_code = 1
         | 
| 123 | 
            +
                      else
         | 
| 124 | 
            +
                        exit_code = nil
         | 
| 125 | 
            +
                      end
         | 
| 126 | 
            +
                      return results, exit_code
         | 
| 127 | 
            +
                    end
         | 
| 128 | 
            +
                  end
         | 
| 129 | 
            +
                end
         | 
| 130 | 
            +
              end
         | 
| 131 | 
            +
            end
         | 
| @@ -23,7 +23,7 @@ module Puppetserver | |
| 23 23 |  | 
| 24 24 | 
             
                  def initialize(logger, settings)
         | 
| 25 25 | 
             
                    @logger = logger
         | 
| 26 | 
            -
                    @client = HttpClient.new(settings)
         | 
| 26 | 
            +
                    @client = HttpClient.new(@logger, settings)
         | 
| 27 27 | 
             
                    @ca_server = settings[:ca_server]
         | 
| 28 28 | 
             
                    @ca_port = settings[:ca_port]
         | 
| 29 29 | 
             
                  end
         | 
| @@ -41,8 +41,8 @@ module Puppetserver | |
| 41 41 | 
             
                  end
         | 
| 42 42 |  | 
| 43 43 | 
             
                  # Returns a URI-like wrapper around CA specific urls
         | 
| 44 | 
            -
                  def make_ca_url(resource_type = nil, certname = nil)
         | 
| 45 | 
            -
                    HttpClient::URL.new('https', @ca_server, @ca_port, 'puppet-ca', 'v1', resource_type, certname)
         | 
| 44 | 
            +
                  def make_ca_url(resource_type = nil, certname = nil, query = {})
         | 
| 45 | 
            +
                    HttpClient::URL.new('https', @ca_server, @ca_port, 'puppet-ca', 'v1', resource_type, certname, query)
         | 
| 46 46 | 
             
                  end
         | 
| 47 47 |  | 
| 48 48 | 
             
                  def process_ttl_input(ttl)
         | 
| @@ -141,7 +141,7 @@ module Puppetserver | |
| 141 141 | 
             
                    when :revoke
         | 
| 142 142 | 
             
                      case result.code
         | 
| 143 143 | 
             
                      when '200', '204'
         | 
| 144 | 
            -
                        @logger.inform " | 
| 144 | 
            +
                        @logger.inform "Certificate for #{certname} has been revoked"
         | 
| 145 145 | 
             
                        return :success
         | 
| 146 146 | 
             
                      when '404'
         | 
| 147 147 | 
             
                        @logger.err 'Error:'
         | 
| @@ -215,7 +215,7 @@ module Puppetserver | |
| 215 215 | 
             
                  def check_revocation(certname, result)
         | 
| 216 216 | 
             
                    case result.code
         | 
| 217 217 | 
             
                    when '200', '204'
         | 
| 218 | 
            -
                      @logger.inform " | 
| 218 | 
            +
                      @logger.inform "Certificate for #{certname} has been revoked"
         | 
| 219 219 | 
             
                      return :success
         | 
| 220 220 | 
             
                    when '409'
         | 
| 221 221 | 
             
                      return :invalid
         | 
| @@ -250,8 +250,8 @@ module Puppetserver | |
| 250 250 | 
             
                  end
         | 
| 251 251 |  | 
| 252 252 | 
             
                  # Returns nil for errors, else the result of the GET request
         | 
| 253 | 
            -
                  def get_certificate_statuses
         | 
| 254 | 
            -
                    result = get('certificate_statuses', 'any_key')
         | 
| 253 | 
            +
                  def get_certificate_statuses(query = {})
         | 
| 254 | 
            +
                    result = get('certificate_statuses', 'any_key', query)
         | 
| 255 255 |  | 
| 256 256 | 
             
                    unless result.code == '200'
         | 
| 257 257 | 
             
                      @logger.err 'Error:'
         | 
| @@ -287,8 +287,8 @@ module Puppetserver | |
| 287 287 | 
             
                  # @param resource_type [String] the resource type of url
         | 
| 288 288 | 
             
                  # @param resource_name [String] the resource name of url
         | 
| 289 289 | 
             
                  # @return [Struct] an instance of the Result struct with :code, :body
         | 
| 290 | 
            -
                  def get(resource_type, resource_name)
         | 
| 291 | 
            -
                    url = make_ca_url(resource_type, resource_name)
         | 
| 290 | 
            +
                  def get(resource_type, resource_name, query = {})
         | 
| 291 | 
            +
                    url = make_ca_url(resource_type, resource_name, query)
         | 
| 292 292 | 
             
                    @client.with_connection(url) do |connection|
         | 
| 293 293 | 
             
                      connection.get(url)
         | 
| 294 294 | 
             
                    end
         | 
    
        data/lib/puppetserver/ca/cli.rb
    CHANGED
    
    | @@ -8,6 +8,7 @@ require 'puppetserver/ca/action/list' | |
| 8 8 | 
             
            require 'puppetserver/ca/action/revoke'
         | 
| 9 9 | 
             
            require 'puppetserver/ca/action/setup'
         | 
| 10 10 | 
             
            require 'puppetserver/ca/action/sign'
         | 
| 11 | 
            +
            require 'puppetserver/ca/action/prune'
         | 
| 11 12 | 
             
            require 'puppetserver/ca/action/migrate'
         | 
| 12 13 | 
             
            require 'puppetserver/ca/errors'
         | 
| 13 14 | 
             
            require 'puppetserver/ca/logger'
         | 
| @@ -25,11 +26,12 @@ Manage the Private Key Infrastructure for | |
| 25 26 | 
             
            Puppet Server's built-in Certificate Authority
         | 
| 26 27 | 
             
            BANNER
         | 
| 27 28 |  | 
| 28 | 
            -
                   | 
| 29 | 
            +
                  ADMIN_ACTIONS = {
         | 
| 29 30 | 
             
                    'import'   => Action::Import,
         | 
| 30 31 | 
             
                    'setup'    => Action::Setup,
         | 
| 31 | 
            -
                    'enable' | 
| 32 | 
            -
                    'migrate' | 
| 32 | 
            +
                    'enable'   => Action::Enable,
         | 
| 33 | 
            +
                    'migrate'  => Action::Migrate,
         | 
| 34 | 
            +
                    'prune'    => Action::Prune
         | 
| 33 35 | 
             
                  }
         | 
| 34 36 |  | 
| 35 37 | 
             
                  MAINT_ACTIONS = {
         | 
| @@ -40,15 +42,15 @@ BANNER | |
| 40 42 | 
             
                    'sign'     => Action::Sign
         | 
| 41 43 | 
             
                  }
         | 
| 42 44 |  | 
| 43 | 
            -
                  VALID_ACTIONS =  | 
| 45 | 
            +
                  VALID_ACTIONS = ADMIN_ACTIONS.merge(MAINT_ACTIONS).sort.to_h
         | 
| 44 46 |  | 
| 45 47 | 
             
                  ACTION_LIST = "\nAvailable Actions:\n\n" +
         | 
| 46 48 | 
             
                    "  Certificate Actions (requires a running Puppet Server):\n\n" +
         | 
| 47 49 | 
             
                    MAINT_ACTIONS.map do |action, cls|
         | 
| 48 50 | 
             
                      "    #{action}\t#{cls::SUMMARY}"
         | 
| 49 51 | 
             
                    end.join("\n") + "\n\n" +
         | 
| 50 | 
            -
                    "   | 
| 51 | 
            -
                     | 
| 52 | 
            +
                    "  Administrative Actions (requires Puppet Server to be stopped):\n\n" +
         | 
| 53 | 
            +
                    ADMIN_ACTIONS.map do |action, cls|
         | 
| 52 54 | 
             
                      "    #{action}\t#{cls::SUMMARY}"
         | 
| 53 55 | 
             
                    end.join("\n")
         | 
| 54 56 |  | 
| @@ -64,8 +66,10 @@ BANNER | |
| 64 66 |  | 
| 65 67 |  | 
| 66 68 | 
             
                  def self.run(cli_args = ARGV, out = STDOUT, err = STDERR)
         | 
| 67 | 
            -
                    logger = Puppetserver::Ca::Logger.new(:info, out, err)
         | 
| 68 69 | 
             
                    parser, general_options, unparsed = parse_general_inputs(cli_args)
         | 
| 70 | 
            +
                    level = general_options.delete('verbose') ? :debug : :info
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                    logger = Puppetserver::Ca::Logger.new(level, out, err)
         | 
| 69 73 |  | 
| 70 74 | 
             
                    if general_options['version']
         | 
| 71 75 | 
             
                      logger.inform Puppetserver::Ca::VERSION
         | 
| @@ -121,6 +125,9 @@ BANNER | |
| 121 125 | 
             
                      opts.on('--version', 'Display the version') do |v|
         | 
| 122 126 | 
             
                        parsed['version'] = true
         | 
| 123 127 | 
             
                      end
         | 
| 128 | 
            +
                      opts.on('--verbose', 'Display low-level information') do |verbose|
         | 
| 129 | 
            +
                        parsed['verbose'] = true
         | 
| 130 | 
            +
                      end
         | 
| 124 131 |  | 
| 125 132 | 
             
                      opts.separator ACTION_OPTIONS
         | 
| 126 133 | 
             
                      opts.separator "\nSee `puppetserver ca <action> --help` for detailed info"
         | 
| @@ -1,5 +1,6 @@ | |
| 1 1 | 
             
            require 'net/https'
         | 
| 2 2 | 
             
            require 'openssl'
         | 
| 3 | 
            +
            require 'uri'
         | 
| 3 4 |  | 
| 4 5 | 
             
            require 'puppetserver/ca/errors'
         | 
| 5 6 |  | 
| @@ -19,7 +20,8 @@ module Puppetserver | |
| 19 20 |  | 
| 20 21 | 
             
                    # Not all connections require a client cert to be present.
         | 
| 21 22 | 
             
                    # For example, when querying the status endpoint.
         | 
| 22 | 
            -
                    def initialize(settings, with_client_cert: true)
         | 
| 23 | 
            +
                    def initialize(logger, settings, with_client_cert: true)
         | 
| 24 | 
            +
                      @logger = logger
         | 
| 23 25 | 
             
                      @store = make_store(settings[:localcacert],
         | 
| 24 26 | 
             
                                          settings[:certificate_revocation],
         | 
| 25 27 | 
             
                                          settings[:hostcrl])
         | 
| @@ -50,7 +52,7 @@ module Puppetserver | |
| 50 52 | 
             
                    # The Connection object should have HTTP verbs defined on it that take
         | 
| 51 53 | 
             
                    # a body (and optional overrides). Returns whatever the block given returned.
         | 
| 52 54 | 
             
                    def with_connection(url, &block)
         | 
| 53 | 
            -
                      request = ->(conn) { block.call(Connection.new(conn, url)) }
         | 
| 55 | 
            +
                      request = ->(conn) { block.call(Connection.new(conn, url, @logger)) }
         | 
| 54 56 |  | 
| 55 57 | 
             
                      begin
         | 
| 56 58 | 
             
                        Net::HTTP.start(url.host, url.port,
         | 
| @@ -85,25 +87,30 @@ module Puppetserver | |
| 85 87 | 
             
                    # and defines methods named after HTTP verbs that are called on the
         | 
| 86 88 | 
             
                    # saved connection, returning a Result.
         | 
| 87 89 | 
             
                    class Connection
         | 
| 88 | 
            -
                      def initialize(net_http_connection, url_struct)
         | 
| 90 | 
            +
                      def initialize(net_http_connection, url_struct, logger)
         | 
| 89 91 | 
             
                        @conn = net_http_connection
         | 
| 90 92 | 
             
                        @url = url_struct
         | 
| 93 | 
            +
                        @logger = logger
         | 
| 91 94 | 
             
                      end
         | 
| 92 95 |  | 
| 93 96 | 
             
                      def get(url_overide = nil, headers = {})
         | 
| 94 97 | 
             
                        url = url_overide || @url
         | 
| 95 98 | 
             
                        headers = DEFAULT_HEADERS.merge(headers)
         | 
| 96 99 |  | 
| 100 | 
            +
                        @logger.debug("Making a GET request at #{url.full_url}")
         | 
| 101 | 
            +
             | 
| 97 102 | 
             
                        request = Net::HTTP::Get.new(url.to_uri, headers)
         | 
| 98 103 | 
             
                        result = @conn.request(request)
         | 
| 99 | 
            -
             | 
| 100 104 | 
             
                        Result.new(result.code, result.body)
         | 
| 105 | 
            +
             | 
| 101 106 | 
             
                      end
         | 
| 102 107 |  | 
| 103 108 | 
             
                      def put(body, url_override = nil, headers = {})
         | 
| 104 109 | 
             
                        url = url_override || @url
         | 
| 105 110 | 
             
                        headers = DEFAULT_HEADERS.merge(headers)
         | 
| 106 111 |  | 
| 112 | 
            +
                        @logger.debug("Making a PUT request at #{url.full_url}")
         | 
| 113 | 
            +
             | 
| 107 114 | 
             
                        request = Net::HTTP::Put.new(url.to_uri, headers)
         | 
| 108 115 | 
             
                        request.body = body
         | 
| 109 116 | 
             
                        result = @conn.request(request)
         | 
| @@ -115,6 +122,8 @@ module Puppetserver | |
| 115 122 | 
             
                        url = url_override || @url
         | 
| 116 123 | 
             
                        headers = DEFAULT_HEADERS.merge(headers)
         | 
| 117 124 |  | 
| 125 | 
            +
                        @logger.debug("Making a DELETE request at #{url.full_url}")
         | 
| 126 | 
            +
             | 
| 118 127 | 
             
                        result = @conn.request(Net::HTTP::Delete.new(url.to_uri, headers))
         | 
| 119 128 |  | 
| 120 129 | 
             
                        Result.new(result.code, result.body)
         | 
| @@ -127,10 +136,13 @@ module Puppetserver | |
| 127 136 | 
             
                    # Like URI, but not... maybe of suspicious value
         | 
| 128 137 | 
             
                    URL = Struct.new(:protocol, :host, :port,
         | 
| 129 138 | 
             
                                     :endpoint, :version,
         | 
| 130 | 
            -
                                     :resource_type, :resource_name) do
         | 
| 139 | 
            +
                                     :resource_type, :resource_name, :query) do
         | 
| 131 140 | 
             
                            def full_url
         | 
| 132 | 
            -
                              protocol + '://' + host + ':' + port + '/' +
         | 
| 133 | 
            -
             | 
| 141 | 
            +
                              url = protocol + '://' + host + ':' + port + '/' +
         | 
| 142 | 
            +
                                    [endpoint, version, resource_type, resource_name].join('/')
         | 
| 143 | 
            +
             | 
| 144 | 
            +
                              url = url + "?" + URI.encode_www_form(query) unless query.empty?
         | 
| 145 | 
            +
                              return url
         | 
| 134 146 | 
             
                            end
         | 
| 135 147 |  | 
| 136 148 | 
             
                            def to_uri
         | 
| @@ -171,7 +183,7 @@ module Puppetserver | |
| 171 183 | 
             
                        # we commonly won't have one, don't require one for creating the connection.
         | 
| 172 184 | 
             
                        # Additionally, we want to ensure the server is stopped before migrating the CA dir to
         | 
| 173 185 | 
             
                        # avoid issues with writing to the CA dir and moving it.
         | 
| 174 | 
            -
                        self.new(settings, with_client_cert: false).with_connection(status_url) do |conn|
         | 
| 186 | 
            +
                        self.new(logger, settings, with_client_cert: false).with_connection(status_url) do |conn|
         | 
| 175 187 | 
             
                          result = conn.get
         | 
| 176 188 | 
             
                          if result.body == "running"
         | 
| 177 189 | 
             
                            logger.err "Puppetserver service is running. Please stop it before attempting to run this command."
         | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: puppetserver-ca
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 2. | 
| 4 | 
            +
              version: 2.3.2
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Puppet, Inc.
         | 
| 8 8 | 
             
            autorequire: 
         | 
| 9 9 | 
             
            bindir: exe
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2021- | 
| 11 | 
            +
            date: 2021-08-17 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: facter
         | 
| @@ -100,6 +100,7 @@ files: | |
| 100 100 | 
             
            - lib/puppetserver/ca/action/import.rb
         | 
| 101 101 | 
             
            - lib/puppetserver/ca/action/list.rb
         | 
| 102 102 | 
             
            - lib/puppetserver/ca/action/migrate.rb
         | 
| 103 | 
            +
            - lib/puppetserver/ca/action/prune.rb
         | 
| 103 104 | 
             
            - lib/puppetserver/ca/action/revoke.rb
         | 
| 104 105 | 
             
            - lib/puppetserver/ca/action/setup.rb
         | 
| 105 106 | 
             
            - lib/puppetserver/ca/action/sign.rb
         |