Commit 0b306511dc3b71aa93a7a265c61bb91694459f4e

Authored by Georg Hopp
1 parent 862ee9fa

handle certificates within hosts automatically

@@ -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 %>&nbsp;
  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