Commit ad07d1db authored by Vladimir Shushlin's avatar Vladimir Shushlin

Add Let's Encrypt errors to log

+specs
parent b8ff11ba
...@@ -37,7 +37,7 @@ module PagesDomains ...@@ -37,7 +37,7 @@ module PagesDomains
save_certificate(acme_order.private_key, api_order) save_certificate(acme_order.private_key, api_order)
acme_order.destroy! acme_order.destroy!
when 'invalid' when 'invalid'
save_order_error(acme_order) save_order_error(acme_order, api_order)
end end
end end
...@@ -48,11 +48,27 @@ module PagesDomains ...@@ -48,11 +48,27 @@ module PagesDomains
pages_domain.update!(gitlab_provided_key: private_key, gitlab_provided_certificate: certificate) pages_domain.update!(gitlab_provided_key: private_key, gitlab_provided_certificate: certificate)
end end
def save_order_error(acme_order) def save_order_error(acme_order, api_order)
log_error(api_order)
return unless Feature.enabled?(:pages_letsencrypt_errors, pages_domain.project) return unless Feature.enabled?(:pages_letsencrypt_errors, pages_domain.project)
acme_order.pages_domain.update_column(:auto_ssl_failed, true) pages_domain.assign_attributes(auto_ssl_failed: true)
pages_domain.save!(validate: false)
acme_order.destroy! acme_order.destroy!
end end
def log_error(api_order)
Gitlab::AppLogger.error(
message: "Failed to obtain Let's Encrypt certificate",
acme_error: api_order.challenge_error,
project_id: pages_domain.project_id,
pages_domain: pages_domain.domain
)
rescue => e
# getting authorizations is an additional network request which can raise errors
Gitlab::ErrorTracking.track_exception(e)
end
end end
end end
...@@ -7,7 +7,7 @@ module Gitlab ...@@ -7,7 +7,7 @@ module Gitlab
@acme_challenge = acme_challenge @acme_challenge = acme_challenge
end end
delegate :token, :file_content, :status, :request_validation, to: :acme_challenge delegate :token, :file_content, :status, :request_validation, :error, to: :acme_challenge
private private
......
...@@ -8,7 +8,6 @@ module Gitlab ...@@ -8,7 +8,6 @@ module Gitlab
end end
def new_challenge def new_challenge
authorization = @acme_order.authorizations.first
challenge = authorization.http challenge = authorization.http
::Gitlab::LetsEncrypt::Challenge.new(challenge) ::Gitlab::LetsEncrypt::Challenge.new(challenge)
end end
...@@ -22,11 +21,19 @@ module Gitlab ...@@ -22,11 +21,19 @@ module Gitlab
acme_order.finalize(csr: csr) acme_order.finalize(csr: csr)
end end
def challenge_error
authorization.challenges.first&.error
end
delegate :url, :status, :expires, :certificate, to: :acme_order delegate :url, :status, :expires, :certificate, to: :acme_order
private private
attr_reader :acme_order attr_reader :acme_order
def authorization
@acme_order.authorizations.first
end
end end
end end
end end
...@@ -38,4 +38,23 @@ describe ::Gitlab::LetsEncrypt::Order do ...@@ -38,4 +38,23 @@ describe ::Gitlab::LetsEncrypt::Order do
order.request_certificate(domain: 'example.com', private_key: private_key) order.request_certificate(domain: 'example.com', private_key: private_key)
end end
end end
describe '#challenge_error' do
it 'returns error if challenge has errors' do
challenge = acme_challenge_double
# error just to give an example
error = {
"type" => "urn:ietf:params:acme:error:dns",
"detail" => "No valid IP addresses found for test.example.com",
"status" => 400
}
allow(challenge).to receive(:error).and_return(error)
acme_order = acme_order_double(authorizations: [acme_authorization_double(challenge)])
expect(described_class.new(acme_order).challenge_error).to eq(error)
end
end
end end
...@@ -11,7 +11,8 @@ module LetsEncryptHelpers ...@@ -11,7 +11,8 @@ module LetsEncryptHelpers
status: 'pending', status: 'pending',
token: 'tokenvalue', token: 'tokenvalue',
file_content: 'hereisfilecontent', file_content: 'hereisfilecontent',
request_validation: true request_validation: true,
error: nil
}.freeze }.freeze
def stub_lets_encrypt_settings def stub_lets_encrypt_settings
...@@ -43,16 +44,17 @@ module LetsEncryptHelpers ...@@ -43,16 +44,17 @@ module LetsEncryptHelpers
challenge challenge
end end
def acme_authorization_double def acme_authorization_double(challenge = acme_challenge_double)
authorization = instance_double('Acme::Client::Resources::Authorization') authorization = instance_double('Acme::Client::Resources::Authorization')
allow(authorization).to receive(:http).and_return(acme_challenge_double) allow(authorization).to receive(:http).and_return(challenge)
allow(authorization).to receive(:challenges).and_return([challenge])
authorization authorization
end end
def acme_order_double(attributes = {}) def acme_order_double(attributes = {})
acme_order = instance_double('Acme::Client::Resources::Order') acme_order = instance_double('Acme::Client::Resources::Order')
allow(acme_order).to receive_messages(ACME_ORDER_METHODS.merge(attributes)) allow(acme_order).to receive_messages(ACME_ORDER_METHODS.merge(attributes))
allow(acme_order).to receive(:authorizations).and_return([acme_authorization_double]) allow(acme_order).to receive(:authorizations).and_return([acme_authorization_double]) unless attributes[:authorizations]
allow(acme_order).to receive(:finalize) allow(acme_order).to receive(:finalize)
acme_order acme_order
end end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment