Allow to load ECDSA certificates for pages domains

Just replace RSA.new with PKey.read
This commit is contained in:
Vladimir Shushlin 2019-09-07 00:29:03 +00:00 committed by Michael Kozono
parent 7920ff1147
commit 8c3d0703ed
7 changed files with 198 additions and 4 deletions

View File

@ -17,7 +17,7 @@ class PagesDomain < ApplicationRecord
validates :certificate, certificate: true, if: ->(domain) { domain.certificate.present? }
validates :key, presence: { message: 'must be present if HTTPS-only is enabled' },
if: :certificate_should_be_present?
validates :key, certificate_key: true, if: ->(domain) { domain.key.present? }
validates :key, certificate_key: true, named_ecdsa_key: true, if: ->(domain) { domain.key.present? }
validates :verification_code, presence: true, allow_blank: false
validate :validate_pages_domain
@ -247,7 +247,7 @@ class PagesDomain < ApplicationRecord
def pkey
return unless key
@pkey ||= OpenSSL::PKey::RSA.new(key)
@pkey ||= OpenSSL::PKey.read(key)
rescue OpenSSL::PKey::PKeyError, OpenSSL::Cipher::CipherError
nil
end

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true
# UrlValidator
# CertificateKeyValidator
#
# Custom validator for private keys.
#
@ -20,7 +20,7 @@ class CertificateKeyValidator < ActiveModel::EachValidator
def valid_private_key_pem?(value)
return false unless value
pkey = OpenSSL::PKey::RSA.new(value)
pkey = OpenSSL::PKey.read(value)
pkey.private?
rescue OpenSSL::PKey::PKeyError
false

View File

@ -0,0 +1,34 @@
# frozen_string_literal: true
# NamedEcdsaKeyValidator
#
# Custom validator for ecdsa private keys.
# Golang currently doesn't support explicit curves for ECDSA certificates
# This validator checks if curve is set by name, not by parameters
#
# class Project < ActiveRecord::Base
# validates :certificate_key, named_ecdsa_key: true
# end
#
class NamedEcdsaKeyValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
if explicit_ec?(value)
record.errors.add(attribute, "ECDSA keys with explicit curves are not supported")
end
end
private
UNNAMED_CURVE = "UNDEF"
def explicit_ec?(value)
return false unless value
pkey = OpenSSL::PKey.read(value)
return false unless pkey.is_a?(OpenSSL::PKey::EC)
pkey.group.curve_name == UNNAMED_CURVE
rescue OpenSSL::PKey::PKeyError
false
end
end

View File

@ -0,0 +1,5 @@
---
title: Allow ECDSA certificates for pages domains
merge_request: 32393
author:
type: added

View File

@ -271,5 +271,88 @@ ZDXgrA==
auto_ssl_enabled { true }
certificate_source { :gitlab_provided }
end
trait :explicit_ecdsa do
certificate '-----BEGIN CERTIFICATE-----
MIID1zCCAzkCCQDatOIwBlktwjAKBggqhkjOPQQDAjBPMQswCQYDVQQGEwJVUzEL
MAkGA1UECAwCTlkxCzAJBgNVBAcMAk5ZMQswCQYDVQQLDAJJVDEZMBcGA1UEAwwQ
dGVzdC1jZXJ0aWZpY2F0ZTAeFw0xOTA4MjkxMTE1NDBaFw0yMTA4MjgxMTE1NDBa
ME8xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJOWTELMAkGA1UEBwwCTlkxCzAJBgNV
BAsMAklUMRkwFwYDVQQDDBB0ZXN0LWNlcnRpZmljYXRlMIICXDCCAc8GByqGSM49
AgEwggHCAgEBME0GByqGSM49AQECQgH/////////////////////////////////
/////////////////////////////////////////////////////zCBngRCAf//
////////////////////////////////////////////////////////////////
///////////////////8BEFRlT65YY4cmh+SmiGgtoVA7qLacluZsxXzuLSJkY7x
CeFWGTlR7H6TexZSwL07sb8HNXPfiD0sNPHvRR/Ua1A/AAMVANCeiAApHLhTlsxn
FzkyhKqg2mS6BIGFBADGhY4GtwQE6c2ePstmI5W0QpxkgTkFP7Uh+CivYGtNPbqh
S1537+dZKP4dwSei/6jeM0izwYVqQpv5fn4xwuW9ZgEYOSlqeJo7wARcil+0LH0b
2Zj1RElXm0RoF6+9Fyc+ZiyX7nKZXvQmQMVQuQE/rQdhNTxwhqJywkCIvpR2n9Fm
UAJCAf//////////////////////////////////////////+lGGh4O/L5Zrf8wB
SPcJpdA7tcm4iZxHrrtvtx6ROGQJAgEBA4GGAAQBVG/4c/hgl36toHj+eGL4pqv7
l7M+ZKQJ4vz0Y9E6xIx+gvfVaZ58krmbBAP53ikwneQbFdcvw3L/ACPEib/qWjkB
ogykguy3OwHtKLYNnDWIsfiLumEjElhcBMZVXiXhb5txf11uXAWn5n6Qhey5YKPM
NjLLqDqaG19efCLCd21A0TcwCgYIKoZIzj0EAwIDgYsAMIGHAkEm68kYFVnN1c2N
OjSJpIDdFWGVYJHyMDI5WgQyhm4hAioXJ0T22Zab8Wmq+hBYRJNcHoaV894blfqR
V3ZJgam8EQJCAcnPpJQ0IqoT1pAQkaL3+Ka8ZaaCd6/8RnoDtGvWljisuyH65SRu
kmYv87bZe1KqOZDoaDBdfVsoxcGbik19lBPV
-----END CERTIFICATE-----'
key '-----BEGIN EC PARAMETERS-----
MIIBwgIBATBNBgcqhkjOPQEBAkIB////////////////////////////////////
//////////////////////////////////////////////////8wgZ4EQgH/////
////////////////////////////////////////////////////////////////
/////////////////ARBUZU+uWGOHJofkpohoLaFQO6i2nJbmbMV87i0iZGO8Qnh
Vhk5Uex+k3sWUsC9O7G/BzVz34g9LDTx70Uf1GtQPwADFQDQnogAKRy4U5bMZxc5
MoSqoNpkugSBhQQAxoWOBrcEBOnNnj7LZiOVtEKcZIE5BT+1Ifgor2BrTT26oUte
d+/nWSj+HcEnov+o3jNIs8GFakKb+X5+McLlvWYBGDkpaniaO8AEXIpftCx9G9mY
9URJV5tEaBevvRcnPmYsl+5ymV70JkDFULkBP60HYTU8cIaicsJAiL6Udp/RZlAC
QgH///////////////////////////////////////////pRhoeDvy+Wa3/MAUj3
CaXQO7XJuImcR667b7cekThkCQIBAQ==
-----END EC PARAMETERS-----
-----BEGIN EC PRIVATE KEY-----
MIICnQIBAQRCAZZRG4FJO+OK29ygycrNzjxQDB+dp+QPo1Pk6RAl5PcraohyhFnI
MGUL4ba1efZUxCbAWxjVRSi7QEUNYCCdUPAtoIIBxjCCAcICAQEwTQYHKoZIzj0B
AQJCAf//////////////////////////////////////////////////////////
////////////////////////////MIGeBEIB////////////////////////////
//////////////////////////////////////////////////////////wEQVGV
PrlhjhyaH5KaIaC2hUDuotpyW5mzFfO4tImRjvEJ4VYZOVHsfpN7FlLAvTuxvwc1
c9+IPSw08e9FH9RrUD8AAxUA0J6IACkcuFOWzGcXOTKEqqDaZLoEgYUEAMaFjga3
BATpzZ4+y2YjlbRCnGSBOQU/tSH4KK9ga009uqFLXnfv51ko/h3BJ6L/qN4zSLPB
hWpCm/l+fjHC5b1mARg5KWp4mjvABFyKX7QsfRvZmPVESVebRGgXr70XJz5mLJfu
cple9CZAxVC5AT+tB2E1PHCGonLCQIi+lHaf0WZQAkIB////////////////////
///////////////////////6UYaHg78vlmt/zAFI9wml0Du1ybiJnEeuu2+3HpE4
ZAkCAQGhgYkDgYYABAFUb/hz+GCXfq2geP54Yvimq/uXsz5kpAni/PRj0TrEjH6C
99VpnnySuZsEA/neKTCd5BsV1y/Dcv8AI8SJv+paOQGiDKSC7Lc7Ae0otg2cNYix
+Iu6YSMSWFwExlVeJeFvm3F/XW5cBafmfpCF7Llgo8w2MsuoOpobX158IsJ3bUDR
Nw==
-----END EC PRIVATE KEY-----'
end
trait :ecdsa do
certificate '-----BEGIN CERTIFICATE-----
MIIB8zCCAVUCCQCGKuPQ6SBxUTAKBggqhkjOPQQDAjA+MQswCQYDVQQGEwJVUzEL
MAkGA1UECAwCVVMxCzAJBgNVBAcMAlVTMRUwEwYDVQQDDAxzaHVzaGxpbi5kZXYw
HhcNMTkwOTAyMDkyMDUxWhcNMjEwOTAxMDkyMDUxWjA+MQswCQYDVQQGEwJVUzEL
MAkGA1UECAwCVVMxCzAJBgNVBAcMAlVTMRUwEwYDVQQDDAxzaHVzaGxpbi5kZXYw
gZswEAYHKoZIzj0CAQYFK4EEACMDgYYABAH9Jd7ZWnTasgltZRbIMreihycOh/G4
TXpkp8tTtEsuD+sh8au3Jywsi89RSZ6vgVoCY7//DQ2vamYnyBZqbL+cTQBsQ7wD
UEaSyP0R3P4b6Ox347pYzXwSdSOra9Cm4TMQe+prVMesxulqIm7G7CTI+9J8LHlJ
z0wUDQz/o+tUSYwv6zAKBggqhkjOPQQDAgOBiwAwgYcCQUOlTnn2QP/uYSh1dUSl
R9WYUg5+PQMg7kS+4K/5+5gonWCvaMcP+2P7hltUcvq41l3uMKKCZRU/x60/FMHc
1ZXdAkIBuVtm9RJXziNOKS4TcpH9os/FuREW8YQlpec58LDZdlivcHnikHZ4LCri
T7zu3VY6Rq+V/IKpsQwQjmoTJ0IpCM8=
-----END CERTIFICATE-----'
key '-----BEGIN EC PARAMETERS-----
BgUrgQQAIw==
-----END EC PARAMETERS-----
-----BEGIN EC PRIVATE KEY-----
MIHbAgEBBEFa72+eREW25IHbke0TiWFdW1R1ad9Nyqaz7CDtv5Kqdgd6Kcl8V2az
Lr6z1PS+JSERWzRP+fps7kdFRrtqy/ECpKAHBgUrgQQAI6GBiQOBhgAEAf0l3tla
dNqyCW1lFsgyt6KHJw6H8bhNemSny1O0Sy4P6yHxq7cnLCyLz1FJnq+BWgJjv/8N
Da9qZifIFmpsv5xNAGxDvANQRpLI/RHc/hvo7HfjuljNfBJ1I6tr0KbhMxB76mtU
x6zG6WoibsbsJMj70nwseUnPTBQNDP+j61RJjC/r
-----END EC PRIVATE KEY-----'
end
end
end

View File

@ -151,6 +151,24 @@ describe PagesDomain do
end
end
end
context 'with ecdsa certificate' do
it "is valid" do
domain = build(:pages_domain, :ecdsa)
expect(domain).to be_valid
end
context 'when curve is set explicitly by parameters' do
it 'adds errors to private key' do
domain = build(:pages_domain, :explicit_ecdsa)
expect(domain).to be_invalid
expect(domain.errors[:key]).not_to be_empty
end
end
end
end
describe 'validations' do

View File

@ -0,0 +1,54 @@
# frozen_string_literal: true
require 'spec_helper'
describe NamedEcdsaKeyValidator do
let(:validator) { described_class.new(attributes: [:key]) }
let!(:domain) { build(:pages_domain) }
subject { validator.validate_each(domain, :key, value) }
context 'with empty value' do
let(:value) { nil }
it 'does not add any error if value is empty' do
subject
expect(domain.errors).to be_empty
end
end
shared_examples 'does not add any error' do
it 'does not add any error' do
expect(value).to be_present
subject
expect(domain.errors).to be_empty
end
end
context 'when key is not EC' do
let(:value) { attributes_for(:pages_domain)[:key] }
include_examples 'does not add any error'
end
context 'with ECDSA certificate with named curve' do
let(:value) { attributes_for(:pages_domain, :ecdsa)[:key] }
include_examples 'does not add any error'
end
context 'with ECDSA certificate with explicit curve params' do
let(:value) { attributes_for(:pages_domain, :explicit_ecdsa)[:key] }
it 'adds errors' do
expect(value).to be_present
subject
expect(domain.errors[:key]).not_to be_empty
end
end
end