« Back to Index

Go: Codesign a Go test binary that listens on network and needs to accept incoming network connections

View original Gist on GitHub

Tags: #go #macos #network #security

1. codesign.sh

# 0. Have a binary to codesign.
# NOTE: I use `if os.Getenv("SKIP_FTP") != "" { t.Skip("...") }` to allow skipping the test when running the full test suite.

go test -c -o ./path/to/package/test_binary ./path/to/package

# 1. Create self-signed private key and certificate
# IMPORTANT: The `-addext` flags are essential for codesigning purposes.

openssl req -new -x509 -days 365 -nodes \
    -keyout ExampleTestBinaryCodeSigning.key -out ExampleTestBinaryCodeSigning.crt \
    -subj "/CN=ExampleTestBinaryCodeSigning" \
    -addext "keyUsage=digitalSignature,keyEncipherment" \
    -addext "extendedKeyUsage=codeSigning"

# 2. Export private key and certificate as p12.
# IMPORTANT: The `-legacy` flag is essential if using OpenSSL 3.x on macOS.
# OpenSSL 3.x changed its default algorithm in pkcs12. 
# Which is not compatible with embedded Security frameworks in macOS/iOS. 
# So you either downgrade to OpenSSL 1.1 or use -legacy flag.

openssl pkcs12 -export \
  -out ExampleTestBinaryCodeSigning.p12 \
  -inkey ExampleTestBinaryCodeSigning.key \
  -in ExampleTestBinaryCodeSigning.crt \
  -legacy \
  -passout pass:whatever

# 3. Validate the password has been set on your p12 (-nokeys vs -noout == just different output)

openssl pkcs12 -info -nokeys -in ExampleTestBinaryCodeSigning.p12 -legacy -password pass:whatever
openssl pkcs12 -info -noout  -in ExampleTestBinaryCodeSigning.p12 -legacy -password pass:whatever

# 4. List your keychains so you know which one you're going to reference and where it is located.
# It can be extracted automatically using `security list | awk '/login.keychain/ { print $1 }'`

security list

# 5. Import your p12 into your keychain.

security import ExampleTestBinaryCodeSigning.p12 -k ~/Library/Keychains/login.keychain-db -P whatever -T /usr/bin/codesign

# 6. Find the certificate in your keychain.

security find-certificate -a -c "ExampleTestBinaryCodeSigning" -p

# 7. Check the details of the certificate in the keychain.

security find-certificate -c "ExampleTestBinaryCodeSigning"

# 8. Set the certificate to be trusted.
# Only possible if you still have the cert.
# Otherwise you have to manually trust it via the "Keychain Access" GUI.

security add-trusted-cert -d -r trustRoot -k ~/Library/Keychains/login.keychain-db ./ExampleTestBinaryCodeSigning.crt

# 9. Validate you have 'valid' identities.

security find-identity -v
security find-identity -p codesigning -v

# 10. Now you may codesign your binary.

codesign -f -s "ExampleTestBinaryCodeSigning" ./path/to/package/test_binary

Makefile

.PHONY: codesigning
codesigning: ID=ExampleTestBinaryCodeSigningIdentity
codesigning: CERT_PSW=whatever
codesigning:
	@if [ $$(uname) == "Darwin" ] && [ "$$(security find-certificate -a -c "$(ID)" -p)" == "" ]; then \
		echo ""; \
		echo "⚠️  no code-signing certificate found in your keychain (so we'll try to generate that for you)"; \
		echo ""; \
		openssl req -new -x509 -days 365 -nodes \
			-keyout $(ID).key -out $(ID).crt \
			-subj "/CN=$(ID)" \
			-addext "keyUsage=digitalSignature,keyEncipherment" \
			-addext "extendedKeyUsage=codeSigning"; \
		openssl pkcs12 -export \
		  -out $(ID).p12 \
		  -inkey $(ID).key \
		  -in $(ID).crt \
		  -legacy \
		  -passout pass:$(CERT_PSW); \
		trap "rm -f $(ID)*" EXIT; \
		kc=$$(security list | awk '/login.keychain/ { gsub(/^ *| *$$/, ""); print $$1 }' | sed 's/"//g'); \
		security import $(ID).p12 \
			-k "$$kc" \
			-P $(CERT_PSW) \
			-T /usr/bin/codesign; \
		security add-trusted-cert -d -r trustRoot -k $$kc $(ID).crt; \
		if security find-identity -p codesigning -v | grep '0 valid identities found'; then \
			echo "🚨 failed to find a valid code-signing identity"; \
			exit 1; \
		fi \
	fi

.PHONY: test
test: codesigning ## Run project's test suite with race detection
	@if [ $$(uname) == "Darwin" ]; then \
		go test -c -o ./path/to/package/test_binary ./path/to/package; \
		codesign -f -s "ExampleTestBinaryCodeSigningIdentity" ./path/to/package/test_binary; \
		trap "rm -f ./path/to/package/test_binary" EXIT; \
		cd ./path/to/package/ && { ./test_binary; cd -; } || cd -; \
		# SKIP_FTP forces the go toolchain to skip the problematic test that uses the network; \
		SKIP_FTP=true go test -race -v -count=1 $(GO_BUILDARGS) $(GO_TESTARGS) $(APP_TESTARGS); \
	else \
		go test -race -v -count=1 $(GO_BUILDARGS) $(GO_TESTARGS) $(APP_TESTARGS); \
	fi