Commit 0b306511dc3b71aa93a7a265c61bb91694459f4e
1 parent
862ee9fa
handle certificates within hosts automatically
Showing
7 changed files
with
83 additions
and
60 deletions
@@ -3,34 +3,5 @@ class ApplicationController < ActionController::Base | @@ -3,34 +3,5 @@ class ApplicationController < ActionController::Base | ||
3 | # For APIs, you may want to use :null_session instead. | 3 | # For APIs, you may want to use :null_session instead. |
4 | protect_from_forgery with: :exception | 4 | protect_from_forgery with: :exception |
5 | 5 | ||
6 | - def check_cert | ||
7 | - @cert = Certificate.find_by active: true | ||
8 | - unless @cert | ||
9 | - @cert = Certificate.create | ||
10 | - @cert.save | ||
11 | - end | ||
12 | - | ||
13 | - # update cert on all hosts if close to end. | ||
14 | - # This will never fail as lxd is very lax with its certificates. | ||
15 | - # It accepts certificates even behind the not_after date. | ||
16 | - # As a result a password is only required when a new host is added | ||
17 | - # or we remove the current cert completely. | ||
18 | - if (@cert.cert.not_after - 1.day + 300) < Time.now | ||
19 | - @new_cert = @cert.update | ||
20 | - Host.all.each { |host| | ||
21 | - host.cert = @cert | ||
22 | - # add new certificate | ||
23 | - cert = Lxd::Certificate.new( | ||
24 | - api: host.api, | ||
25 | - certificate: @new_cert.cert.to_pem.split("\n")[1...-1].join) | ||
26 | - cert.add | ||
27 | - # delete old certificate / we don't want this to be used | ||
28 | - # any more. | ||
29 | - Lxd::Certificate.new( | ||
30 | - api: host.api, fingerprint: @cert.cert_fpr).delete | ||
31 | - } | ||
32 | - @cert = @new_cert | ||
33 | - end | ||
34 | - end | ||
35 | end | 6 | end |
36 | # vim: set et ts=2 sw=2: | 7 | # vim: set et ts=2 sw=2: |
1 | class DashboardController < ApplicationController | 1 | class DashboardController < ApplicationController |
2 | def index | 2 | def index |
3 | - check_cert | ||
4 | @hosts = Host.all | 3 | @hosts = Host.all |
5 | 4 | ||
6 | @hosts.map { |host| | 5 | @hosts.map { |host| |
7 | - host.cert = @cert | ||
8 | - if host.config.auth == 'untrusted' | 6 | + if host.lxd_config.auth == 'untrusted' |
9 | session[:return_to] = request.env["REQUEST_URI"] | 7 | session[:return_to] = request.env["REQUEST_URI"] |
10 | redirect_to controller: 'hosts', action: 'auth', id: host.id | 8 | redirect_to controller: 'hosts', action: 'auth', id: host.id |
11 | return | 9 | return |
12 | end | 10 | end |
13 | } | 11 | } |
14 | - | ||
15 | - @certificates = Lxd::Certificate.all @hosts.first.api | ||
16 | end | 12 | end |
17 | end | 13 | end |
18 | # vim: set et ts=2 sw=2: | 14 | # vim: set et ts=2 sw=2: |
@@ -28,8 +28,7 @@ class HostsController < ApplicationController | @@ -28,8 +28,7 @@ class HostsController < ApplicationController | ||
28 | 28 | ||
29 | # PATCH/PUT /hosts/1/add_key | 29 | # PATCH/PUT /hosts/1/add_key |
30 | def add_key | 30 | def add_key |
31 | - cert = Lxd::Certificate.new api: @host.api | ||
32 | - cert.add params[:hosts][:password] | 31 | + @host.lxd_authenticate params[:hosts][:password] |
33 | redirect_to session.delete(:return_to) | 32 | redirect_to session.delete(:return_to) |
34 | end | 33 | end |
35 | 34 | ||
@@ -76,9 +75,7 @@ class HostsController < ApplicationController | @@ -76,9 +75,7 @@ class HostsController < ApplicationController | ||
76 | private | 75 | private |
77 | # Use callbacks to share common setup or constraints between actions. | 76 | # Use callbacks to share common setup or constraints between actions. |
78 | def set_host | 77 | def set_host |
79 | - check_cert | ||
80 | @host = Host.find(params[:id]) | 78 | @host = Host.find(params[:id]) |
81 | - @host.cert = @cert | ||
82 | end | 79 | end |
83 | 80 | ||
84 | # Never trust parameters from the scary internet, only allow the white list through. | 81 | # Never trust parameters from the scary internet, only allow the white list through. |
@@ -2,28 +2,49 @@ require "openssl" | @@ -2,28 +2,49 @@ require "openssl" | ||
2 | require 'digest/md5' | 2 | require 'digest/md5' |
3 | 3 | ||
4 | class Certificate < ActiveRecord::Base | 4 | class Certificate < ActiveRecord::Base |
5 | + private_class_method :new | ||
6 | + | ||
7 | + def self.get | ||
8 | + @@cert ||= find_by active: true | ||
9 | + @@cert ||= create | ||
10 | + if @@cert.is_expired? | ||
11 | + @@cert = @@cert.update | ||
12 | + end | ||
13 | + @@cert | ||
14 | + end | ||
15 | + | ||
5 | def self.create(old=nil) | 16 | def self.create(old=nil) |
6 | key = if old then old.key else OpenSSL::PKey::RSA.new 4096 end | 17 | key = if old then old.key else OpenSSL::PKey::RSA.new 4096 end |
7 | cert = OpenSSL::X509::Certificate.new | 18 | cert = OpenSSL::X509::Certificate.new |
8 | cert.version = if old then old.cert.version else 2 end | 19 | cert.version = if old then old.cert.version else 2 end |
9 | cert.serial = if old then old.cert.serial+1 else 0 end | 20 | cert.serial = if old then old.cert.serial+1 else 0 end |
10 | cert.not_before = Time.now | 21 | cert.not_before = Time.now |
11 | - #cert.not_after = Time.now + 1.year | ||
12 | - cert.not_after = Time.now + 1.day | 22 | + #cert.not_after = Time.now + 3.months |
23 | + cert.not_after = Time.now + 1.day + 5.minutes | ||
13 | cert.public_key = key.public_key | 24 | cert.public_key = key.public_key |
14 | cert.subject = | 25 | cert.subject = |
15 | OpenSSL::X509::Name.parse( | 26 | OpenSSL::X509::Name.parse( |
16 | 'CN=lex-deeit/' + Rails.configuration.x.certificate['x509_base']) | 27 | 'CN=lex-deeit/' + Rails.configuration.x.certificate['x509_base']) |
17 | cert.sign key, OpenSSL::Digest::SHA256.new | 28 | cert.sign key, OpenSSL::Digest::SHA256.new |
18 | - Certificate.new key: key.to_pem, cert: cert.to_pem, active: true | 29 | + certificate = new(key: key.to_pem, cert: cert.to_pem, active: true) |
30 | + certificate.save | ||
31 | + certificate | ||
19 | end | 32 | end |
20 | 33 | ||
21 | def update | 34 | def update |
22 | self.active = false | 35 | self.active = false |
23 | self.save | 36 | self.save |
24 | - cert = Certificate.create(self) | ||
25 | - cert.save | ||
26 | - cert | 37 | + Certificate.create(self) |
38 | + end | ||
39 | + | ||
40 | + def is_expired? | ||
41 | + # The cert is already expired | ||
42 | + self.cert.not_after < Time.now | ||
43 | + end | ||
44 | + | ||
45 | + def expires_soon? | ||
46 | + # The cert will expire within the next day or is alreay expired | ||
47 | + (self.cert.not_after - 1.day) < Time.now | ||
27 | end | 48 | end |
28 | 49 | ||
29 | def key | 50 | def key |
@@ -43,5 +64,13 @@ class Certificate < ActiveRecord::Base | @@ -43,5 +64,13 @@ class Certificate < ActiveRecord::Base | ||
43 | def cert_fpr | 64 | def cert_fpr |
44 | Digest::SHA256.hexdigest(cert.to_der).upcase | 65 | Digest::SHA256.hexdigest(cert.to_der).upcase |
45 | end | 66 | end |
67 | + | ||
68 | + def to_s | ||
69 | + cert.to_pem.split("\n")[1...-1].join | ||
70 | + end | ||
71 | + | ||
72 | + def to_str | ||
73 | + to_s | ||
74 | + end | ||
46 | end | 75 | end |
47 | # vim: set et ts=2 sw=2: | 76 | # vim: set et ts=2 sw=2: |
1 | class Host < ActiveRecord::Base | 1 | class Host < ActiveRecord::Base |
2 | - def cert=(cert) | ||
3 | - @cert = cert | ||
4 | - end | 2 | + belongs_to :certificate |
5 | 3 | ||
6 | - def api | ||
7 | - Lxd::API.get self, @cert | 4 | + def certificate |
5 | + # ensure that we always use a current, working non expired certificate. | ||
6 | + case | ||
7 | + when super.nil? | ||
8 | + self.certificate_id = Certificate.get.id | ||
9 | + self.save | ||
10 | + super(true) | ||
11 | + when super.expires_soon? | ||
12 | + old = super | ||
13 | + new = Certificate.get.update | ||
14 | + Lxd::Certificate.new(api: api(old), certificate: new.to_s).add | ||
15 | + self.certificate_id = new.id | ||
16 | + self.save | ||
17 | + @api = nil # enforce new api to get the new certificate used. | ||
18 | + # finally remove the old certificate from lxd | ||
19 | + Lxd::Certificate.new(api: api(new), fingerprint: old.cert_fpr).delete | ||
20 | + super(true) | ||
21 | + else | ||
22 | + super | ||
23 | + end | ||
8 | end | 24 | end |
9 | 25 | ||
10 | - def config | 26 | + def lxd_config |
11 | Lxd::Config.get api | 27 | Lxd::Config.get api |
12 | end | 28 | end |
29 | + | ||
30 | + def lxd_certificates | ||
31 | + Lxd::Certificate.all api | ||
32 | + end | ||
33 | + | ||
34 | + def lxd_authenticate password | ||
35 | + Lxd::Certificate.new(api: api).add password | ||
36 | + end | ||
37 | + | ||
38 | + private | ||
39 | + def api certificate = nil | ||
40 | + @api ||= Lxd::API.get self, certificate | ||
41 | + end | ||
13 | end | 42 | end |
14 | # vim: ts=2 sw=2: | 43 | # vim: ts=2 sw=2: |
1 | module Lxd::API | 1 | module Lxd::API |
2 | - def self.get host, certificate | ||
3 | - begin | ||
4 | - uri = URI.parse host.uri | ||
5 | - rescue | ||
6 | - end | 2 | + def self.get host, certificate = nil |
3 | + certificate ||= host.certificate | ||
4 | + uri = URI.parse host.uri | ||
7 | con = Net::HTTP.new uri.host, uri.port ? uri.port : 8443 | 5 | con = Net::HTTP.new uri.host, uri.port ? uri.port : 8443 |
8 | con.use_ssl = true | 6 | con.use_ssl = true |
9 | con.cert = OpenSSL::X509::Certificate.new certificate.cert | 7 | con.cert = OpenSSL::X509::Certificate.new certificate.cert |
1 | <h1>Dashboard#index</h1> | 1 | <h1>Dashboard#index</h1> |
2 | -<p><%= @cert.cert_fpr %></p> | ||
3 | -<p>Serial: <%= @cert.cert.serial %></p> | 2 | +<% Certificate.all.each do |cert| -%> |
3 | +<p>Fingerprint: <%= cert.cert_fpr %> | ||
4 | +Serial: <%= cert.cert.serial %></p> | ||
5 | +<% end -%> | ||
6 | +<hr/> | ||
4 | <% @hosts.each do |host| -%> | 7 | <% @hosts.each do |host| -%> |
5 | -<p><%= host.config.inspect %></p> | 8 | +<p><%= host.lxd_config.inspect %></p> |
9 | +<% host.lxd_certificates.each do |certificate| -%> | ||
10 | +<p><%= certificate.fingerprint %></p> | ||
6 | <% end -%> | 11 | <% end -%> |
7 | -<% @certificates.each do |cert| -%> | ||
8 | -<p><%= cert.fingerprint %></p> | ||
9 | <% end -%> | 12 | <% end -%> |
Please
register
or
login
to post a comment