diff --git a/add-host-workflow.md b/add-host-workflow.md new file mode 100644 index 0000000..1a9aa36 --- /dev/null +++ b/add-host-workflow.md @@ -0,0 +1,37 @@ +# Workflow for adding a LXD host + +## User input + + * A symbolic name + * The LDX host base URL + * The password for certificate addition + +## workflow + + * Check our current client key. + * Recreate client key if we find no active valid key + * This also involves deactivating the old one and activate the new one. + * Get API version of the LXD host. (unauth) + * Bail out if not supported ... currently (only 1.0 is supported) + * Get Server info / well, this might also be done in the previous task. + * If unathorized add cert with password. + * I guess thats it. + +## stored + + * The new cert eventually + * The name and url of the LXD host + +# Workflow accessing LXD host + +## input + + * The name of the LXD host + +## workflow + + * Check our current client cert. + * If close to expiration create a new one and send it to + LXD host with the old one. + * If behind expiration ask for password + * Connect to LXD host using the current active cert. diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index d83690e..bdc68af 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -2,4 +2,8 @@ class ApplicationController < ActionController::Base # Prevent CSRF attacks by raising an exception. # For APIs, you may want to use :null_session instead. protect_from_forgery with: :exception + + def check_cert + end end +# vim: set et ts=2 sw=2: diff --git a/app/controllers/dashboard_controller.rb b/app/controllers/dashboard_controller.rb index cad83ba..97b0de5 100644 --- a/app/controllers/dashboard_controller.rb +++ b/app/controllers/dashboard_controller.rb @@ -2,11 +2,14 @@ class DashboardController < ApplicationController def index @lxd_host = LxdHost.find(1) @cert = Certificate.find(1) - @lxd = Lxd::Server.by_host @lxd_host, @cert + @api = Lxd::API.get @lxd_host, @cert + @lxd_config = Lxd::Config.get @api - if @lxd.auth == 'untrusted' - Lxd::Certificate.new.save(@lxd_host, @cert) - @lxd = Lxd::Server.by_host @lxd_host, @cert + if @lxd_config.auth == 'untrusted' + # Here the controller has to ask for the password + cert = Lxd::Certificate.new api: @api + cert.save 'xxxxxxxxxx' + @lxd_config = Lxd::Config.get @api end end end diff --git a/app/models/certificate.rb b/app/models/certificate.rb index aa8efed..5e7b2d0 100644 --- a/app/models/certificate.rb +++ b/app/models/certificate.rb @@ -3,7 +3,7 @@ require 'digest/md5' class Certificate < ActiveRecord::Base def key - OpenSSL::PKey::RSA.new read_attribute(:key) if read_attribute(:key) + OpenSSL::PKey::EC.new read_attribute(:key) if read_attribute(:key) end def cert @@ -11,11 +11,16 @@ class Certificate < ActiveRecord::Base end def key_fpr - Digest::SHA1.hexdigest(key.to_der).upcase + Digest::SHA256.hexdigest(key.to_der).upcase end def cert_fpr - Digest::SHA1.hexdigest(cert.to_der).upcase + Digest::SHA256.hexdigest(cert.to_der).upcase + end + + private + + def _key end end # vim: set et ts=2 sw=2: diff --git a/app/models/lxd/api.rb b/app/models/lxd/api.rb new file mode 100644 index 0000000..60ca59d --- /dev/null +++ b/app/models/lxd/api.rb @@ -0,0 +1,45 @@ +module Lxd::API + def self.get host, certificate + uri = URI.parse host.uri + con = Net::HTTP.new uri.host, uri.port + con.use_ssl = true + con.cert = OpenSSL::X509::Certificate.new certificate.cert + con.key = OpenSSL::PKey::RSA.new certificate.key + con.verify_mode = OpenSSL::SSL::VERIFY_NONE + + resp = self.call con, Net::HTTP::Get.new('/') + raise "unsupported api version" unless resp['metadata'].include? '/1.0' + Lxd::API::V1_0.new con + end + + def self.call con, req + resp = con.request req + raise "request failure: " + resp.code unless resp.code != 200 + JSON.parse resp.body + end + + def initialize con + @con = con + end + + def call req + handle_response(Lxd::API.call @con, req) + end + + def get uri + call Net::HTTP::Get.new uri + end + + def put uri, data={} + request = Net::HTTP::Put.new uri + request.body = data.to_json + call request + end + + def post uri, data={} + request = Net::HTTP::Post.new uri + request.body = data.to_json + call request + end +end +# vim: set ts=2 sw=2: diff --git a/app/models/lxd/api/v1_0.rb b/app/models/lxd/api/v1_0.rb new file mode 100644 index 0000000..0458a9a --- /dev/null +++ b/app/models/lxd/api/v1_0.rb @@ -0,0 +1,54 @@ +class Lxd::API::V1_0 + include Lxd::API + + def config + get '/1.0' + end + + def config= config={} + put '/1.0', config: config + end + + def certificates + get '/1.0/certificates'.map { |uri| + { + :uri => uri, + :cert => get(uri) + } + } + end + + def add_certificate cert={} + # TODO validate hash + post '/1.0/certificates', cert + end + + def handle_response resp + """ + 100 Operation created + 101 Started + 102 Stopped + 103 Running + 104 Cancelling + 105 Pending + 106 Starting + 107 Stopping + 108 Aborting + 109 Freezing + 110 Frozen + 111 Thawed + 200 Success + 400 Failure + 401 Cancelled + + + 100 to 199: resource state (started, stopped, ready, ...) + 200 to 399: positive action result + 400 to 599: negative action result + 600 to 999: future use + """ + raise "api error" if [400..500].include? resp['error_code'] + resp['metadata'] + end +end +# vim: set ts=2 sw=2: diff --git a/app/models/lxd/certificate.rb b/app/models/lxd/certificate.rb index 6f1748f..da6b852 100644 --- a/app/models/lxd/certificate.rb +++ b/app/models/lxd/certificate.rb @@ -1,25 +1,22 @@ class Lxd::Certificate include ActiveModel::Model - def save(host, certificate) - all = Array.new; + attr_accessor :api, :type, :certificate, :fingerprint - uri = URI.parse(host.uri + '/1.0/certificates') - http = Net::HTTP.new(uri.host, uri.port) - http.use_ssl = true - http.cert = OpenSSL::X509::Certificate.new(certificate.cert) - http.key = OpenSSL::PKey::RSA.new(certificate.key) - http.verify_mode = OpenSSL::SSL::VERIFY_NONE + def self.all api + api.certificates.map { |cert| + Lxd::Certificate.new({api: api}.merge cert) + } + end - request = Net::HTTP::Post.new(uri.request_uri) - request.body = { - :type => 'client', - :name => 'foo', - :password => '[where to get this from....]' - }.to_json - response = http.request(request) + def add password=nil, name='lex-deeit' + data = Hash.new + data[:type] = @type if @type else 'client' + data[:name] = name + data[:password] = password if password + data[:certificate] = @certificate if @certificate - response.code + @api.add_certificate data end end # vim: set ts=2 sw=2: diff --git a/app/models/lxd/config.rb b/app/models/lxd/config.rb new file mode 100644 index 0000000..9d6fc43 --- /dev/null +++ b/app/models/lxd/config.rb @@ -0,0 +1,15 @@ +class Lxd::Config + include ActiveModel::Model + + attr_accessor :api, :api_extensions, :api_status, :api_version, :auth, + :config, :environment, :public + + def self.get api + Lxd::Config.new({api: api}.merge api.config) + end + + def save + @api.config = @config + end +end +# vim: set ts=2 sw=2: diff --git a/app/models/lxd/connection.rb b/app/models/lxd/connection.rb new file mode 100644 index 0000000..906bdbd --- /dev/null +++ b/app/models/lxd/connection.rb @@ -0,0 +1,50 @@ +class Lxd::Connection + attr_reader :con, :host, :port + + """ + /1.0 + /1.0/certificates + /1.0/certificates/ + /1.0/containers + /1.0/containers/ + /1.0/containers//exec + /1.0/containers//files + /1.0/containers//snapshots + /1.0/containers//snapshots/ + /1.0/containers//state + /1.0/containers//logs + /1.0/containers//logs/ + /1.0/events + /1.0/images + /1.0/images/ + /1.0/images//export + /1.0/images/aliases + /1.0/images/aliases/ + /1.0/networks + /1.0/networks/ + /1.0/operations + /1.0/operations/ + /1.0/operations//wait + /1.0/operations//websocket + /1.0/profiles + /1.0/profiles/ + """ + + def initialize host, certificate + uri = URI.parse host.uri + @host = uri.host + @port = uri.port + @con = Net::HTTP.new @host, @port + @con.use_ssl = true + @con.cert = OpenSSL::X509::Certificate.new certificate.cert + @con.key = OpenSSL::PKey::EC.new certificate.key + @con.verify_mode = OpenSSL::SSL:VERIFY_NONE + end + + def call + if not @api_version + @con.request(Net::HTTP::Get.new '/') + versions + end + end +# vim: set et ts=2 sw=2: diff --git a/app/models/lxd/server.rb b/app/models/lxd/server.rb deleted file mode 100644 index 6ce48af..0000000 --- a/app/models/lxd/server.rb +++ /dev/null @@ -1,23 +0,0 @@ -class Lxd::Server - include ActiveModel::Model - - attr_accessor :api_extensions, :api_status, :api_version, :auth, :config, - :environment, :public - - def self.by_host(host, certificate) - all = Array.new; - - uri = URI.parse(host.uri + '/1.0') - http = Net::HTTP.new(uri.host, uri.port) - http.use_ssl = true - http.cert = OpenSSL::X509::Certificate.new(certificate.cert) - http.key = OpenSSL::PKey::RSA.new(certificate.key) - http.verify_mode = OpenSSL::SSL::VERIFY_NONE - - request = Net::HTTP::Get.new(uri.request_uri) - response = http.request(request) - - Lxd::Server.new(JSON.parse(response.body)['metadata']) - end -end -# vim: set ts=2 sw=2: diff --git a/app/views/dashboard/index.html.erb b/app/views/dashboard/index.html.erb index 4cfea7e..1899a62 100644 --- a/app/views/dashboard/index.html.erb +++ b/app/views/dashboard/index.html.erb @@ -1,11 +1,11 @@

Dashboard#index

<%= @lxd_host.class %>

<%= @cert.class %>

-

<%= @lxd.class %>

-

<%= @lxd.api_extensions.inspect %>

-

<%= @lxd.api_status %>

-

<%= @lxd.api_version %>

-

<%= @lxd.auth %>

-

<%= @lxd.config.inspect %>

-

<%= @lxd.environment.inspect %>

-

<%= @lxd.public %>

+

<%= @lxd_config.class %>

+

<%= @lxd_config.api_extensions.inspect %>

+

<%= @lxd_config.api_status %>

+

<%= @lxd_config.api_version %>

+

<%= @lxd_config.auth %>

+

<%= @lxd_config.config.inspect %>

+

<%= @lxd_config.environment.inspect %>

+

<%= @lxd_config.public %>