Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# kitchen-oci CHANGELOG

# 2.1.0
- feat: add support for `ED25519` ssh keys.

# 2.0.0
- feat: set default value for `are_legacy_imds_endpoints_disabled` to `true`
> BREAKING CHANGE: This overrides the default value to `true` in accordance with latest [OCI secuirty guidelines](https://docs.oracle.com/en-us/iaas/Content/Compute/Tasks/gettingmetadata.htm)
Expand Down
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ The following driver parameters are common to both instance types, but are not r
- `oci_config`, Hash of additional `OCI::Config` settings. Allows you to test without an oci config file [[more](#use-without-oci-config-file)]
- `ssh_keypath`, SSH public key (default: `~/.ssh/id_rsa.pub`)
- `ssh_keygen`, Automatically generate the rsa key pair for an instance (default: `false`) [[more](#ssh-keygen)]
- `ssh_keytype`, Specify the type of ssh key to generate (valid values: `rsa`, `ed25519`) (default: `rsa`) [[more](#ssh-keygen)]
- `post_create_script`, run a script on an instance after deployment [[more](#post-create-script)]
- `post_create_reboot`, reboot the instance after instance creation (default: `false`)
- `proxy_url`, Connect via the specified proxy URL [[more](#proxy-support)]
Expand Down Expand Up @@ -309,7 +310,8 @@ These scripts are executed by the user specified as the transport username (most
## SSH Keygen

The driver can generate an ssh key pair for an instance during creation. In order to turn this feature on, add the `ssh_keygen` property to the `driver` and set the value to `true`. This can be set in the `driver` section on a
per-platform or per-suite basis, but can also be enabled globally for the entire kitchen.yml in the top-level `driver` section.
per-platform or per-suite basis, but can also be enabled globally for the entire kitchen.yml in the top-level `driver` section. By default, the driver will create a 4096 bit RSA key. For additional security, the driver can also create
a `ED25519` OpenSSH key. In order to enable this, utilize the `ssh_keytype: ed25519` parameter (see example below).

Ensure that the `transport` section does not contain a path to a private key (the `ssh_key` property). If the `transport` has a value in `ssh_key` property, this will mismatch with the key pair that the driver will create causing your
instance creation to be stuck in an endless loop waiting for `transport` to receive a confirmed ssh connection.
Expand All @@ -321,6 +323,7 @@ Upon instance termination (`kitchen destroy`), the generated key pair will be re
```yml
driver:
ssh_keygen: true
ssh_keytype: ed25519

transport:
username: opc
Expand Down
1 change: 1 addition & 0 deletions kitchen-oci.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ Gem::Specification.new do |spec| # rubocop: disable Metrics/BlockLength
}
spec.add_dependency "oci", "~> 2.18"
spec.add_dependency "test-kitchen"
spec.add_dependency "ed25519"
spec.add_development_dependency "bundler"
spec.add_development_dependency "chefstyle"
spec.add_development_dependency "pry"
Expand Down
45 changes: 6 additions & 39 deletions lib/kitchen/driver/oci.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,15 @@ module Driver
# @author Stephen Pearson <stephen.pearson@oracle.com>
class Oci < Kitchen::Driver::Base
require_relative "oci_version"
require_relative "oci/validations"
require_relative "oci/mixin/actions"
require_relative "oci/mixin/models"
require_relative "oci/mixin/volumes"

include Kitchen::Driver::Oci::Mixin::Actions
include Kitchen::Driver::Oci::Mixin::Models
include Kitchen::Driver::Oci::Mixin::Volumes

plugin_version Kitchen::Driver::OCI_VERSION
kitchen_driver_api_version 2

Expand Down Expand Up @@ -66,6 +71,7 @@ class Oci < Kitchen::Driver::Base
default_keypath = File.expand_path(File.join(%w{~ .ssh id_rsa.pub}))
default_config :ssh_keypath, default_keypath
default_config :ssh_keygen, false
default_config :ssh_keytype, "rsa"
default_config :post_create_script, nil
default_config :proxy_url, nil
default_config :user_data, nil
Expand Down Expand Up @@ -95,28 +101,6 @@ class Oci < Kitchen::Driver::Base
# dbaas configs
default_config :dbaas, {}

validations[:instance_type] = lambda do |attr, val, driver|
validation_error("[:#{attr}] #{val} is not a valid instance_type. must be either compute or dbaas.", driver) unless %w{compute dbaas}.include?(val.downcase)
end

validations[:nsg_ids] = lambda do |attr, val, driver|
unless val.nil?
validation_error("[:#{attr}] list cannot be longer than 5 items", driver) if val.length > 5
end
end

validations[:volumes] = lambda do |attr, val, driver|
val.each do |vol_attr|
unless ["iscsi", "paravirtual", nil].include?(vol_attr[:type])
validation_error("[:#{attr}][:type] #{vol_attr[:type]} is not a valid volume type for #{vol_attr[:name]}", driver)
end
end
end

def self.validation_error(message, driver)
raise UserError, "#{driver.class}<#{driver.instance.name}>#config#{message}"
end

# Creates an instance.
# (see Kitchen::Driver::Base#create)
#
Expand Down Expand Up @@ -145,23 +129,6 @@ def destroy(state)
detatch_and_delete_volumes(state, oci, api)
terminate(state, inst)
end

private

include Kitchen::Driver::Oci::Mixin::Actions
include Kitchen::Driver::Oci::Mixin::Models
include Kitchen::Driver::Oci::Mixin::Volumes

# Creates the OCI config and API clients.
#
# @param action [Symbol] the name of the method that called this method.
# @return [Oci::Config, Oci::Api]
def auth(action)
oci = Oci::Config.new(config)
api = Oci::Api.new(oci.config, config)
oci.compartment if action == :create
[oci, api]
end
end
end
end
55 changes: 2 additions & 53 deletions lib/kitchen/driver/oci/instance.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,10 @@ class Instance < Oci # rubocop:disable Metrics/ClassLength
require_relative "models/compute"
require_relative "models/dbaas"
require_relative "instance/common"
require_relative "mixin/ssh_keys"

include CommonLaunchDetails
include Mixin::SshKeys

def initialize(opts = {})
super()
Expand Down Expand Up @@ -97,59 +99,6 @@ def public_ip_allowed?
!subnet.prohibit_public_ip_on_vnic
end

# Returns the location of the public ssh key.
#
# @return [String]
def public_key_file
if config[:ssh_keygen]
"#{config[:kitchen_root]}/.kitchen/.ssh/#{config[:instance_name]}_rsa.pub"
else
config[:ssh_keypath]
end
end

# Returns the name of the private key file.
#
# @return [String]
def private_key_file
public_key_file.gsub(".pub", "")
end

# Generates an RSA key pair to be used to SSH to the instance and updates the state with the full path to the private key.
def gen_key_pair
FileUtils.mkdir_p("#{config[:kitchen_root]}/.kitchen/.ssh")
rsa_key = OpenSSL::PKey::RSA.new(4096)
write_private_key(rsa_key)
write_public_key(rsa_key)
state.store(:ssh_key, private_key_file)
end

# Writes the private key.
#
# @param rsa_key [OpenSSL::PKey::RSA] the generated RSA key.
def write_private_key(rsa_key)
File.open(private_key_file, "wb") { |k| k.write(rsa_key.to_pem) }
File.chmod(0600, private_key_file)
end

# Writes the encoded private key as a public key.
#
# @param rsa_key [OpenSSL::PKey::RSA] the generated RSA key.
def write_public_key(rsa_key)
File.open(public_key_file, "wb") { |k| k.write("ssh-rsa #{encode_private_key(rsa_key)} #{config[:instance_name]}") }
File.chmod(0600, public_key_file)
end

# Encodes the private key.
#
# @param rsa_key [OpenSSL::PKey::RSA] the generated RSA key.
def encode_private_key(rsa_key)
prefix = "#{[7].pack("N")}ssh-rsa"
exponent = rsa_key.e.to_s(0)
modulus = rsa_key.n.to_s(0)
["#{prefix}#{exponent}#{modulus}"].pack("m0")
end

# Generates a random password.
#
# @param special_chars [Array] an array of special characters to include in the random password.
Expand Down
2 changes: 1 addition & 1 deletion lib/kitchen/driver/oci/instance/dbaas.rb
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ def node_count
end

# Adds the ssh_public_keys property to the launch_details.
def pubkey
def ssh_public_keys
result = []
result << read_public_key
launch_details.ssh_public_keys = result
Expand Down
11 changes: 11 additions & 0 deletions lib/kitchen/driver/oci/mixin/actions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,17 @@ module Mixin
#
# @author Justin Steele <justin.steele@oracle.com>
module Actions
# Creates the OCI config and API clients.
#
# @param action [Symbol] the name of the method that called this method.
# @return [Oci::Config, Oci::Api]
def auth(action)
oci = Oci::Config.new(config)
api = Oci::Api.new(oci.config, config)
oci.compartment if action == :create
[oci, api]
end

# Launches an instance.
#
# @param state [Hash] (see Kitchen::StateFile)
Expand Down
Loading