Skip to content

Commit

Permalink
Implement deep signing for OSX Notarization
Browse files Browse the repository at this point in the history
* Adds deep signing of libraries and binaries to the pakacking process for the pkg packager.
* Enables the hardened runtime for binaries.
* Adds --preserve-xattr flag to pkgbuild so signing is preserved through packaging and install.*

In order to deep sign we have to know where a software definition will install binaries and libraries. To facilite this lib_dirs and bin_dirs functions have been added to the software definition. These two functions return defaults that match standard omnibus locations, but allow individual software defs to override if they do something different.

Bumps major version since signing deep signing is a major change.

Signed-off-by: Jon Morrow <jmorrow@chef.io>
  • Loading branch information
Jon Morrow committed Jan 22, 2020
1 parent 70855aa commit cba1d5d
Show file tree
Hide file tree
Showing 7 changed files with 546 additions and 5 deletions.
5 changes: 5 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## Omnibus 7.0:

### Deep Signing and Hardened Runtime

When packaging using the pkg packager omnibus will now deep sign all binaries and libraries in the package based of each software definition's bin_dirs and lib_dirs. When siging binaries the hardened runtime is enabled.

## Omnibus 6.0:

Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
6.1.21
7.0.0
124 changes: 121 additions & 3 deletions lib/omnibus/packagers/pkg.rb
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ class Packager::PKG < Packager::Base
build do
write_scripts

sign_software_libs_and_bins

build_component_pkg

write_distribution_file
Expand Down Expand Up @@ -177,6 +179,67 @@ def write_scripts
end
end

def sign_software_libs_and_bins
if signing_identity
log.info(log_key) { "Finding libraries and binaries that require signing." }

bin_dirs = Set[]
lib_dirs = Set[]
binaries = Set[]
libraries = Set[]

# Capture lib_dirs and bin_dirs from each software
project.softwares.each do |software|
lib_dirs.merge(software.lib_dirs)
bin_dirs.merge(software.bin_dirs)
end

# Find all binaries in each bind_dir
bin_dirs.each do |dir|
binaries.merge Dir["#{dir}/*"]
end
# Filter out symlinks, non-files, and non-executables
log.debug(log_key) { " Filtering non-binary files:" }
binaries.select! { |bin| is_binary?(bin) }

# Use otool to find all libries that are used by our binaries
binaries.each do |bin|
libraries.merge find_linked_libs bin
end

# Find all libraries in each lib_dir and add any we missed with otool
lib_dirs.each do |dir|
libraries.merge Dir["#{dir}/*"]
end

# Filter Mach-O libraries and bundles
log.debug(log_key) { " Filtering non-library files:" }
libraries.select! { |lib| is_macho?(lib) }

# Use otool to find all libries that are used by our libraries
otool_libs = Set[]
libraries.each do |lib|
otool_libs.merge find_linked_libs lib
end

# Filter Mach-O libraries and bundles
otool_libs.select! { |lib| is_macho?(lib) }
libraries.merge otool_libs

log.info(log_key) { " Signing libraries:" } unless libraries.empty?
libraries.each do |library|
log.debug(log_key) { " Signing: #{library}" }
sign_library(library)
end

log.info(log_key) { " Signing binaries:" } unless binaries.empty?
binaries.each do |binary|
log.debug(log_key) { " Signing: #{binary}" }
sign_binary(binary, true)
end
end
end

#
# Construct the intermediate build product. It can be installed with the
# Installer.app, but doesn't contain the data needed to customize the
Expand All @@ -185,16 +248,20 @@ def write_scripts
# @return [void]
#
def build_component_pkg
command = <<-EOH.gsub(/^ {8}/, "")
command = <<~EOH
pkgbuild \\
--identifier "#{safe_identifier}" \\
--version "#{safe_version}" \\
--scripts "#{scripts_dir}" \\
--root "#{project.install_dir}" \\
--install-location "#{project.install_dir}" \\
"#{component_pkg}"
--preserve-xattr \\
EOH

command << %Q{ --sign "#{signing_identity}" \\\n} if signing_identity
command << %Q{ "#{component_pkg}"}
command << %Q{\n}

Dir.chdir(staging_dir) do
shellout!(command)
end
Expand Down Expand Up @@ -229,7 +296,7 @@ def write_distribution_file
# @return [void]
#
def build_product_pkg
command = <<-EOH.gsub(/^ {8}/, "")
command = <<~EOH
productbuild \\
--distribution "#{staging_dir}/Distribution" \\
--resources "#{resources_dir}" \\
Expand Down Expand Up @@ -320,5 +387,56 @@ def safe_version
converted
end
end

#
# Given a file path return any linked libraries.
#
# @param [String] file_path
# The path to a file
# @return [Array<String>]
# The linked libs
#
def find_linked_libs(file_path)
# Find all libaries for each bin
command = "otool -L #{file_path}"

stdout = shellout!(command).stdout
stdout.slice!(file_path)
stdout.scan(/#{install_dir}\S*/)
end

def sign_library(lib)
sign_binary(lib)
end

def sign_binary(bin, hardened_runtime = false)
command = "codesign -s '#{signing_identity}' '#{bin}'"
command << %q{ --options=runtime} if hardened_runtime
## Force re-signing to deal with binaries that have the same sha.
command << %q{ --force}
command << %Q{\n}

shellout!(command)
end

def is_binary?(bin)
is_binary = File.file?(bin) &&
File.executable?(bin) &&
!File.symlink?(bin)
log.debug(log_key) { " removing from signing: #{bin}" } unless is_binary
is_binary
end

def is_macho?(lib)
is_macho = false
if is_binary?(lib)
command = "file #{lib}"

stdout = shellout!(command).stdout
is_macho = stdout.match?(/Mach-O.*library/) || stdout.match?(/Mach-O.*bundle/)
end
log.debug(log_key) { " removing from signing: #{lib}" } unless is_macho
is_macho
end
end
end
40 changes: 40 additions & 0 deletions lib/omnibus/software.rb
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,46 @@ def maintainer(val = NULL)
end
expose :maintainer

#
# Sets the bin_dirs where this software installs bins.
#
# @example
# bin_dirs ['/opt/chef-workstation/bin']
#
# @param [Array<String>] val
# the bin_dirs of the software
#
# @return [Array<String>]
#
def bin_dirs(val = NULL)
if null?(val)
@bin_dirs || [windows_safe_path("#{install_dir}/bin"), windows_safe_path("#{install_dir}/embedded/bin")]
else
@bin_dirs = val
end
end
expose :bin_dirs

#
# Sets the lib_dirs where this software installs libs.
#
# @example
# lib_dirs ['/opt/chef-workstation/bin']
#
# @param [Array<String>] val
# the lib_dirs of the software
#
# @return [Array<String>]
#
def lib_dirs(val = NULL)
if null?(val)
@lib_dirs || [windows_safe_path("#{install_dir}/embedded/lib")]
else
@lib_dirs = val
end
end
expose :lib_dirs

#
# Add a software dependency to this software.
#
Expand Down
2 changes: 1 addition & 1 deletion lib/omnibus/version.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@
#

module Omnibus
VERSION = "6.1.21".freeze
VERSION = "7.0.0".freeze
end
Loading

0 comments on commit cba1d5d

Please sign in to comment.