Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a bash script to create a certificate and sign delve's executable on macOS #758

Closed
aarzilli opened this issue Mar 7, 2017 · 13 comments

Comments

@aarzilli
Copy link
Member

aarzilli commented Mar 7, 2017

Some people do not want to use homebrew to install go (#753 and go-delve/homebrew-delve#9). We should make a bash script that can be run with sudo after go get that:

  1. creates a new certificate
  2. installs it
  3. signs delve's executable.
@justinclift
Copy link
Contributor

justinclift commented Mar 7, 2017

As a curiosity thought, which kind of certificate are you meaning?

Are you meaning an OSX developer cert, used for signing releases so Gatekeeper doesn't complain? If so, it might rely on people having paid the $99 to Apple for developer membership. Not sure if that's a workable thing or not.

Doing the signing bit with that kind of certificate is fairly easy though:

@aarzilli
Copy link
Member Author

aarzilli commented Mar 7, 2017

Yes. Our homebrew formula creates a self signed certificate using openssl, adds it to the keychain then uses it to sign dlv with codesign. The last bit is easy but creating the certificate isn't. There should be a script do to that without having to use homebrew.

See: https://github.com/go-delve/homebrew-delve/blob/master/Formula/delve.rb

@justinclift
Copy link
Contributor

justinclift commented Mar 7, 2017

k, so converting the commands in the Homebrew formula gives this:

#!/bin/sh

CERT="dlv-cert"
TEMPLATE="$CERT.tmpl"

echo '[ req ]' > $TEMPLATE
echo 'default_bits            = 2048                  # RSA key size' >> $TEMPLATE
echo 'encrypt_key             = no                    # Protect private key' >> $TEMPLATE
echo 'default_md              = sha512                # MD to use' >> $TEMPLATE
echo 'prompt                  = no                    # Prompt for DN' >> $TEMPLATE
echo 'distinguished_name      = codesign_dn           # DN template' >> $TEMPLATE
echo '' >> $TEMPLATE
echo '[ codesign_dn ]' >> $TEMPLATE
echo 'commonName              = "dlv-cert"' >> $TEMPLATE
echo '' >> $TEMPLATE
echo '[ codesign_reqext ]' >> $TEMPLATE
echo 'keyUsage                = critical,digitalSignature' >> $TEMPLATE
echo 'extendedKeyUsage        = critical,codeSigning' >> $TEMPLATE

echo "Generating ${CERT}.cer"
openssl req -new -newkey rsa:2048 -x509 -days 3650 -nodes -config ${TEMPLATE} \
 -extensions codesign_reqext -batch -out $CERT.cer -keyout $CERT.key

echo "Installing ${CERT}.cer as root"
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain \
 $CERT.cer
sudo security import $CERT.key -A -k /Library/Keychains/System.keychain

echo "Killing taskgated"
sudo pkill -f /usr/libexec/taskgated

echo "Signing delve executable"
codesign -s $CERT.cer $1

echo "Removing generated files"
rm $CERT.tmpl $CERT.cer $CERT.key

Running that, it does generate a key + cert, then import them both into the system keychain.

I'm not sure where the delve executable would be in relation to this script being run, so in the above it's left as $1. eg it assumes the executable path is passed on the command line.

If the executable is at a known location instead, it might be better to hard code that location.

@justinclift
Copy link
Contributor

justinclift commented Mar 7, 2017

For reference, the cert generated here seems ok:

$ openssl x509 -noout -text -in dlv-cert.cer
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            e7:78:25:24:7b:88:cd:54
        Signature Algorithm: sha512WithRSAEncryption
        Issuer: CN=dlv-cert
        Validity
            Not Before: Mar  7 12:16:44 2017 GMT
            Not After : Mar  5 12:16:44 2027 GMT
        Subject: CN=dlv-cert
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
            RSA Public Key: (2048 bit)
                Modulus (2048 bit):
                    00:f0:1b:c6:94:d4:2a:95:d4:a2:ff:ee:60:cd:dd:
                    45:8b:7f:98:53:9e:dd:d3:5c:d7:b8:f5:c3:19:c9:
                    34:5c:48:c6:44:f2:7a:1c:1d:54:96:70:ab:6f:d4:
                    6d:a2:7c:c1:ae:fc:19:fe:80:40:e7:92:a9:6c:62:
                    56:56:35:f4:ec:bb:3f:6c:c5:82:e9:4a:35:3c:ea:
                    cb:a7:00:12:63:2f:18:8a:d4:ed:bb:e8:b7:fe:dc:
                    13:dc:e8:6c:e4:5b:aa:16:6f:a9:f0:4f:20:c2:ec:
                    0a:b6:6d:3b:16:88:bf:e5:07:9e:8b:1f:79:7b:43:
                    53:2d:d4:51:01:98:fd:dd:71:8c:76:51:e9:5d:56:
                    fe:80:29:f4:3e:42:f5:af:25:48:5d:1d:1d:4a:9b:
                    42:09:b9:62:7e:b3:95:9b:f0:00:56:f2:fb:93:0e:
                    9b:10:b0:25:a4:1a:b1:b6:e8:51:98:89:4d:88:66:
                    57:b3:c1:4c:da:72:c2:cf:55:91:5f:08:68:a5:f7:
                    d3:9a:91:fd:12:b7:9c:39:96:ee:ee:05:9b:6b:7a:
                    cf:a7:fd:b1:5d:14:f2:9f:45:75:47:44:87:cd:41:
                    86:76:4a:33:bb:f0:57:64:c3:58:f8:68:c8:60:dd:
                    eb:cd:4d:e8:9b:b8:ed:1d:03:55:b1:99:ae:09:1e:
                    dd:c1
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Key Usage: critical
                Digital Signature
            X509v3 Extended Key Usage: critical
                Code Signing
    Signature Algorithm: sha512WithRSAEncryption
        87:5e:09:4a:fc:ff:e9:fb:93:c3:b2:3c:39:a7:fb:0f:59:46:
        59:e4:8c:bc:1e:a3:88:10:56:df:f6:b1:5c:ee:5f:ba:38:8e:
        5e:00:f3:ea:af:b3:f8:bf:c6:3e:e2:fb:71:4c:90:16:f8:41:
        13:dc:34:0b:27:d7:a8:5b:f9:df:91:28:55:03:ca:f1:79:cd:
        3e:b6:46:5d:37:0b:1f:86:bf:34:72:4e:b5:fa:1a:6e:49:9b:
        77:02:7f:68:55:ce:d9:1a:37:cd:91:d0:fd:84:4e:a8:6f:c9:
        33:6b:3b:94:b5:0d:e7:ae:6d:e0:cb:ff:b8:ef:cf:09:cc:2e:
        fd:c7:8b:ce:ed:e3:d6:7a:b1:e8:6d:d0:19:28:20:92:74:bc:
        28:62:a0:d1:25:e6:91:be:94:11:5c:a1:0b:e0:d8:a3:b5:2f:
        2e:9a:2c:ca:2e:dc:4e:8a:85:98:54:e9:61:6a:aa:14:bd:ec:
        03:5c:49:1a:a2:13:fb:a1:2a:f6:ea:8d:40:cf:74:79:d4:c1:
        14:48:98:e0:43:4a:8d:a4:1b:b5:cb:7a:8f:c9:e2:30:76:76:
        72:08:2d:91:0e:d8:12:fd:7c:77:06:44:a4:e1:a4:1a:11:79:
        7a:a0:01:42:3e:99:a0:9a:e5:ff:b9:a4:a7:a1:59:0a:a7:22:
        4a:88:be:d6

@justinclift
Copy link
Contributor

Also note the complete lack of error checking in the script. Probably wouldn't hurt to at least add some minimial catches for non zero error codes (etc).

@aarzilli
Copy link
Member Author

aarzilli commented Mar 7, 2017

Codesigning is actually done in the makefile:
https://github.com/derekparker/delve/blob/master/Makefile#L41

I don't know if it is better to tell the user to run the script after go get (and have the script codesign the executable) or to tell them to run the script first and then make install.

Either way the script should check if the certificate already exists, like the brew recipe does.

For error checking you can change the shell to /bin/bash and add set -e at the beginning.

PS. you should post this as a pull request.

@justinclift
Copy link
Contributor

Oops, yep. Missed the lines for checking if it's already there.

With this script, the commands could be incorporated directly into the Makefile itself, just before the codesign command. Would that be optimal?

@aarzilli
Copy link
Member Author

aarzilli commented Mar 7, 2017

it could be a script, let's say in scripts/gencert.sh, then in the makefile if CERT isn't set gencert.sh is called and CERT is set to dlv-cert before codesign.

@justinclift
Copy link
Contributor

No worries at all, sounds workable. 😄

@justinclift
Copy link
Contributor

Interestingly, this turns up a bug in existing Makefile. 😉

Creating an issue for it now.

@seguri
Copy link

seguri commented Mar 7, 2017

Ported from formula:

#!/bin/bash
set -o errexit
cert=dlv-cert
cat <<EOF >${cert}.cfg
[ req ]
default_bits       = 2048        # RSA key size
encrypt_key        = no          # Protect private key
default_md         = sha512      # MD to use
prompt             = no          # Prompt for DN
distinguished_name = codesign_dn # DN template
[ codesign_dn ]
commonName         = "dlv-cert"
[ codesign_reqext ]
keyUsage           = critical,digitalSignature
extendedKeyUsage   = critical,codeSigning
EOF
if $(security find-certificate -Z -p -c "$cert" /Library/Keychains/System.keychain 2>&1 | egrep -o '^SHA-1' >/dev/null); then
    echo "$cert is already installed, no need to create it"
else
    echo "Generating $cert"
    openssl req -new -newkey rsa:2048 -x509 -days 3650 -nodes -config "${cert}.cfg" -extensions codesign_reqext -batch -out "${cert}.pem" -keyout "${cert}.key"
    echo "[SUDO] Installing ${cert} as root"
    sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain "${cert}.pem"
    sudo security import "${cert}.key" -A -k /Library/Keychains/System.keychain
    echo "[SUDO] Killing taskgated"
    sudo pkill -f /usr/libexec/taskgated
fi

@justinclift
Copy link
Contributor

Ahhh, the EOF approach is probably better. I'll update my PR to use that. 😄

@justinclift
Copy link
Contributor

This can probably be closed, now that #760 is merged. 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants