packaging 0.88.77 → 0.99.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +128 -74
  3. data/lib/packaging/artifactory.rb +60 -433
  4. data/lib/packaging/config/params.rb +7 -28
  5. data/lib/packaging/config.rb +50 -150
  6. data/lib/packaging/deb/repo.rb +19 -20
  7. data/lib/packaging/gem.rb +83 -41
  8. data/lib/packaging/ips.rb +57 -0
  9. data/lib/packaging/msi.rb +89 -0
  10. data/lib/packaging/nuget.rb +1 -1
  11. data/lib/packaging/osx.rb +36 -0
  12. data/lib/packaging/paths.rb +87 -225
  13. data/lib/packaging/platforms.rb +416 -443
  14. data/lib/packaging/repo.rb +22 -122
  15. data/lib/packaging/retrieve.rb +7 -36
  16. data/lib/packaging/rpm/repo.rb +8 -5
  17. data/lib/packaging/tar.rb +0 -9
  18. data/lib/packaging/util/date.rb +0 -5
  19. data/lib/packaging/util/execution.rb +2 -2
  20. data/lib/packaging/util/git.rb +1 -1
  21. data/lib/packaging/util/gpg.rb +1 -5
  22. data/lib/packaging/util/net.rb +37 -79
  23. data/lib/packaging/util/rake_utils.rb +0 -1
  24. data/lib/packaging/util/ship.rb +13 -142
  25. data/lib/packaging/util/tool.rb +1 -1
  26. data/lib/packaging/util/version.rb +0 -8
  27. data/lib/packaging/util.rb +2 -2
  28. data/lib/packaging.rb +3 -3
  29. data/spec/fixtures/config/params.yaml +2 -0
  30. data/spec/lib/packaging/artifactory_spec.rb +16 -66
  31. data/spec/lib/packaging/config_spec.rb +29 -49
  32. data/spec/lib/packaging/deb/repo_spec.rb +7 -16
  33. data/spec/lib/packaging/paths_spec.rb +56 -321
  34. data/spec/lib/packaging/platforms_spec.rb +21 -46
  35. data/spec/lib/packaging/repo_spec.rb +40 -78
  36. data/spec/lib/packaging/retrieve_spec.rb +8 -47
  37. data/spec/lib/packaging/rpm/repo_spec.rb +4 -4
  38. data/spec/lib/packaging/tar_spec.rb +40 -34
  39. data/spec/lib/packaging/util/git_tag_spec.rb +1 -1
  40. data/spec/lib/packaging/util/gpg_spec.rb +1 -1
  41. data/spec/lib/packaging/util/net_spec.rb +15 -35
  42. data/spec/lib/packaging/util/ship_spec.rb +63 -145
  43. data/spec/spec_helper.rb +14 -0
  44. data/tasks/00_utils.rake +6 -4
  45. data/tasks/apple.rake +0 -2
  46. data/tasks/config.rake +0 -5
  47. data/tasks/education.rake +5 -5
  48. data/tasks/fetch.rake +14 -17
  49. data/tasks/gem.rake +121 -134
  50. data/tasks/jenkins.rake +7 -51
  51. data/tasks/nightly_repos.rake +69 -20
  52. data/tasks/pe_ship.rake +11 -16
  53. data/tasks/retrieve.rake +6 -13
  54. data/tasks/ship.rake +256 -196
  55. data/tasks/sign.rake +135 -63
  56. data/tasks/tar.rake +6 -0
  57. data/templates/packaging.xml.erb +7 -9
  58. data/templates/repo.xml.erb +3 -6
  59. metadata +27 -80
  60. data/lib/packaging/archive.rb +0 -126
  61. data/lib/packaging/artifactory/extensions.rb +0 -94
  62. data/lib/packaging/config/validations.rb +0 -13
  63. data/lib/packaging/metrics.rb +0 -15
  64. data/lib/packaging/sign/deb.rb +0 -9
  65. data/lib/packaging/sign/dmg.rb +0 -41
  66. data/lib/packaging/sign/ips.rb +0 -57
  67. data/lib/packaging/sign/msi.rb +0 -124
  68. data/lib/packaging/sign/rpm.rb +0 -115
  69. data/lib/packaging/sign.rb +0 -8
  70. data/spec/lib/packaging/gem_spec.rb +0 -86
  71. data/spec/lib/packaging/sign_spec.rb +0 -133
  72. data/tasks/archive.rake +0 -69
@@ -1,126 +0,0 @@
1
- module Pkg::Archive
2
- module_function
3
-
4
- # Array of base paths for foss artifacts on weth
5
- def base_paths
6
- [Pkg::Config.yum_repo_path, Pkg::Config.apt_repo_staging_path, Pkg::Config.apt_repo_path, '/opt/downloads'].compact.freeze
7
- end
8
-
9
- # Array of paths for temporarily staging artifacts before syncing to release-archives on s3
10
- def archive_paths
11
- [Pkg::Config.yum_archive_path, Pkg::Config.apt_archive_path, Pkg::Config.freight_archive_path, Pkg::Config.downloads_archive_path, '/opt/tmp-apt'].compact.freeze
12
- end
13
-
14
- # Move yum directories from repo path to archive staging path
15
- def stage_yum_archives(directory)
16
- # /opt/repository/yum/#{directory}
17
- full_directory = File.join(Pkg::Config.yum_repo_path, directory)
18
- archive_path = File.join(Pkg::Config.yum_archive_path, directory)
19
- command = <<-CMD
20
- if [ ! -d #{full_directory} ]; then
21
- if [ -d #{archive_path} ]; then
22
- echo "Directory #{full_directory} has already been staged, skipping . . ."
23
- exit 0
24
- else
25
- echo "ERROR: Couldn't find directory #{full_directory}, exiting . . ."
26
- exit 1
27
- fi
28
- fi
29
- find #{full_directory} -type l -delete
30
- sudo chattr -i -R #{full_directory}
31
- sudo mkdir --parents #{File.dirname(archive_path)}
32
- sudo chown root:release -R #{Pkg::Config.yum_archive_path}
33
- sudo chmod g+w -R #{Pkg::Config.yum_archive_path}
34
- mv #{full_directory} #{archive_path}
35
- CMD
36
- Pkg::Util::Net.remote_execute(Pkg::Config.staging_server, command)
37
- end
38
-
39
- # Move directories from freight path (aka repo staging path) to archive staging paths
40
- def stage_apt_archives(directory)
41
- find_command = "find #{Pkg::Config.apt_repo_staging_path} -type d -name #{directory}"
42
- find_command = "find #{Pkg::Config.apt_repo_staging_path} -maxdepth 2 -type f" if directory == 'main'
43
- command = <<-CMD
44
- for stuff in $(#{find_command}); do
45
- find $stuff -type l -delete
46
- codename=$(dirname ${stuff##{Pkg::Config.apt_repo_staging_path}/})
47
- sudo mkdir --parents #{Pkg::Config.freight_archive_path}/$codename
48
- sudo chown root:release -R #{Pkg::Config.freight_archive_path}/$codename
49
- sudo chmod g+w -R #{Pkg::Config.freight_archive_path}/$codename
50
- mv $stuff #{Pkg::Config.freight_archive_path}/$codename
51
-
52
- pool_directory=#{Pkg::Config.apt_repo_path}/pool/$codename/#{directory}
53
- if [ ! -d $pool_directory ]; then
54
- echo "Can't find directory $pool_directory, it may have already been archived, skipping . . ."
55
- continue
56
- fi
57
- sudo mkdir --parents /opt/tmp-apt
58
- sudo chown root:release -R /opt/tmp-apt
59
- sudo chmod g+w -R /opt/tmp-apt
60
- mv $pool_directory /opt/tmp-apt
61
- done
62
- CMD
63
- Pkg::Util::Net.remote_execute(Pkg::Config.staging_server, command)
64
- end
65
-
66
- # Move downloads directories to archive staging path
67
- def stage_downloads_archives(directory)
68
- # /opt/downloads/#{directory}
69
- full_directory = File.join('/', 'opt', 'downloads', directory)
70
- archive_path = File.join(Pkg::Config.downloads_archive_path, directory)
71
- command = <<-CMD
72
- if [ ! -d #{full_directory} ]; then
73
- if [ -d #{archive_path} ]; then
74
- echo "Directory #{full_directory} has already been staged, skipping . . ."
75
- exit 0
76
- else
77
- echo "ERROR: Couldn't find directory #{full_directory}, exiting . . ."
78
- exit 1
79
- fi
80
- fi
81
- find #{full_directory} -type l -delete
82
- sudo chattr -i -R #{full_directory}
83
- sudo mkdir --parents #{File.dirname(archive_path)}
84
- sudo chown root:release -R #{Pkg::Config.downloads_archive_path}
85
- sudo chmod g+w -R #{Pkg::Config.downloads_archive_path}
86
- mv #{full_directory} #{archive_path}
87
- CMD
88
- Pkg::Util::Net.remote_execute(Pkg::Config.staging_server, command)
89
- end
90
-
91
- # Delete empty directories from repo paths on weth
92
- def remove_empty_directories
93
- base_paths.each do |path|
94
- command = <<-CMD
95
- for directory in $(find #{path} -type d); do
96
- if [ ! -d $directory ]; then
97
- echo "Can't find directory $directory, it was probably already deleted, skipping . . ."
98
- continue
99
- fi
100
- files=$(find $directory -type f)
101
- if [ -z "$files" ]; then
102
- echo "No files in directory $directory, deleting . . ."
103
- sudo rm -rf $directory
104
- fi
105
- done
106
- CMD
107
- Pkg::Util::Net.remote_execute(Pkg::Config.staging_server, command)
108
- end
109
- end
110
-
111
- # Delete broken symlinks from repo paths on weth
112
- def remove_dead_symlinks
113
- base_paths.each do |path|
114
- command = "find #{path} -xtype l -delete"
115
- Pkg::Util::Net.remote_execute(Pkg::Config.staging_server, command)
116
- end
117
- end
118
-
119
- # Delete artifacts from archive staging paths (after they've been synced to s3)
120
- def delete_staged_archives
121
- archive_paths.each do |archive_path|
122
- command = "sudo rm -rf #{File.join(archive_path, '*')}"
123
- Pkg::Util::Net.remote_execute(Pkg::Config.staging_server, command)
124
- end
125
- end
126
- end
@@ -1,94 +0,0 @@
1
- require 'artifactory'
2
-
3
- module ArtifactoryExtensions
4
- module ClassMethods
5
- #
6
- # Search for an artifact in a repo using an Ant-like pattern.
7
- # Unlike many Artifactory searches, this one is restricted to a single
8
- # repository.
9
- #
10
- # @example Search in a repository named 'foo_local' for an artifact in a directory containing
11
- # the word "recent", named "artifact[0-9].txt"
12
- # Artifact.pattern_search(pattern: '*recent*/artifact[0-9].txt',
13
- # repo: 'foo_local')
14
- #
15
- # @param [Hash] options
16
- # A hash of options, as follows:
17
- #
18
- # @option options [Artifactory::Client] :client
19
- # the client object to make the request with
20
- # @option options [String] :pattern
21
- # the Ant-like pattern to use for finding artifacts within the repos. Note that the
22
- # Ant pattern '**' is barred in this case by JFrog.
23
- # @option options [String] :repo
24
- # the repo to search
25
- #
26
- # @return [Array<Resource::Artifact>]
27
- # a list of artifacts that match the query
28
- #
29
- def pattern_search(options = {})
30
- client = extract_client!(options)
31
- params = Artifactory::Util.slice(options, :pattern, :repo)
32
- pattern_search_parameter = { :pattern => "#{params[:repo]}:#{params[:pattern]}" }
33
- response = client.get('/api/search/pattern', pattern_search_parameter)
34
- return [] if response['files'].nil? || response['files'].empty?
35
-
36
- # A typical response:
37
- # {
38
- # "repoUri"=>"https:<artifactory endpoint>/<repo>",
39
- # "sourcePattern"=>"<repo>:<provided search pattern>",
40
- # "files"=>[<filename that matched pattern>, ...]
41
- # }
42
- #
43
- # Inserting '/api/storage' before the repo makes the 'from_url' call work correctly.
44
- #
45
- repo_uri = response['repoUri']
46
- unless repo_uri.include?('/api/storage/')
47
- # rubocop:disable Style/PercentLiteralDelimiters
48
- repo_uri.sub!(%r(/#{params[:repo]}$), "/api/storage/#{params[:repo]}")
49
- end
50
- response['files'].map do |file_path|
51
- from_url("#{repo_uri}/#{file_path}", client: client)
52
- end
53
- end
54
-
55
- # This adds the `exact_match` option to artifactory search, and defaults it
56
- # to true. With `exact_match` set to `true` the artifact will only be
57
- # returned if the name in the download uri matches the name we're trying to
58
- # download
59
- def search(options = {})
60
- exact_match = options[:exact_match].nil? ? true : options[:exact_match]
61
- artifacts = super
62
-
63
- if exact_match
64
- artifacts.select! { |artifact| File.basename(artifact.download_uri) == options[:name] }
65
- end
66
- artifacts
67
- end
68
-
69
- # This adds the `name` option to artifactory checksum search. It defaults to
70
- # unset. If set, the artifact is only returned if the download uri matches
71
- # the passed name
72
- def checksum_search(options = {})
73
- artifacts = super
74
- if options[:name]
75
- artifacts.select! { |artifact| File.basename(artifact.download_uri) == options[:name] }
76
- end
77
- artifacts
78
- end
79
- end
80
-
81
- # needed to prepend class methods, see https://stackoverflow.com/questions/18683750/how-to-prepend-classmethods
82
- def self.prepended(base)
83
- class << base
84
- prepend ClassMethods
85
- end
86
- end
87
- end
88
-
89
- module Artifactory
90
- class Resource::Artifact
91
- # use prepend instead of monkeypatching so we can call `super`
92
- prepend ArtifactoryExtensions
93
- end
94
- end
@@ -1,13 +0,0 @@
1
- module Pkg
2
- class ConfigValidations
3
-
4
- class << self
5
-
6
- # As a validation, this one is kindof lame but is intended as a seed pattern for possibly
7
- # more robust ones.
8
- def not_empty?(value)
9
- value.to_s.empty? ? false : true
10
- end
11
- end
12
- end
13
- end
@@ -1,15 +0,0 @@
1
- module Pkg::Metrics
2
- module_function
3
-
4
- def update_release_metrics
5
- metrics_repo = 'release-metrics'
6
- command = <<CMD
7
- git clone git@github.com:puppetlabs/#{metrics_repo}.git
8
- cd #{metrics_repo}
9
- bundle exec add-release --date #{Pkg::Util::Date.today} --project #{Pkg::Config.project} --version #{Pkg::Config.ref}
10
- cd ..
11
- rm -r #{metrics_repo}
12
- CMD
13
- Pkg::Util::Execution.capture3(command, true)
14
- end
15
- end
@@ -1,9 +0,0 @@
1
- module Pkg::Sign::Deb
2
- module_function
3
-
4
- def sign_changes(file)
5
- # Lazy lazy lazy lazy lazy
6
- sign_program = "-p'gpg --use-agent --no-tty'" if ENV['RPM_GPG_AGENT']
7
- Pkg::Util::Execution.capture3("debsign #{sign_program} --re-sign -k#{Pkg::Config.gpg_key} #{file}")
8
- end
9
- end
@@ -1,41 +0,0 @@
1
- module Pkg::Sign::Dmg
2
- module_function
3
-
4
- def sign(target_dir = 'pkg')
5
- use_identity = "-i #{Pkg::Config.osx_signing_ssh_key}" unless Pkg::Config.osx_signing_ssh_key.nil?
6
-
7
- if Pkg::Config.osx_signing_server =~ /@/
8
- host_string = "#{Pkg::Config.osx_signing_server}"
9
- else
10
- host_string = "#{ENV['USER']}@#{Pkg::Config.osx_signing_server}"
11
- end
12
- ssh_host_string = "#{use_identity} #{host_string}"
13
- rsync_host_string = "-e 'ssh #{use_identity}' #{host_string}"
14
-
15
- work_dir = "/tmp/#{Pkg::Util.rand_string}"
16
- mount = File.join(work_dir, "mount")
17
- signed = File.join(work_dir, "signed")
18
- Pkg::Util::Net.remote_execute(ssh_host_string, "mkdir -p #{mount} #{signed}")
19
- dmgs = Dir.glob("#{target_dir}/apple/**/*.dmg")
20
- Pkg::Util::Net.rsync_to(dmgs.join(" "), rsync_host_string, work_dir)
21
- Pkg::Util::Net.remote_execute(ssh_host_string, %Q[for dmg in #{dmgs.map { |d| File.basename(d, ".dmg") }.join(" ")}; do
22
- /usr/bin/hdiutil attach #{work_dir}/$dmg.dmg -mountpoint #{mount} -nobrowse -quiet ;
23
- /usr/bin/security -q unlock-keychain -p "#{Pkg::Config.osx_signing_keychain_pw}" "#{Pkg::Config.osx_signing_keychain}" ;
24
- for pkg in $(ls #{mount}/*.pkg | xargs -n 1 basename); do
25
- if /usr/sbin/pkgutil --check-signature #{mount}/$pkg ; then
26
- echo "$pkg is already signed, skipping . . ." ;
27
- cp #{mount}/$pkg #{signed}/$pkg ;
28
- else
29
- /usr/bin/productsign --keychain "#{Pkg::Config.osx_signing_keychain}" --sign "#{Pkg::Config.osx_signing_cert}" #{mount}/$pkg #{signed}/$pkg ;
30
- fi
31
- done
32
- /usr/bin/hdiutil detach #{mount} -quiet ;
33
- /bin/rm #{work_dir}/$dmg.dmg ;
34
- /usr/bin/hdiutil create -volname $dmg -srcfolder #{signed}/ #{work_dir}/$dmg.dmg ;
35
- /bin/rm #{signed}/* ; done])
36
- dmgs.each do | dmg |
37
- Pkg::Util::Net.rsync_from("#{work_dir}/#{File.basename(dmg)}", rsync_host_string, File.dirname(dmg))
38
- end
39
- Pkg::Util::Net.remote_execute(ssh_host_string, "if [ -d '#{work_dir}' ]; then rm -rf '#{work_dir}'; fi")
40
- end
41
- end
@@ -1,57 +0,0 @@
1
- module Pkg::Sign::Ips
2
- module_function
3
-
4
- def sign(target_dir = 'pkg')
5
- use_identity = "-i #{Pkg::Config.ips_signing_ssh_key}" unless Pkg::Config.ips_signing_ssh_key.nil?
6
-
7
- ssh_host_string = "#{use_identity} #{ENV['USER']}@#{Pkg::Config.ips_signing_server}"
8
- rsync_host_string = "-e 'ssh #{use_identity}' #{ENV['USER']}@#{Pkg::Config.ips_signing_server}"
9
-
10
- p5ps = Dir.glob("#{target_dir}/solaris/11/**/*.p5p")
11
-
12
- p5ps.each do |p5p|
13
- work_dir = "/tmp/#{Pkg::Util.rand_string}"
14
- unsigned_dir = "#{work_dir}/unsigned"
15
- repo_dir = "#{work_dir}/repo"
16
- signed_dir = "#{work_dir}/pkgs"
17
-
18
- Pkg::Util::Net.remote_execute(ssh_host_string, "mkdir -p #{repo_dir} #{unsigned_dir} #{signed_dir}")
19
- Pkg::Util::Net.rsync_to(p5p, rsync_host_string, unsigned_dir)
20
-
21
- # Before we can get started with signing packages we need to create a repo
22
- Pkg::Util::Net.remote_execute(ssh_host_string, "sudo -E /usr/bin/pkgrepo create #{repo_dir}")
23
- Pkg::Util::Net.remote_execute(ssh_host_string, "sudo -E /usr/bin/pkgrepo set -s #{repo_dir} publisher/prefix=puppetlabs.com")
24
- # And import all the packages into the repo.
25
- Pkg::Util::Net.remote_execute(ssh_host_string, "sudo -E /usr/bin/pkgrecv -s #{unsigned_dir}/#{File.basename(p5p)} -d #{repo_dir} '*'")
26
- # We are going to hard code the values for signing cert locations for now.
27
- # This autmation will require an update to actually become reusable, but
28
- # for now these values will stay this way so solaris signing will stop
29
- # failing. Please update soon. 06/23/16
30
- #
31
- # - Sean P. McDonald
32
- #
33
- # We sign the entire repo
34
- sign_cmd = "sudo -E /usr/bin/pkgsign -c /root/signing/signing_cert_2020.pem \
35
- -i /root/signing/Thawte_SHA256_Code_Signing_CA.pem \
36
- -i /root/signing/Thawte_Primary_Root_CA.pem \
37
- -k /root/signing/signing_key_2020.pem \
38
- -s 'file://#{work_dir}/repo' '*'"
39
- puts "About to sign #{p5p} with #{sign_cmd} in #{work_dir}"
40
- Pkg::Util::Net.remote_execute(ssh_host_string, sign_cmd.squeeze(' '))
41
- # pkgrecv with -a will pull packages out of the repo, so we need to do that too to actually get the packages we signed
42
- Pkg::Util::Net.remote_execute(ssh_host_string, "sudo -E /usr/bin/pkgrecv -d #{signed_dir}/#{File.basename(p5p)} -a -s #{repo_dir} '*'")
43
- begin
44
- # lets make sure we actually signed something?
45
- # **NOTE** if we're repeatedly trying to sign the same version this
46
- # might explode because I don't know how to reset the IPS cache.
47
- # Everything is amazing.
48
- Pkg::Util::Net.remote_execute(ssh_host_string, "sudo -E /usr/bin/pkg contents -m -g #{signed_dir}/#{File.basename(p5p)} '*' | grep '^signature '")
49
- rescue RuntimeError
50
- raise "Looks like #{File.basename(p5p)} was not signed correctly, quitting!"
51
- end
52
- # and pull the packages back.
53
- Pkg::Util::Net.rsync_from("#{signed_dir}/#{File.basename(p5p)}", rsync_host_string, File.dirname(p5p))
54
- Pkg::Util::Net.remote_execute(ssh_host_string, "if [ -e '#{work_dir}' ] ; then sudo rm -r '#{work_dir}' ; fi")
55
- end
56
- end
57
- end
@@ -1,124 +0,0 @@
1
- module Pkg::Sign::Msi
2
- module_function
3
-
4
- def sign(target_dir = 'pkg')
5
- use_identity = "-i #{Pkg::Config.msi_signing_ssh_key}" if Pkg::Config.msi_signing_ssh_key
6
-
7
- ssh_host_string = "#{use_identity} Administrator@#{Pkg::Config.msi_signing_server}"
8
- rsync_host_string = "-e 'ssh #{use_identity}' Administrator@#{Pkg::Config.msi_signing_server}"
9
-
10
- work_dir = "Windows/Temp/#{Pkg::Util.rand_string}"
11
- Pkg::Util::Net.remote_execute(ssh_host_string, "mkdir -p C:/#{work_dir}")
12
- msis = Dir.glob("#{target_dir}/windows*/**/*.msi")
13
- Pkg::Util::Net.rsync_to(msis.join(" "), rsync_host_string, "/cygdrive/c/#{work_dir}",
14
- extra_flags: ["--ignore-existing --relative"])
15
-
16
- # Please Note:
17
- # We are currently adding two signatures to the msi.
18
- #
19
- # Microsoft compatable Signatures are composed of three different
20
- # elements.
21
- # 1) The Certificate used to sign the package. This is the element that
22
- # is attached to organization. The certificate has an associated
23
- # algorithm. We recently (February 2016) had to switch from a sha1 to
24
- # a sha256 certificate. Sha1 was deprecated by many Microsoft
25
- # elements on 2016-01-01, which forced us to switch to a sha256 cert.
26
- # This sha256 certificate is recognized by all currently supported
27
- # windows platforms (Windows 8/Vista forward).
28
- # 2) The signature used to attach the certificate to the package. This
29
- # can be a done with a variety of digest algorithms. Older platforms
30
- # (i.e., Windows 8 and Windows Vista) don't recognize later
31
- # algorithms like sha256.
32
- # 3) The timestamp used to validate when the package was signed. This
33
- # comes from an external source and can be delivered with a variety
34
- # of digest algorithms. Older platforms do not recognize newer
35
- # algorithms like sha256.
36
- #
37
- # We could have only one signature with the Sha256 Cert, Sha1 Signature,
38
- # and Sha1 Timestamp, but that would be too easy. The sha256 signature
39
- # and timestamp add more security to our packages. We can't have only
40
- # sha256 elements in our package signature, though, because Windows 8
41
- # and Windows Vista just don't recognize them at all.
42
- #
43
- # In order to add two signatures to an MSI, we also need to change the
44
- # tool we use to sign packages with. Previously, we were using SignTool
45
- # which is the Microsoft blessed program used to sign packages. However,
46
- # this tool isn't able to add two signatures to an MSI specifically. It
47
- # can dual-sign an exe, just not an MSI. In order to get the dual-signed
48
- # packages, we decided to switch over to using osslsigncode. The original
49
- # project didn't have support to compile on a windows system, so we
50
- # decided to use this fork. The binaries on the signer were pulled from
51
- # https://sourceforge.net/u/keeely/osslsigncode/ci/master/tree/
52
- #
53
- # These are our signatures:
54
- # The first signature:
55
- # * Sha256 Certificate
56
- # * Sha1 Signature
57
- # * Sha1 Timestamp
58
- #
59
- # The second signature:
60
- # * Sha256 Certificate
61
- # * Sha256 Signature
62
- # * Sha256 Timestamp
63
- #
64
- # Once we no longer support Windows 8/Windows Vista, we can remove the
65
- # first Sha1 signature.
66
- sign_command = <<-CMD
67
- for msipath in #{msis.join(" ")}; do
68
- msi="$(basename $msipath)"
69
- msidir="C:/#{work_dir}/$(dirname $msipath)"
70
- if "/cygdrive/c/tools/osslsigncode-fork/osslsigncode.exe" verify -in "$msidir/$msi" ; then
71
- echo "$msi is already signed, skipping . . ." ;
72
- else
73
- tries=5
74
- sha1Servers=(http://timestamp.digicert.com/sha1/timestamp
75
- http://timestamp.comodoca.com/authenticode)
76
- for timeserver in "${sha1Servers[@]}"; do
77
- for ((try=1; try<=$tries; try++)) do
78
- ret=$(/cygdrive/c/tools/osslsigncode-fork/osslsigncode.exe sign \
79
- -n "Puppet" -i "http://www.puppet.com" \
80
- -h sha1 \
81
- -pkcs12 "#{Pkg::Config.msi_signing_cert}" \
82
- -pass "#{Pkg::Config.msi_signing_cert_pw}" \
83
- -t "$timeserver" \
84
- -in "$msidir/$msi" \
85
- -out "$msidir/signed-$msi")
86
- if [[ $ret == *"Succeeded"* ]]; then break; fi
87
- done;
88
- if [[ $ret == *"Succeeded"* ]]; then break; fi
89
- done;
90
- echo $ret
91
- if [[ $ret != *"Succeeded"* ]]; then exit 1; fi
92
- sha256Servers=(http://timestamp.digicert.com/sha256/timestamp
93
- http://timestamp.comodoca.com?td=sha256)
94
- for timeserver in "${sha256Servers[@]}"; do
95
- for ((try=1; try<=$tries; try++)) do
96
- ret=$(/cygdrive/c/tools/osslsigncode-fork/osslsigncode.exe sign \
97
- -n "Puppet" -i "http://www.puppet.com" \
98
- -nest -h sha256 \
99
- -pkcs12 "#{Pkg::Config.msi_signing_cert}" \
100
- -pass "#{Pkg::Config.msi_signing_cert_pw}" \
101
- -ts "$timeserver" \
102
- -in "$msidir/signed-$msi" \
103
- -out "$msidir/$msi")
104
- if [[ $ret == *"Succeeded"* ]]; then break; fi
105
- done;
106
- if [[ $ret == *"Succeeded"* ]]; then break; fi
107
- done;
108
- echo $ret
109
- if [[ $ret != *"Succeeded"* ]]; then exit 1; fi
110
- fi
111
- done
112
- CMD
113
-
114
- Pkg::Util::Net.remote_execute(
115
- ssh_host_string,
116
- sign_command,
117
- { fail_fast: false }
118
- )
119
- msis.each do | msi |
120
- Pkg::Util::Net.rsync_from("/cygdrive/c/#{work_dir}/#{msi}", rsync_host_string, File.dirname(msi))
121
- end
122
- Pkg::Util::Net.remote_execute(ssh_host_string, "if [ -d '/cygdrive/c/#{work_dir}' ]; then rm -rf '/cygdrive/c/#{work_dir}'; fi")
123
- end
124
- end
@@ -1,115 +0,0 @@
1
- module Pkg::Sign::Rpm
2
- module_function
3
-
4
- def sign(rpm, sign_flags = nil)
5
- # To enable support for wrappers around rpm and thus support for gpg-agent
6
- # rpm signing, we have to be able to tell the packaging repo what binary to
7
- # use as the rpm signing tool.
8
- rpm_command = ENV['RPM'] || Pkg::Util::Tool.find_tool('rpm')
9
-
10
- # If we're using the gpg agent for rpm signing, we don't want to specify the
11
- # input for the passphrase, which is what '--passphrase-fd 3' does. However,
12
- # if we're not using the gpg agent, this is required, and is part of the
13
- # defaults on modern rpm. The fun part of gpg-agent signing of rpms is
14
- # specifying that the gpg check command always return true
15
- gpg_check_command = ''
16
- input_flag = ''
17
- if Pkg::Util.boolean_value(ENV['RPM_GPG_AGENT'])
18
- gpg_check_command = "--define '%__gpg_check_password_cmd /bin/true'"
19
- else
20
- input_flag = "--passphrase-fd 3"
21
- end
22
-
23
- # Try this up to 5 times, to allow for incorrect passwords
24
- Pkg::Util::Execution.retry_on_fail(:times => 5) do
25
- # This definition of %__gpg_sign_cmd is the default on modern rpm. We
26
- # accept extra flags to override certain signing behavior for older
27
- # versions of rpm, e.g. specifying V3 signatures instead of V4.
28
- Pkg::Util::Execution.capture3("#{rpm_command} #{gpg_check_command} --define '%_gpg_name #{Pkg::Util::Gpg.key}' --define '%__gpg_sign_cmd %{__gpg} gpg #{sign_flags} #{input_flag} --batch --no-verbose --no-armor --no-secmem-warning -u %{_gpg_name} -sbo %{__signature_filename} %{__plaintext_filename}' --addsign #{rpm}")
29
- end
30
- end
31
-
32
- def legacy_sign(rpm)
33
- sign(rpm, "--force-v3-sigs --digest-algo=sha1")
34
- end
35
-
36
- def has_sig?(rpm)
37
- # This should allow the `Pkg::Util::Gpg.key` method to fail if gpg_key is
38
- # not set, before shelling out. We also only want the short key, all
39
- # lowercase, since that's what the `rpm -Kv` output uses.
40
- key = Pkg::Util::Gpg.key.downcase.chars.last(8).join
41
- signature_check_output = %x(rpm --checksig --verbose #{rpm})
42
- # If the signing key has not been loaded on the system this is running on,
43
- # the check will exit 1, even if the rpm is signed, so we can't use capture3,
44
- # which bails out with non-0 exit codes. Instead, check that the output
45
- # looks more-or-less how we expect it to.
46
- fail "Something went wrong checking the signature of #{rpm}." unless signature_check_output.include? "Header"
47
- return signature_check_output.include? "key ID #{key}"
48
- end
49
-
50
- def sign_all(rpm_directory)
51
- # Create a hash mapping full paths to basenames.
52
- # This will allow us to keep track of the different paths that may be
53
- # associated with a single basename, e.g. noarch packages.
54
- all_rpms = {}
55
- rpms_to_sign = Dir["#{rpm_directory}/**/*.rpm"]
56
- rpms_to_sign.each do |rpm_path|
57
- all_rpms[rpm_path] = File.basename(rpm_path)
58
- end
59
- # Delete a package, both from the signing server and from the rpm array, if
60
- # there are other packages with the same basename so that we only sign the
61
- # package once.
62
- all_rpms.each do |rpm_path, rpm_filename|
63
- if rpms_to_sign.map { |rpm| File.basename(rpm) }.count(rpm_filename) > 1
64
- FileUtils.rm(rpm_path)
65
- rpms_to_sign.delete(rpm_path)
66
- end
67
- end
68
-
69
- v3_rpms = []
70
- v4_rpms = []
71
- rpms_to_sign.each do |rpm|
72
- platform_tag = Pkg::Paths.tag_from_artifact_path(rpm)
73
- platform, version, _ = Pkg::Platforms.parse_platform_tag(platform_tag)
74
-
75
- # We don't sign AIX rpms
76
- next if platform_tag.include?('aix')
77
-
78
- if has_sig? rpm
79
- puts "#{rpm} is already signed, skipping . . ."
80
- next
81
- end
82
-
83
- case Pkg::Platforms.signature_format_for_platform_version(platform, version)
84
- when 'v3'
85
- v3_rpms << rpm
86
- when 'v4'
87
- v4_rpms << rpm
88
- else
89
- fail "Cannot find signature type for package '#{rpm}'"
90
- end
91
- end
92
-
93
- unless v3_rpms.empty?
94
- puts "Signing legacy (v3) rpms..."
95
- legacy_sign(v3_rpms.join(' '))
96
- end
97
-
98
- unless v4_rpms.empty?
99
- puts "Signing modern (v4) rpms..."
100
- sign(v4_rpms.join(' '))
101
- end
102
-
103
- # Using the map of paths to basenames, we re-hardlink the rpms we deleted.
104
- all_rpms.each do |link_path, rpm_filename|
105
- next if File.exist? link_path
106
- FileUtils.mkdir_p(File.dirname(link_path))
107
- # Find paths where the signed rpm has the same basename, but different
108
- # full path, as the one we need to link.
109
- paths_to_link_to = rpms_to_sign.select { |rpm| File.basename(rpm) == rpm_filename && rpm != link_path }
110
- paths_to_link_to.each do |path|
111
- FileUtils.ln(path, link_path, :force => true, :verbose => true)
112
- end
113
- end
114
- end
115
- end
@@ -1,8 +0,0 @@
1
- module Pkg::Sign
2
- require 'packaging/sign/deb'
3
- require 'packaging/sign/dmg'
4
- require 'packaging/sign/ips'
5
- require 'packaging/sign/msi'
6
- require 'packaging/sign/rpm'
7
- module_function
8
- end