All Files
(100.0%
covered at
2.6
hits/line)
4 files in total.
116 relevant lines.
116 lines covered and
0 lines missed
-
# frozen_string_literal: true
-
-
1
module CertBot
-
# This class is used by Let's Encrypt to request a DNS record change in order for control validation
-
1
class DNSChange
-
1
require 'timeout'
-
1
require 'aws-sdk-route53'
-
-
1
def self.do_change(type, dnsname, validation, timeout: 300)
-
4
@route53 = Aws::Route53::Client.new(region: 'us-west-2')
-
4
zone_id = @route53.list_hosted_zones_by_name(dns_name: dnsname.split('.')[-2..-1].join('.')) \
-
.hosted_zones.first.id
-
-
4
change_resp = @route53.change_resource_record_sets(
-
change_batch: {
-
changes: [
-
{
-
action: type,
-
resource_record_set: {
-
name: "_acme-challenge.#{dnsname}",
-
type: 'TXT',
-
ttl: 60,
-
resource_records: [
-
{ value: "\"#{validation}\"" }
-
]
-
}
-
}
-
],
-
comment: 'automated certbot update'
-
},
-
hosted_zone_id: zone_id.split('/').last
-
)
-
-
4
wait_for_change(change_resp.change_info.id, timeout)
-
end
-
-
1
def self.wait_for_change(change_id, timeout)
-
4
change_resp = @route53.get_change(id: change_id)
-
4
Timeout.timeout(timeout) do
-
4
until change_resp.change_info.status == 'INSYNC'
-
3
sleep 10
-
3
change_resp = @route53.get_change(id: change_id)
-
end
-
end
-
4
change_id
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require 'spec_helper'
-
-
1
describe 'letsencryptaws::certbot' do
-
9
let(:chef_run) { ChefSpec::SoloRunner.new }
-
-
1
before do
-
8
allow(File).to receive(:blockdev?).with('/dev/xvdf').and_return(true)
-
8
stub_data_bag_item('testbag', 'testitem').and_return('p12_password' => 'foo')
-
8
stub_search('node', 'letsencryptaws_certs_*:*').and_return([])
-
8
stub_command('[ -d /mnt/letsencrypt/live ]').and_return(true)
-
8
stub_command('fsck.ext4 -n /dev/xvdf').and_return(false)
-
8
chef_run.node.default['ec2'] = {}
-
8
chef_run.node.normal['letsencryptaws']['sync_bucket'] = 'foo'
-
8
chef_run.node.normal['letsencryptaws']['certs']['test.example.com'] = []
-
8
chef_run.node.normal['letsencryptaws']['data_bag'] = 'testbag'
-
8
chef_run.node.normal['letsencryptaws']['data_bag_item'] = 'testitem'
-
8
chef_run.converge(described_recipe)
-
end
-
-
1
it 'updates apt repo' do
-
1
expect(chef_run).to periodic_apt_update('update')
-
end
-
-
1
it 'installs needed packages' do
-
1
expect(chef_run).to install_python_runtime('2.7')
-
1
expect(chef_run).to upgrade_python_package('cryptography')
-
1
expect(chef_run).to install_python_package('certbot')
-
1
expect(chef_run).to install_python_package('awscli')
-
1
expect(chef_run).to install_package('ruby')
-
1
expect(chef_run).to install_gem_package('aws-sdk-route53')
-
end
-
-
1
it 'creates directories' do
-
1
expect(chef_run).to create_directory('/mnt/letsencrypt')
-
1
expect(chef_run).to create_directory('/mnt/letsencrypt/scripts')
-
end
-
-
1
it 'creates filesystem and mounts' do
-
1
expect(chef_run).to run_execute('mkfs.ext4 /dev/xvdf')
-
1
expect(chef_run).to mount_mount('/mnt/letsencrypt')
-
end
-
-
1
it 'creates ruby scripts for certbot' do
-
1
expect(chef_run).to create_cookbook_file('/mnt/letsencrypt/scripts/certbot_route53_authenticator.rb')
-
1
expect(chef_run).to create_cookbook_file('/mnt/letsencrypt/scripts/certbot_route53_cleanup.rb')
-
1
expect(chef_run).to create_cookbook_file('/mnt/letsencrypt/scripts/dnschange.rb')
-
end
-
-
1
it 'fetches certificate for requested domain' do
-
1
expect(chef_run).to run_execute('get certificate for test.example.com')
-
end
-
-
1
it 'removes unrequested certificates' do
-
1
expect(chef_run).to run_ruby_block('remove unrequested certificates')
-
end
-
-
1
it 'syncs certificates to s3' do
-
1
expect(chef_run).to run_execute('sync certificates to s3')
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require 'ostruct'
-
1
require 'spec_helper'
-
-
1
describe 'letsencryptaws::default' do
-
9
let(:chef_run) { ChefSpec::SoloRunner.new }
-
-
1
before do
-
8
allow(Etc).to receive(:getpwnam).and_return(OpenStruct.new(uid: 0))
-
8
allow(Etc).to receive(:getgrnam).and_return(OpenStruct.new(gid: 0))
-
8
stub_data_bag_item('testbag', 'testitem').and_return('p12_password' => 'foo')
-
8
chef_run.node.normal['letsencryptaws']['certs']['test.example.com'] = []
-
8
chef_run.node.normal['letsencryptaws']['data_bag'] = 'testbag'
-
8
chef_run.node.normal['letsencryptaws']['data_bag_item'] = 'testitem'
-
8
chef_run.node.normal['letsencryptaws']['sync_bucket'] = 'foobucket'
-
8
chef_run.converge(described_recipe)
-
end
-
-
1
it 'creates directories' do
-
1
expect(chef_run).to create_directory('/etc/ssl/certs')
-
1
expect(chef_run).to create_directory('/etc/ssl/private')
-
end
-
-
1
it 'ensures ssl group' do
-
1
expect(chef_run).to create_group('ssl-cert')
-
end
-
-
1
it 'downloads default certificates' do
-
1
expect(chef_run).to create_remote_file_s3('/etc/ssl/certs/default.crt')
-
1
expect(chef_run).to create_remote_file_s3('/etc/ssl/private/default.key')
-
1
expect(chef_run).to create_remote_file_s3('/etc/ssl/certs/default.ca')
-
end
-
-
1
it 'does not update ca certificates' do
-
1
expect(chef_run).to nothing_execute('update-ca-certificates')
-
end
-
-
1
it 'downloads requested certificates' do
-
1
expect(chef_run).to create_remote_file_s3('/etc/ssl/certs/test.example.com.crt')
-
1
expect(chef_run).to create_remote_file_s3('/etc/ssl/private/test.example.com.key')
-
1
expect(chef_run).to create_remote_file_s3('/etc/ssl/certs/test.example.com.ca')
-
end
-
-
1
it 'composes requested certificates' do
-
1
expect(chef_run).to create_if_missing_file('/etc/ssl/certs/test.example.com.crt')
-
1
expect(chef_run).to create_if_missing_file('/etc/ssl/private/test.example.com.key')
-
1
expect(chef_run).to create_if_missing_file('/etc/ssl/certs/test.example.com.ca')
-
1
expect(chef_run).to create_file('/etc/ssl/certs/test.example.com.crt-chain')
-
end
-
-
1
it 'generates pkcs12 keyring' do
-
1
expect(chef_run).to nothing_execute('generate pkcs12 store for test.example.com')
-
1
expect(chef_run.execute('generate pkcs12 store for test.example.com')).to \
-
subscribe_to('remote_file_s3[/etc/ssl/certs/test.example.com.crt]').on(:run).delayed
-
1
expect(chef_run).to write_log('pkcs12 store needs generated for test.example.com')
-
1
expect(chef_run.log('pkcs12 store needs generated for test.example.com')).to \
-
notify('execute[generate pkcs12 store for test.example.com]').to(:run).immediately
-
1
expect(chef_run).to create_file('/etc/ssl/private/test.example.com.p12')
-
end
-
-
1
context 'when testing' do
-
1
before do
-
1
chef_run.node.normal['letsencryptaws']['test_certs'] = true
-
1
chef_run.converge(described_recipe)
-
end
-
-
1
it 'updates ca certificates' do
-
1
expect(chef_run).to create_remote_file('/usr/local/share/ca-certificates/fakeroot.crt')
-
1
expect(chef_run.remote_file('/usr/local/share/ca-certificates/fakeroot.crt')).to \
-
notify('execute[update-ca-certificates]').to(:run).immediately
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require 'spec_helper'
-
-
1
describe 'letsencryptaws::import_keystore' do
-
3
let(:chef_run) { ChefSpec::SoloRunner.new }
-
-
1
before do
-
2
stub_data_bag_item('testbag', 'testitem').and_return(
-
'p12_password' => 'foo',
-
'keystore_passwords' => { 'default' => 'bar' }
-
)
-
2
chef_run.node.normal['letsencryptaws']['certs']['test.example.com'] = []
-
2
chef_run.node.normal['letsencryptaws']['data_bag'] = 'testbag'
-
2
chef_run.node.normal['letsencryptaws']['data_bag_item'] = 'testitem'
-
2
chef_run.node.normal['letsencryptaws']['import_keystore']['/tmp/foo'] = ['test.example.com']
-
2
chef_run.converge(described_recipe)
-
end
-
-
1
it 'installs java' do
-
1
expect(chef_run).to install_package('openjdk-8-jre')
-
end
-
-
# Subscribes can't be tested because the target resource is outside this recipe
-
1
it 'imports pkcs12 keyring into keystore' do
-
1
expect(chef_run).to nothing_execute('import test.example.com into /tmp/foo')
-
end
-
end