Class: Puppetserver::Ca::CertificateAuthority

Inherits:
Object
  • Object
show all
Includes:
Utils
Defined in:
lib/puppetserver/ca/certificate_authority.rb

Constant Summary collapse

REVOKE_BODY =
JSON.dump({ desired_state: 'revoked' })
SIGN_BODY =
JSON.dump({ desired_state: 'signed' })

Instance Method Summary collapse

Constructor Details

#initialize(logger, settings) ⇒ CertificateAuthority

Returns a new instance of CertificateAuthority.



14
15
16
17
18
19
# File 'lib/puppetserver/ca/certificate_authority.rb', line 14

def initialize(logger, settings)
  @logger = logger
  @client = HttpClient.new(settings)
  @ca_server = settings[:ca_server]
  @ca_port = settings[:ca_port]
end

Instance Method Details

#check_clean(certname, result) ⇒ Object

logs the action and returns a status symbol 👑



195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
# File 'lib/puppetserver/ca/certificate_authority.rb', line 195

def check_clean(certname, result)
  case result.code
  when '200', '204'
    @logger.inform "Cleaned files related to #{certname}"
    return :success
  when '404'
    @logger.err 'Error:'
    @logger.err "    Could not find files to clean for #{certname}"
    return :not_found
  else
    @logger.err 'Error:'
    @logger.err "    When attempting to clean certificate '#{certname}', received:"
    @logger.err "      code: #{result.code}"
    @logger.err "      body: #{result.body.to_s}" if result.body
    return :error
  end
end

#check_revocation(certname, result) ⇒ Object

possibly logs the action, always returns a status symbol 👑



176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
# File 'lib/puppetserver/ca/certificate_authority.rb', line 176

def check_revocation(certname, result)
  case result.code
  when '200', '204'
    @logger.inform "Revoked certificate for #{certname}"
    return :success
  when '409'
    return :invalid
  when '404'
    return :not_found
  else
    @logger.err 'Error:'
    @logger.err "    When attempting to revoke certificate '#{certname}', received:"
    @logger.err "      code: #{result.code}"
    @logger.err "      body: #{result.body.to_s}" if result.body
    return :error
  end
end

#clean_certs(certnames) ⇒ Boolean

Make an HTTP request to CA to clean the named certificates

Parameters:

  • certnames (Array)

    the name of the certificate(s) to have cleaned

Returns:

  • (Boolean)

    whether all certificate cleaning and revocation was successful



140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/puppetserver/ca/certificate_authority.rb', line 140

def clean_certs(certnames)
  url = make_ca_url('certificate_status')

  results = @client.with_connection(url) do |connection|
    certnames.map do |certname|
      url.resource_name = certname
      revoke_result = connection.put(REVOKE_BODY, url)
      revoked = check_revocation(certname, revoke_result)

      cleaned = nil
      unless revoked == :error
        clean_result = connection.delete(url)
        cleaned = check_clean(certname, clean_result)
      end

      if revoked == :error || cleaned != :success
        :error

      # If we get passed the first conditional we know that
      # cleaned must == :success and revoked must be one of
      # :invalid, :not_found, or :success. We'll treat both
      # :not_found and :success of revocation here as successes.
      # However we'll treat invalid's specially.
      elsif revoked == :invalid
        :invalid

      else
        :success
      end
    end
  end

  return results.reduce {|prev, curr| worst_result(prev, curr) }
end

#get(resource_type, resource_name) ⇒ Struct

Make an HTTP GET request to CA

Parameters:

  • resource_type (String)

    the resource type of url

  • resource_name (String)

    the resource name of url

Returns:

  • (Struct)

    an instance of the Result struct with :code, :body



251
252
253
254
255
256
# File 'lib/puppetserver/ca/certificate_authority.rb', line 251

def get(resource_type, resource_name)
  url = make_ca_url(resource_type, resource_name)
  @client.with_connection(url) do |connection|
    connection.get(url)
  end
end

#get_certificate(certname) ⇒ Object

Returns nil for errors, else the result of the GET request



228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
# File 'lib/puppetserver/ca/certificate_authority.rb', line 228

def get_certificate(certname)
  result = get('certificate', certname)

  case result.code
  when '200'
    return result
  when '404'
    @logger.err 'Error:'
    @logger.err "    Signed certificate #{certname} could not be found on the CA"
    return nil
  else
    @logger.err 'Error:'
    @logger.err "    When attempting to download certificate '#{certname}', received:"
    @logger.err "      code: #{result.code}"
    @logger.err "      body: #{result.body.to_s}" if result.body
    return nil
  end
end

#get_certificate_statusesObject

Returns nil for errors, else the result of the GET request



214
215
216
217
218
219
220
221
222
223
224
225
# File 'lib/puppetserver/ca/certificate_authority.rb', line 214

def get_certificate_statuses
  result = get('certificate_statuses', 'any_key')

  unless result.code == '200'
    @logger.err 'Error:'
    @logger.err "    code: #{result.code}"
    @logger.err "    body: #{result.body}" if result.body
    return nil
  end

  result
end

#make_ca_url(resource_type = nil, certname = nil) ⇒ Object

Returns a URI-like wrapper around CA specific urls



34
35
36
# File 'lib/puppetserver/ca/certificate_authority.rb', line 34

def make_ca_url(resource_type = nil, certname = nil)
  HttpClient::URL.new('https', @ca_server, @ca_port, 'puppet-ca', 'v1', resource_type, certname)
end

#process_results(type, certname, result) ⇒ Object

logs the action and returns true/false for success



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/puppetserver/ca/certificate_authority.rb', line 84

def process_results(type, certname, result)
  case type
  when :sign
    case result.code
    when '204'
      @logger.inform "Successfully signed certificate request for #{certname}"
      return :success
    when '404'
      @logger.err 'Error:'
      @logger.err "    Could not find certificate request for #{certname}"
      return :not_found
    else
      @logger.err 'Error:'
      @logger.err "    When attempting to sign certificate request '#{certname}', received"
      @logger.err "      code: #{result.code}"
      @logger.err "      body: #{result.body.to_s}" if result.body
      return :error
    end
  when :revoke
    case result.code
    when '200', '204'
      @logger.inform "Revoked certificate for #{certname}"
      return :success
    when '404'
      @logger.err 'Error:'
      @logger.err "    Could not find certificate for #{certname}"
      return :not_found
    when '409'
      @logger.err 'Error:'
      @logger.err "    Could not revoke unsigned csr for #{certname}"
      return :invalid
    else
      @logger.err 'Error:'
      @logger.err "    When attempting to revoke certificate '#{certname}', received:"
      @logger.err "      code: #{result.code}"
      @logger.err "      body: #{result.body.to_s}" if result.body
      return :error
    end
  when :submit
    case result.code
    when '200', '204'
      @logger.inform "Successfully submitted certificate request for #{certname}"
      return :success
    else
      @logger.err 'Error:'
      @logger.err "    When attempting to submit certificate request for '#{certname}', received:"
      @logger.err "      code: #{result.code}"
      @logger.err "      body: #{result.body.to_s}" if result.body
      return :error
    end
  end
end

#put(certnames, resource_type:, body:, type:, headers: {}) ⇒ Boolean

Make an HTTP PUT request to CA

Parameters:

  • resource_type (String)

    the resource type of url

  • certnames (Array)

    array of certnames

  • body (JSON/String)

    body of the put request

  • type (Symbol)

    type of error processing to perform on result

Returns:

  • (Boolean)

    whether all requests were successful



72
73
74
75
76
77
78
79
80
81
# File 'lib/puppetserver/ca/certificate_authority.rb', line 72

def put(certnames, resource_type:, body:, type:, headers: {})
  url = make_ca_url(resource_type)
  results = @client.with_connection(url) do |connection|
    certnames.map do |certname|
      url.resource_name = certname
      result = connection.put(body, url, headers)
      process_results(type, certname, result)
    end
  end
end

#revoke_certs(certnames) ⇒ Object



47
48
49
50
51
52
53
54
# File 'lib/puppetserver/ca/certificate_authority.rb', line 47

def revoke_certs(certnames)
  results = put(certnames,
              resource_type: 'certificate_status',
              body: REVOKE_BODY,
              type: :revoke)

  results.reduce {|prev, curr| worst_result(prev, curr) }
end

#sign_certs(certnames) ⇒ Object



38
39
40
41
42
43
44
45
# File 'lib/puppetserver/ca/certificate_authority.rb', line 38

def sign_certs(certnames)
  results = put(certnames,
                resource_type: 'certificate_status',
                body: SIGN_BODY,
                type: :sign)

  results.all? {|result| result == :success }
end

#submit_certificate_request(certname, csr) ⇒ Object



56
57
58
59
60
61
62
63
64
# File 'lib/puppetserver/ca/certificate_authority.rb', line 56

def submit_certificate_request(certname, csr)
  results = put([certname],
              resource_type: 'certificate_request',
              body: csr.to_pem,
              headers: {'Content-Type' => 'text/plain'},
              type: :submit)

  results.all? {|result| result == :success }
end

#worst_result(previous_result, current_result) ⇒ Object



21
22
23
24
25
26
27
28
29
30
31
# File 'lib/puppetserver/ca/certificate_authority.rb', line 21

def worst_result(previous_result, current_result)
  %i{success invalid not_found error}.each do |state|
    if previous_result == state
      return current_result
    elsif current_result == state
      return previous_result
    else
      next
    end
  end
end