From a6baf60e59ba83aaeeff1c86e53769f61f0b5626 Mon Sep 17 00:00:00 2001 From: Martin Lensment Date: Wed, 25 Feb 2015 17:33:32 +0200 Subject: [PATCH] Readme updates + tests for cert --- CHANGELOG.md | 5 ++ README.md | 19 +++++- app/models/certificate.rb | 5 ++ app/models/version/certificate_version.rb | 4 ++ config/application-example.yml | 1 + .../20150223104842_create_certificates.rb | 14 +++++ db/schema.rb | 14 +++++ spec/fabricators/certificate_fabricator.rb | 33 ++++++++++ spec/models/certificate_spec.rb | 61 +++++++++++++++++++ 9 files changed, 154 insertions(+), 2 deletions(-) create mode 100644 app/models/version/certificate_version.rb create mode 100644 spec/fabricators/certificate_fabricator.rb create mode 100644 spec/models/certificate_spec.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index d19f858a1..b322d303e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -97,6 +97,8 @@ With these lines: SSLVerifyClient require SSLVerifyDepth 1 SSLCACertificateFile /home/registry/registry/shared/ca/certs/ca.crt.pem + SSLCARevocationFile /home/registry/registry/shared/ca/crl/crl.pem + SSLCARevocationCheck chain RequestHeader set SSL_CLIENT_S_DN_CN "%{SSL_CLIENT_S_DN_CN}s" ``` @@ -110,6 +112,8 @@ Add these lines: SSLVerifyClient none SSLVerifyDepth 1 SSLCACertificateFile /home/registry/registry/shared/ca/certs/ca.crt.pem + SSLCARevocationFile /home/registry/registry/shared/ca/crl/crl.pem + SSLCARevocationCheck chain RequestHeader set SSL_CLIENT_S_DN_CN "" @@ -134,6 +138,7 @@ Configure registry and epp application.yml to match the CA settings: ca_cert_path: '/home/registry/registry/shared/ca/certs/ca.crt.pem' ca_key_path: '/home/registry/registry/shared/ca/private/ca.key.pem' ca_key_password: 'registryalpha' +crl_path: '/home/registry/registry/shared/ca/crl/crl.pem' webclient_ip: '54.154.91.240' ``` diff --git a/README.md b/README.md index 8b1dabe6d..384814f8f 100644 --- a/README.md +++ b/README.md @@ -153,6 +153,9 @@ Be sure to update paths to match your system configuration. SSLVerifyClient require SSLVerifyDepth 1 SSLCACertificateFile /home/registry/registry/shared/ca/certs/ca.crt.pem + SSLCARevocationFile /home/registry/registry/shared/ca/crl/crl.pem + SSLCARevocationCheck chain + RequestHeader set SSL_CLIENT_S_DN_CN "%{SSL_CLIENT_S_DN_CN}s" EPPEngine On @@ -192,6 +195,7 @@ mkdir certs crl newcerts private csrs chmod 700 private touch index.txt echo 1000 > serial +echo 1000 > crlnumber ``` Generate the root key (prompts for pass phrase): @@ -257,12 +261,23 @@ Sign the request and create certificate: openssl ca -keyfile private/ca.key.pem -cert certs/ca.crt.pem -extensions usr_cert -notext -md sha256 -in csrs/webclient.csr.pem -out certs/webclient.crt.pem ``` -Certificates for API Users are generated via the user interface. CSR must be uploaded for each API User. Certificates are created automatically after saving the user. +Create certificate revocation list (prompts for pass phrase): +``` +openssl ca -keyfile private/ca.key.pem -cert certs/ca.crt.pem -gencrl -out crl/crl.pem +``` -Private key and certificate must be packaged to pkcs12 and added to the browser's certificate bank. +Certificates for API Users are generated via the user interface. CSR must be uploaded for each API User. + +Private key and certificate must be packaged to pkcs12 and added to the browser. Make sure application configuration files contain correct paths to certificates. +In test environment it's important to set unique_subject option to false. +In CA directory: +``` +echo "unique_subject = no" > index.txt.attr +``` + ### EPP web client Please follow EPP web client readme: diff --git a/app/models/certificate.rb b/app/models/certificate.rb index ce46c2b6e..30cbbe949 100644 --- a/app/models/certificate.rb +++ b/app/models/certificate.rb @@ -1,4 +1,8 @@ class Certificate < ActiveRecord::Base + include Versions + + belongs_to :api_user + SIGNED = 'signed' UNSIGNED = 'unsigned' EXPIRED = 'expired' @@ -69,6 +73,7 @@ class Certificate < ActiveRecord::Base if err.match(/Data Base Updated/) || err.match(/ERROR:Already revoked/) save! + @cached_status = REVOKED else errors.add(:base, I18n.t('failed_to_revoke_certificate')) logger.error('FAILED TO REVOKE CLIENT CERTIFICATE') diff --git a/app/models/version/certificate_version.rb b/app/models/version/certificate_version.rb new file mode 100644 index 000000000..bcc89e62a --- /dev/null +++ b/app/models/version/certificate_version.rb @@ -0,0 +1,4 @@ +class CertificateVersion < PaperTrail::Version + self.table_name = :log_certificates + self.sequence_name = :log_certificates_id_seq +end diff --git a/config/application-example.yml b/config/application-example.yml index 51a4661e5..4ac8d0ddb 100644 --- a/config/application-example.yml +++ b/config/application-example.yml @@ -10,6 +10,7 @@ defaults: &defaults ca_cert_path: ca-cert-path-here ca_key_path: ca-key-path-here ca_key_password: ca-key-pass-phrase-here + crl_path: crl-path-here development: <<: *defaults diff --git a/db/migrate/20150223104842_create_certificates.rb b/db/migrate/20150223104842_create_certificates.rb index 6a1c55503..b84e9b5ce 100644 --- a/db/migrate/20150223104842_create_certificates.rb +++ b/db/migrate/20150223104842_create_certificates.rb @@ -4,10 +4,24 @@ class CreateCertificates < ActiveRecord::Migration t.integer :api_user_id t.text :csr t.text :crt + t.string :creator_str + t.string :updator_str t.timestamps end + create_table :log_certificates do |t| + t.string "item_type", null: false + t.integer "item_id", null: false + t.string "event", null: false + t.string "whodunnit" + t.json "object" + t.json "object_changes" + t.datetime "created_at" + t.string "session" + t.json "children" + end + ApiUser.all.each do |x| x.certificates << Certificate.new(crt: x.crt, csr: x.csr) end diff --git a/db/schema.rb b/db/schema.rb index 82b269b56..cb37d9166 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -57,6 +57,8 @@ ActiveRecord::Schema.define(version: 20150223104842) do t.integer "api_user_id" t.text "csr" t.text "crt" + t.string "creator_str" + t.string "updator_str" t.datetime "created_at" t.datetime "updated_at" end @@ -283,6 +285,18 @@ ActiveRecord::Schema.define(version: 20150223104842) do add_index "log_api_users", ["item_type", "item_id"], name: "index_log_api_users_on_item_type_and_item_id", using: :btree add_index "log_api_users", ["whodunnit"], name: "index_log_api_users_on_whodunnit", using: :btree + create_table "log_certificates", force: :cascade do |t| + t.string "item_type", null: false + t.integer "item_id", null: false + t.string "event", null: false + t.string "whodunnit" + t.json "object" + t.json "object_changes" + t.datetime "created_at" + t.string "session" + t.json "children" + end + create_table "log_contact_disclosures", force: :cascade do |t| t.string "item_type", null: false t.integer "item_id", null: false diff --git a/spec/fabricators/certificate_fabricator.rb b/spec/fabricators/certificate_fabricator.rb new file mode 100644 index 000000000..8e97b9ec5 --- /dev/null +++ b/spec/fabricators/certificate_fabricator.rb @@ -0,0 +1,33 @@ +# default fabricator should be reusable +Fabricator(:certificate) do + api_user + csr "-----BEGIN CERTIFICATE REQUEST-----\n" \ + "MIIE+DCCAuACAQAwgZ0xCzAJBgNVBAYTAkVFMREwDwYDVQQIDAhIYXJqdW1hYTEQ\n" \ + "MA4GA1UEBwwHVGFsbGlubjEbMBkGA1UECgwSRWVzdGkgSW50ZXJuZXRpIFNBMRIw\n" \ + "EAYDVQQLDAlSRUdJU1RSQVIxEjAQBgNVBAMMCXdlYmNsaWVudDEkMCIGCSqGSIb3\n" \ + "DQEJARYVd2ViY2xpZW50QGludGVybmV0LmVlMIICIjANBgkqhkiG9w0BAQEFAAOC\n" \ + "Ag8AMIICCgKCAgEAuXronFj8CxPWGkyUhXf+/WirkFGb8a/My2+7GvQWYE10Nq4C\n" \ + "u9wDgjU3AuLw8qzwEeE3Z5uxHXWfwnshXOF6aJNCQWUsrs0odCxw69iIwCNGKhyF\n" \ + "jljtx8uSH8RRSRc8BFIUkvUpmp8m7kZTlB4FDey+XaGy4p/rImiAiwfFMIJMjdE9\n" \ + "9gk0EGDbomgP6KC3Ss/iQfuOFCQWSqjFuvp3mygr193YplaPgeLM1ERIW1LVFGDK\n" \ + "jy6keZ3E/Vb4O4qUPDRgTMr2KWM3Auzh2hXCymHNWn3yRn5Q4KSjJbG/P7Kz5nfZ\n" \ + "kY3eVRBIBll+1Q0VV7g+1B48zzjZX2qiY3iL77MV1oL17KeOO3PAxsEtptdqNgUa\n" \ + "Fpp73dwPST1ZKvq8FSgDKcdTCziSeViGhXjJRpEMr8FoeKNO7nvd1maKN9HAOy75\n" \ + "eSxatj6LoQ+JFN7Ci3IbwKFI7BnIHbEr9eP7O7Qbhljz2GE9+GWUqr3zwUEgpFSI\n" \ + "crAnRHQI2ALakEMsryF416zg5yr/bJp8/IzgZLaKpBVLOL88sI6r+JRdM6QXvKYx\n" \ + "XhamV6bH6CrR8ZYN4okaZH6sAcy8eyBnEmc05h/KsDzTNadwadeZe73F+PltoEXH\n" \ + "XgtpTpQ8XarN1uLq99WD6gWilAx3LF/xetCO86+w/MkYBmfOrXge+WLUUW8CAwEA\n" \ + "AaAVMBMGCSqGSIb3DQEJBzEGDAR0ZXN0MA0GCSqGSIb3DQEBCwUAA4ICAQAkTlU3\n" \ + "RcI6UMRA7As2FJSph3QurPebQFoZhnhMD+hb6+hXip8MY77YxLwo/ihB9wghaZKL\n" \ + "uV0BxjdZgjDt9GhA8dtPgaCp5LvB6kQYvcEzRvitN2CpJhtz39rlF3gxuy+RtpNf\n" \ + "5KbC691FivoXur1qx9I7mc4snB3DTzLiJPIZ6nQzPYcSVpPCbns30N/i/sOdHO0o\n" \ + "9hP5wlhCdYrOxad993m+InpMDyDWhB1+TA9ZO7gYpg8S4kBX3Cz9OXe80Pe56ZdK\n" \ + "pcgjTXnUDjNSRRGamJib2lyZ/axMbb/etwyy3X+jBDuOQropkmgrPEFJHpgNlFah\n" \ + "BuW7KEASqbw5YxpTSc0nDk5uxBw3voL8fk9M1sX64tbzGAEBRZnrWGeb1mOLM/YI\n" \ + "K6ocAYSBhNmWUzpHTwL7qSeP9ztQUGzoGHyRjBdan+1U2G75Kpj+TjEm/X8wmtnq\n" \ + "3/qVhUYNEavcZbgR1gSE45+mS8NsD7Oq0Xdc0UKsVDbUcCGIkGG9+ERAbRznfi3W\n" \ + "qhChtUxySX8T3SmX5mviwlJ5OwQVjdUF1/2voPK0oFK7zV+wZqcuORzDKdqB8XV7\n" \ + "MDcQjza4EOB78OmcHDgQ7nMXuY7/UL4F+bRZosxPy43X2JId5d+/GpgV8sP9dzK8\n" \ + "UGJDNEZ2YsBbPuKZS+2eNZ8g3sjjFBeadvrQ1w==\n" \ + "-----END CERTIFICATE REQUEST-----" +end diff --git a/spec/models/certificate_spec.rb b/spec/models/certificate_spec.rb new file mode 100644 index 000000000..109b0a482 --- /dev/null +++ b/spec/models/certificate_spec.rb @@ -0,0 +1,61 @@ +require 'rails_helper' + +describe Certificate do + it { should belong_to(:api_user) } + + context 'with invalid attribute' do + before :all do + @certificate = Certificate.new + end + + it 'should not be valid' do + @certificate.valid? + @certificate.errors.full_messages.should match_array([ + "Csr is missing" + ]) + end + + it 'should not have any versions' do + @certificate.versions.should == [] + end + end + + context 'with valid attributes' do + before :all do + @certificate = Fabricate(:certificate) + end + + it 'should be valid' do + @certificate.valid? + @certificate.errors.full_messages.should match_array([]) + end + + it 'should be valid twice' do + @certificate = Fabricate(:certificate) + @certificate.valid? + @certificate.errors.full_messages.should match_array([]) + end + + it 'should sign csr' do + @certificate.status.should == 'unsigned' + @certificate.sign! + @certificate.status.should == 'signed' + @certificate.crt.should_not be_blank + end + + it 'should revoke crt' do + @certificate.revoke! + @certificate.status.should == 'revoked' + end + + it 'should have one version' do + with_versioning do + @certificate.versions.should == [] + @certificate.csr = 'new_request' + @certificate.save + @certificate.errors.full_messages.should match_array([]) + @certificate.versions.size.should == 1 + end + end + end +end