From: František Dvořák Date: Thu, 9 Jun 2016 16:58:52 +0000 (+0200) Subject: Experiments with switching of identities. X-Git-Url: http://scientific.zcu.cz/git/?a=commitdiff_plain;h=2d610e9591e3b1cc409e6ad8a666e61d9451e04e;p=now.git Experiments with switching of identities. --- diff --git a/.rubocop.yml b/.rubocop.yml index e272ac4..f348ad9 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -15,6 +15,11 @@ AllCops: Metrics: Enabled: false +# Use the return of the conditional for variable assignment and comparison +# (wtf) +Style/ConditionalAssignment: + Enabled: false + # Favor format over sprintf # (easy to read) Style/FormatString: diff --git a/README.md b/README.md index 70d5fdf..06b8c4b 100644 --- a/README.md +++ b/README.md @@ -3,14 +3,39 @@ ## Overview This is the component to extend OpenNebula network orchestration capabilities. -## Launch -``` -rackup -``` +## Admin Usage + +At OpenNebula host: + + oneuser create nowadmin --driver server_cipher 'the-best-strongest-password-ever' + oneuser chgrp nowadmin oneadmin + +At NOW host (configration `/etc/now.yaml`): + + opennebula: + admin_user: 'nowadmin' + admin_password: 'the-best-strongest-password-ever' + endpoint: http://nebula.example.com:2633/RPC2 + +Launch NOW: + + rackup + +## Usage + +List networks example: + + curl http://now.example.com:9292/list?user=myuser ## Development -``` -make run -``` -It calls `rackup` through bundler. +Using `make`: + + make check + make run + +Or directly: + + bundle install + bundle exec rubocop + bundle exec rackup diff --git a/lib/api.rb b/lib/api.rb index 9396775..7e5c62f 100644 --- a/lib/api.rb +++ b/lib/api.rb @@ -22,6 +22,18 @@ module Now before do # to sinatra request logger point to proper object env['rack.logger'] = $logger + + switch_user(params['user']) + end + + helpers do + def switch_user(user) + if user.nil? + @nebula.switch_server() + else + @nebula.switch_user(user) + end + end end get '/' do @@ -35,6 +47,7 @@ module Now networks = @nebula.list_networks JSON.pretty_generate(networks) rescue NowError => e + logger.error "[HTTP #{e.code}] #{e.message}" halt e.code, e.message end end @@ -45,6 +58,7 @@ module Now network = @nebula.get(params['id']) JSON.pretty_generate(network) rescue NowError => e + logger.error "[HTTP #{e.code}] #{e.message}" halt e.code, e.message end end diff --git a/lib/nebula.rb b/lib/nebula.rb index 284a6a7..d0251cb 100644 --- a/lib/nebula.rb +++ b/lib/nebula.rb @@ -20,44 +20,71 @@ public def deep_merge(second) end module Now + + EXPIRE_LENGTH = 8 * 60 * 60 + # NOW core class for communication with OpenNebula class Nebula - attr_accessor :config, :logger, :client + attr_accessor :logger + @ctx = nil + @server_ctx = nil + @user_ctx = nil def load_config(file) c = YAML.load_file(file) - @logger.debug "Config file '#{file}' loaded" + logger.debug "Config file '#{file}' loaded" return c rescue Errno::ENOENT - @logger.debug "Config file '#{file}' not found" + logger.debug "Config file '#{file}' not found" return {} end def one_connect(url, credentials) - @logger.debug "Connecting to #{url}..." - @client = OpenNebula::Client.new(credentials, url) + logger.debug "Connecting to #{url} ..." + return OpenNebula::Client.new(credentials, url) + end + + def switch_user(user) + admin_user = @config['opennebula']['admin_user'] + admin_password = @config['opennebula']['admin_password'] + logger.debug "Authentication from #{admin_user} to #{user}" + + server_auth = ServerCipherAuth.new(admin_user, admin_password) + expiration = Time.now.to_i + EXPIRE_LENGTH + user_token = server_auth.login_token(expiration, user) + + @user_ctx = one_connect(@url, user_token) + @ctx = @user_ctx + end + + def switch_server() + admin_user = @config['opennebula']['admin_user'] + admin_password = @config['opennebula']['admin_password'] + logger.debug "Authentication to #{admin_user}" + + direct_token = "#{admin_user}:#{admin_password}" + @server_ctx = one_connect(@url, direct_token) + @ctx = @server_ctx end def initialize() @logger = $logger - @logger.info "Starting Network Orchestrator Wrapper (NOW #{VERSION})" + logger.info "Starting Network Orchestrator Wrapper (NOW #{VERSION})" @config = {} c = load_config(::File.expand_path('../../etc/now.yaml', __FILE__)) @config = @config.deep_merge(c) - #@logger.debug "Configuration: #{@config}" + #logger.debug "Configuration: #{@config}" c = load_config('/etc/now.yaml') @config = @config.deep_merge(c) - #@logger.debug "Configuration: #{@config}" + #logger.debug "Configuration: #{@config}" - url = @config['opennebula']['endpoint'] - credentials = "#{@config['opennebula']['admin_user']}:#{@config['opennebula']['admin_password']}" - one_connect(url, credentials) + @url = @config['opennebula']['endpoint'] end def list_networks() - vn_pool = OpenNebula::VirtualNetworkPool.new(client, -1) + vn_pool = OpenNebula::VirtualNetworkPool.new(@ctx, -1) check(vn_pool.info) networks = [] @@ -73,12 +100,12 @@ module Now def get(network_id) vn_generic = OpenNebula::VirtualNetwork.build_xml(network_id) - vn = OpenNebula::VirtualNetwork.new(vn_generic, @client) + vn = OpenNebula::VirtualNetwork.new(vn_generic, @ctx) check(vn.info) id = vn.id title = vn.name - @logger.debug "OpenNebula get(#{network_id}) ==> #{id}, #{title}" + logger.debug "OpenNebula get(#{network_id}) ==> #{id}, #{title}" network = Network.new(id: id, title: title) return network.to_hash diff --git a/lib/server_cipher_auth.rb b/lib/server_cipher_auth.rb new file mode 100644 index 0000000..ba2e796 --- /dev/null +++ b/lib/server_cipher_auth.rb @@ -0,0 +1,91 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2013, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + +require 'openssl' +require 'digest/sha1' + +require 'base64' +require 'fileutils' + +module Now + # Server authentication class. This method can be used by OpenNebula services + # to let access authenticated users by other means. It is based on OpenSSL + # symmetric ciphers + class ServerCipherAuth + ########################################################################### + # Constants with paths to relevant files and defaults + ########################################################################### + CIPHER = 'aes-256-cbc' + + def initialize(srv_user, srv_passwd) + @srv_user = srv_user + @srv_passwd = srv_passwd + + if srv_passwd.nil? || srv_passwd.empty? + @key = '' + else + @key = ::Digest::SHA1.hexdigest(@srv_passwd) + end + + @cipher = ::OpenSSL::Cipher::Cipher.new(CIPHER) + end + + # Creates a ServerCipher for client usage + def self.new_client(srv_user, srv_passwd) + new(srv_user, srv_passwd) + end + + # Generates a login token in the form: + # - server_user:target_user:time_expires + # The token is then encrypted with the contents of one_auth + def login_token(expire, target_user = nil) + target_user ||= @srv_user + token_txt = "#{@srv_user}:#{target_user}:#{expire}" + + token = encrypt(token_txt) + token64 = ::Base64.encode64(token).strip.delete("\n") + + "#{@srv_user}:#{target_user}:#{token64}" + end + + # Returns a valid password string to create a user using this auth driver + def password + @srv_passwd + end + + private + + def encrypt(data) + @cipher.encrypt + @cipher.key = @key + + rc = @cipher.update(data) + rc << @cipher.final + + rc + end + + def decrypt(data) + @cipher.decrypt + @cipher.key = @key + + rc = @cipher.update(::Base64.decode64(data)) + rc << @cipher.final + + rc + end + end +end diff --git a/swagger.yaml b/swagger.yaml index bb2d3fb..f76e1f4 100644 --- a/swagger.yaml +++ b/swagger.yaml @@ -19,7 +19,12 @@ paths: type: "string" /list: get: - parameters: [] + parameters: + - in: "query" + name: "user" + description: "OpenNebula user identity" + required: false + type: "string" responses: 200: description: "List existing networks" @@ -40,6 +45,11 @@ paths: required: true type: "integer" format: "int64" + - in: "query" + name: "user" + description: "OpenNebula user identity" + required: false + type: "string" responses: 200: description: "Get information about network"