lib/plugins/inspec-artifact/lib/inspec-artifact/cli.rb



# encoding: utf-8
require_relative 'base'

#
# Notes:
#
# Generate keys
#   The initial implementation uses 2048 bit RSA key pairs (public + private).
#   Public keys must be available for a customer to install and verify an artifact.
#   Private keys should be stored in a secure location and NOT be distributed.
#     (They're only for creating artifacts).
#
#
# .IAF file format
#   .iaf = "InSpec Artifact File", easy to rename if you'd like something more appropriate.
#   The iaf file wraps a binary artifact with some metadata. The first implementation
#   looks like this:
#
# INSPEC-PROFILE-1
# name_of_signing_key
# algorithm
# signature
# <empty line>
# binary-blob
# <eof>
#
# Let's look at each line:
# INSPEC-PROFILE-1:
#   This is the artifact version descriptor. It should't change unless the
#   format of the archive changes.
#
# name_of_signing_key
#   The name of the public key that can be used to verify an artifact
#
# algorithm
#   The digest used to sign, I picked SHA512 to start with.
#   If we support multiple digests, we'll need to have the verify() method
#   support each digest.
#
# signature
#   The result of passing the binary artifact through the digest algorithm above.
#   Result is base64 encoded.
#
# <empty line>
#   We use an empty line to separate artifact header from artifact body (binary blob).
#   The artifact body can be anything you like.
#
# binary-blob
#   A binary blob, most likely a .tar.gz or tar.xz file. We'll need to pick one and
#   stick with it as part of the "INSPEC-PROFILE-1" artifact version. If we change block
#   format, the artifact version descriptor must be incremented, and the sign()
#   and verify() methods must be updated to support a newer version.
#
#
# Key revocation
#   This implementation doesn't support key revocation. However, a customer
#   can remove the public cert file before installation, and artifacts will then
#   fail verification.
#
# Key locations
#   This implementation uses the current working directory to find public and
#   private keys. We should establish a common key directory (similar to /hab/cache/keys
#   or ~/.hab/cache/keys in Habitat).
#
# Extracting artifacts outside of InSpec
#   As in Habitat, the artifact format for InSpec allows the use of common
#   Unix tools to read the header and body of an artifact.
# To extract the header from a .iaf:
#        sed '/^$/q' foo.iaf
# To extract the raw content from a .iaf:
#        sed '1,/^$/d' foo.iaf

module InspecPlugins
  module Artifact
    class CLI < Inspec.plugin(2, :cli_command)
      subcommand_desc 'artifact SUBCOMMAND', 'Manage InSpec Artifacts'

      desc 'generate', 'Generate a RSA key pair for signing and verification'
      option :keyname, type: :string, required: true,
        desc: 'Desriptive name of key'
      option :keydir, type: :string, default: './',
        desc: 'Directory to search for keys'
      def generate_keys
        puts 'Generating keys'
        InspecPlugins::Artifact::Base.keygen(options)
      end

      desc 'sign-profile', 'Create a signed .iaf artifact'
      option :profile, type: :string, required: true,
        desc: 'Path to profile directory'
      option :keyname, type: :string, required: true,
        desc: 'Desriptive name of key'
      def sign_profile
        InspecPlugins::Artifact::Base.profile_sign(options)
      end

      desc 'verify-profile', 'Verify a signed .iaf artifact'
      option :infile, type: :string, required: true,
          desc: '.iaf file to verify'
      def verify_profile
        InspecPlugins::Artifact::Base.profile_verify(options)
      end

      desc 'install-profile', 'Verify and install a signed .iaf artifact'
      option :infile, type: :string, required: true,
          desc: '.iaf file to install'
      option :destdir, type: :string, required: true,
        desc: 'Installation directory'
      def install_profile
        InspecPlugins::Artifact::Base.profile_install(options)
      end
    end
  end
end