Commit 0b306511dc3b71aa93a7a265c61bb91694459f4e

Authored by Georg Hopp
1 parent 862ee9fa

handle certificates within hosts automatically

... ... @@ -3,34 +3,5 @@ class ApplicationController < ActionController::Base
3 3 # For APIs, you may want to use :null_session instead.
4 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 6 end
36 7 # vim: set et ts=2 sw=2:
... ...
1 1 class DashboardController < ApplicationController
2 2 def index
3   - check_cert
4 3 @hosts = Host.all
5 4
6 5 @hosts.map { |host|
7   - host.cert = @cert
8   - if host.config.auth == 'untrusted'
  6 + if host.lxd_config.auth == 'untrusted'
9 7 session[:return_to] = request.env["REQUEST_URI"]
10 8 redirect_to controller: 'hosts', action: 'auth', id: host.id
11 9 return
12 10 end
13 11 }
14   -
15   - @certificates = Lxd::Certificate.all @hosts.first.api
16 12 end
17 13 end
18 14 # vim: set et ts=2 sw=2:
... ...
... ... @@ -28,8 +28,7 @@ class HostsController < ApplicationController
28 28
29 29 # PATCH/PUT /hosts/1/add_key
30 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 32 redirect_to session.delete(:return_to)
34 33 end
35 34
... ... @@ -76,9 +75,7 @@ class HostsController < ApplicationController
76 75 private
77 76 # Use callbacks to share common setup or constraints between actions.
78 77 def set_host
79   - check_cert
80 78 @host = Host.find(params[:id])
81   - @host.cert = @cert
82 79 end
83 80
84 81 # Never trust parameters from the scary internet, only allow the white list through.
... ...
... ... @@ -2,28 +2,49 @@ require "openssl"
2 2 require 'digest/md5'
3 3
4 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 16 def self.create(old=nil)
6 17 key = if old then old.key else OpenSSL::PKey::RSA.new 4096 end
7 18 cert = OpenSSL::X509::Certificate.new
8 19 cert.version = if old then old.cert.version else 2 end
9 20 cert.serial = if old then old.cert.serial+1 else 0 end
10 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 24 cert.public_key = key.public_key
14 25 cert.subject =
15 26 OpenSSL::X509::Name.parse(
16 27 'CN=lex-deeit/' + Rails.configuration.x.certificate['x509_base'])
17 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 32 end
20 33
21 34 def update
22 35 self.active = false
23 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 48 end
28 49
29 50 def key
... ... @@ -43,5 +64,13 @@ class Certificate < ActiveRecord::Base
43 64 def cert_fpr
44 65 Digest::SHA256.hexdigest(cert.to_der).upcase
45 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 75 end
47 76 # vim: set et ts=2 sw=2:
... ...
1 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 24 end
9 25
10   - def config
  26 + def lxd_config
11 27 Lxd::Config.get api
12 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 42 end
14 43 # vim: ts=2 sw=2:
... ...
1 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 5 con = Net::HTTP.new uri.host, uri.port ? uri.port : 8443
8 6 con.use_ssl = true
9 7 con.cert = OpenSSL::X509::Certificate.new certificate.cert
... ...
1 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 %>&nbsp;
  4 +Serial: <%= cert.cert.serial %></p>
  5 +<% end -%>
  6 +<hr/>
4 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 11 <% end -%>
7   -<% @certificates.each do |cert| -%>
8   -<p><%= cert.fingerprint %></p>
9 12 <% end -%>
... ...
Please register or login to post a comment