New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature/install certificate #48
Changes from 6 commits
6e4c923
0be14dc
06e530f
335c748
5db3fae
cd24204
755042a
d2153ae
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
# | ||
# Cookbook Name:: dnsimple | ||
# Library:: provider_dnsimple_certificate | ||
# | ||
# Copyright 2014-2017 Aetrion, LLC dba DNSimple | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
require_relative 'dnsimple_provider' | ||
|
||
class Chef | ||
class Provider | ||
class DnsimpleCertificate < DnsimpleProvider | ||
use_inline_resources | ||
|
||
provides :dnsimple_certificate | ||
|
||
def load_current_resource | ||
@current_resource = Chef::Resource::DnsimpleCertificate.new(@new_resource.name) | ||
@current_resource.certificate_common_name(@new_resource.certificate_common_name) | ||
@current_resource.domain(@new_resource.domain) | ||
|
||
# TODO: replace dnsimple_client.certificates.certificates with .all_certificates when it is added | ||
# to the API client | ||
certificates = dnsimple_client.certificates.certificates(dnsimple_client_account_id, @new_resource.domain) | ||
@existing_certificate = certificates.data.detect do |certificate| | ||
(certificate.common_name == @new_resource.certificate_common_name) && (certificate.state == 'issued') && (Date.parse(certificate.expires_on) > Date.today) | ||
end | ||
|
||
@current_resource.exists = !@existing_certificate.nil? | ||
# rubocop:disable Style/GuardClause | ||
if @current_resource.exists | ||
@existing_certificate_bundle = dnsimple_client.certificates.download_certificate(dnsimple_client_account_id, @new_resource.domain, @existing_certificate.id).data | ||
@existing_private_key = dnsimple_client.certificates.certificate_private_key(dnsimple_client_account_id, @new_resource.domain, @existing_certificate.id).data | ||
@current_resource.expires_on = Date.parse(@existing_certificate.expires_on) | ||
@current_resource.server_pem = @existing_certificate_bundle.server | ||
@current_resource.chain_pem = @existing_certificate_bundle.chain | ||
@current_resource.private_key_pem = @existing_private_key.private_key | ||
end | ||
end | ||
|
||
action :install do | ||
if @current_resource.exists | ||
install_certificate | ||
else | ||
Chef::Log.info "DNSimple: no certificate found #{new_resource.certificate_common_name}" | ||
end | ||
end | ||
|
||
def install_certificate | ||
converge_by("install certificate #{current_resource.certificate_common_name} expiring #{current_resource.expires_on}") do | ||
declare_resource(:file, "#{current_resource.name}/#{current_resource.domain}.crt") do | ||
content "#{current_resource.server_pem}#{current_resource.chain_pem.join("\n")}" | ||
mode current_resource.mode | ||
owner current_resource.owner | ||
group current_resource.group | ||
end | ||
declare_resource(:file, "#{current_resource.name}/#{current_resource.domain}.key") do | ||
content current_resource.private_key_pem | ||
mode current_resource.mode | ||
owner current_resource.owner | ||
group current_resource.group | ||
sensitive true | ||
end | ||
end | ||
end | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
# | ||
# Cookbook Name:: dnsimple | ||
# Library:: resource_dnsimple_certificate | ||
# | ||
# Copyright 2014-2017 Aetrion, LLC dba DNSimple | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
require_relative 'dnsimple_resource' | ||
|
||
class Chef | ||
class Resource | ||
class DnsimpleCertificate < DnsimpleResource | ||
resource_name :dnsimple_certificate | ||
|
||
allowed_actions :install | ||
default_action :install | ||
|
||
property :install_path, kind_of: String, name_property: true | ||
property :certificate_common_name, kind_of: String, required: true | ||
property :domain, kind_of: String, required: true | ||
property :expires_on, kind_of: Date | ||
property :server_pem, kind_of: String | ||
property :chain_pem, kind_of: Array | ||
property :private_key_pem, kind_of: String | ||
|
||
property :mode, kind_of: String | ||
property :owner, kind_of: String | ||
property :group, kind_of: String | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
require 'spec_helper' | ||
require 'dnsimple' | ||
require_relative '../../libraries/provider_dnsimple_certificate' | ||
require_relative '../../libraries/resource_dnsimple_certificate' | ||
|
||
describe Chef::Provider::DnsimpleCertificate do | ||
before(:each) do | ||
@node = stub_node(platform: 'ubuntu', version: '14.04') | ||
@events = Chef::EventDispatch::Dispatcher.new | ||
@new_resource = Chef::Resource::DnsimpleCertificate.new('/path/to/certificate.crt') | ||
@run_context = Chef::RunContext.new(@node, {}, @events) | ||
@current_resource = Chef::Resource::DnsimpleCertificate.new('/path/to/certificate.crt') | ||
@provider = Chef::Provider::DnsimpleCertificate.new(@new_resource, @run_context) | ||
end | ||
|
||
describe '#install' do | ||
before(:each) do | ||
@new_resource.access_token('this_is_a_token') | ||
@provider.dnsimple_client = client | ||
@new_resource.certificate_common_name = certificate_data[:common_name] | ||
@new_resource.domain = 'example.com' | ||
@provider.current_resource = @current_resource | ||
end | ||
|
||
let(:client) { instance_double(Dnsimple::Client, identity: identity, certificates: certificates) } | ||
let(:identity) { instance_double(Dnsimple::Client::Identity, whoami: whoami_response) } | ||
let(:whoami_response) { instance_double(Dnsimple::Response, data: data) } | ||
let(:data) { instance_double(Dnsimple::Struct::Whoami, account: account) } | ||
let(:account) { instance_double(Dnsimple::Struct::Account, id: 1) } | ||
let(:certificates) { instance_double(Dnsimple::Client::Certificates, certificates: certificate_list, download_certificate: certificate_bundle_response, certificate_private_key: private_key_bundle_response) } | ||
let(:certificate_list) { instance_double(Dnsimple::CollectionResponse, data: [certificate]) } | ||
let(:certificate) { instance_double(Dnsimple::Struct::Certificate, id: certificate_data[:id], common_name: certificate_data[:common_name], expires_on: certificate_data[:expires_on], state: 'issued') } | ||
let(:certificate_bundle_response) { instance_double(Dnsimple::Response, data: certificate_bundle) } | ||
let(:certificate_bundle) { instance_double(Dnsimple::Struct::CertificateBundle, server: 'server-pem', chain: ['chain-pem']) } | ||
let(:private_key_bundle_response) { instance_double(Dnsimple::Response, data: private_key_bundle) } | ||
let(:private_key_bundle) { instance_double(Dnsimple::Struct::CertificateBundle, private_key: 'private-key-pem') } | ||
let(:certificate_data) do | ||
{ | ||
id: 1, | ||
common_name: 'www.example.com', | ||
expires_on: '2018-01-01', | ||
} | ||
end | ||
|
||
context 'if the certificate exists' do | ||
it 'installs the certificate' do | ||
@provider.run_action(:install) | ||
expect(@new_resource).to be_updated | ||
end | ||
end | ||
|
||
it 'implements the load_current_resource interface' do | ||
expect { @provider.load_current_resource }.to_not raise_exception | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
directory '/etc/apache2/ssl' do | ||
owner 'www-data' | ||
group 'www-data' | ||
recursive true | ||
end | ||
|
||
dnsimple_certificate '/etc/apache2/ssl' do | ||
certificate_common_name 'www.dnsimple.xyz' | ||
domain node['dnsimple']['test_domain'] | ||
access_token node['dnsimple']['access_token'] | ||
base_url node['dnsimple']['base_url'] | ||
mode '0755' | ||
owner 'web_admin' | ||
group 'web_admin' | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
# TODO: verify with x509_certificate | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are these for some other day in the future or this PR? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can be for now or later. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you know the command it's as easy as describe command('my cool ssl check command') do
its('stdout') { should match /totally right/ }
its('exit_code') { should eq 0 }
end There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One of the folks who helped my at the hackday indicated there are full blown test helpers for public and private keys in inspec that remove the need for match. At least that's what I understood. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So they are correct https://www.inspec.io/docs/reference/resources/x509_certificate/ |
||
describe file('/etc/apache2/ssl/dnsimple.xyz.crt') do | ||
its('content') { should match /-----BEGIN CERTIFICATE-----/ } | ||
end | ||
# TODO: verify with key_rsa | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are these for some other day in the future or this PR? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can be for now or later. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. https://www.inspec.io/docs/reference/resources/key_rsa/ seems simple to impliment I think and could be put in here |
||
describe file('/etc/apache2/ssl/dnsimple.xyz.key') do | ||
its('content') { should match /-----BEGIN RSA PRIVATE KEY-----/ } | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
name: install_certificate | ||
title: Install Certificate Test | ||
maintainer: Aetrion, LLC dba DNSimple | ||
copyright: Aetrion, LLC dba DNSimple | ||
copyright_email: ops@dnsimple.com | ||
license: Apache 2 license | ||
summary: Confirms the test cookbook to install a certificate via the sandbox API | ||
version: 1.0.0 | ||
supports: | ||
- os-family: linux | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we key on common name as part of the existence check, should that also be the name property?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's not the impression I got from @onlyhavecans - he indicated that since this is effectively a file resource that the file path should be the name property.