Preserving xattr with pkgbuild

If you’re deploying a script or other file (text, image, etc) that is code signed, you will need to make sure the code sign requirements are kept (see here as to why).

If you use pkgbuild, you’ll need to make sure you include the --preserve-xattr argument in the build options. This argument is undocumented, and may not be supported in older versions of pkgbuild.

Note, If you use the Packages app to build Mac pkg files, it doesn’t preserve extended attributes when it builds a pkg file, so other work arounds will be required if you’re using that to build your software packages.

Using outset as an example of building a package with pkgbuild and preserving the extended attributes, here’s a quick example of modifying the Makefile to include this argument.

In the block below, the repo is cloned, then sed is used to modify the line containing pkgbuild, adding the --preserve-xattr argument after the --ownership recommended argument, git diff is then run to verify the change.

[jappleseed@pegasus]:git # git clone https://github.com/chilcote/outset
Cloning into 'outset'...
remote: Enumerating objects: 515, done.
remote: Total 515 (delta 0), reused 0 (delta 0), pack-reused 515
Receiving objects: 100% (515/515), 273.71 KiB | 479.00 KiB/s, done.
Resolving deltas: 100% (226/226), done.
[jappleseed@pegasus]:git # cd outset/
[jappleseed@pegasus]:outset # sed -i Makefile.backup '/pkgbuild/s/ownership recommended/ownership recommended --preserve-xattr/g' Makefile
[jappleseed@pegasus]:outset # git diff Makefile
[jappleseed@pegasus]:outset # git diff Makefile
diff --git a/Makefile b/Makefile
index 7decb72..872e583 100644
--- a/Makefile
+++ b/Makefile
@@ -16,7 +16,7 @@ clean:
  ##  pkg - Create a package using pkgbuild pkg: clean
-       pkgbuild --root pkgroot --scripts scripts --identifier ${PKGID} --version ${PKGVERSION} --ownership recommended ./${PKGTITLE}-${PKGVERSION}.component.pkg
+       pkgbuild --root pkgroot --scripts scripts --identifier ${PKGID} --version ${PKGVERSION} --ownership recommended --preserve-xattr ./${PKGTITLE}-${PKGVERSION}.component.pkg
        productbuild --identifier ${PKGID}.${PKGVERSION} --package ./${PKGTITLE}-${PKGVERSION}.component.pkg ./${PKGTITLE}-${PKGVERSION}.pkg
        rm -f ./${PKGTITLE}-${PKGVERSION}.component.pkg
[jappleseed@pegasus]:outset #

Note: this is an example only that’s fairly specific to modifying the outset Makefile for repeatable builds that preserve the extended attributes. If you want to preserve extended attributes for other files when your packages are built, you may need to manually modify the Makefile or adjust your command line pkgbuild arguments to include the --preserve-xattr command.
Greg Neagle has added this flag into the munki-pkg utility.

To code sign, an example is provided below.
In this example, the codesign command is used with the relevant certificate details. The -i flag is used to specify an identifier for the code sign requirements. This is not a required argument, but if it isn’t supplied, codesign will use the filename to determine the identifier. Lastly, the path to the object being signed is provided.

The xattr command is used to demonstrate that for plain text files such as this, the code sign requirements are actually written out as extended attributes.
The codesign -dr - /path/to/file command outputs the code sign requirements for the file just signed.

[jappleseed@pegasus]:outset # codesign -s "Mac Developer: jappleseed@example.org (QED00ABDEC)" -i com.github.outset pkgroot/usr/local/outset/outset 
[jappleseed@pegasus]:outset # xattr pkgroot/usr/local/outset/outset 
com.apple.cs.CodeDirectory
com.apple.cs.CodeRequirements
com.apple.cs.CodeRequirements-1
com.apple.cs.CodeSignature
[jappleseed@pegasus]:outset # codesign -dr - pkgroot/usr/local/outset/outset 
Executable=/Users/jappleseed/Desktop/git/outset/pkgroot/usr/local/outset/outset
host => identifier "com.apple.pythonw" and anchor apple
designated => identifier "com.github.outset" and anchor apple generic and certificate leaf[subject.CN] = "Mac Developer: jappleseed@example.org (QED00ABDEC)" and certificate 1[field.1.2.345.678901.234.5.6.7] /* exists */
[jappleseed@pegasus]:outset #

From here, you can follow your normal build process.
In the case of this outset example it’s simply a matter of running make pkg.

[jappleseed@pegasus]:outset # make pkg
rm -f ./outset*.{dmg,pkg}
rm -f ./pkgroot/usr/local/outset/FoundationPlist/*.pyc
pkgbuild --root pkgroot --scripts scripts --identifier com.github.outset --version "2.0.6" --ownership recommended --preserve-xattr ./"outset"-"2.0.6".component.pkg
pkgbuild: Inferring bundle components from contents of pkgroot
pkgbuild: Adding top-level postinstall script
pkgbuild: Wrote package to ./outset-2.0.6.component.pkg
productbuild --identifier com.github.outset."2.0.6" --package ./"outset"-"2.0.6".component.pkg ./"outset"-"2.0.6".pkg
productbuild: Wrote product to ./outset-2.0.6.pkg
rm -f ./"outset"-"2.0.6".component.pkg

Note: these Makefile changes will be removed if the outset repo is updated, and this will preserve all extended attributes for files in the build process, so you may want to remove specific extended attributes such as com.apple.quarantine attributes.