Move to protecting-coide-integrity
Largely finishes the document -- will work on updated content in "trusted team communication" and by reviewing the workstation security guide. We need at least a basic workstation security guide for the Mac systems. Signed-off-by: Konstantin Ryabitsev <konstantin@linuxfoundation.org>
This commit is contained in:
parent
9d61a13f1c
commit
34233e9d81
|
@ -1,6 +1,6 @@
|
||||||
# Free software developer security hygiene
|
# Protecting code integrity with PGP
|
||||||
|
|
||||||
Updated: 2017-12-01
|
Updated: 2017-12-15
|
||||||
|
|
||||||
### Target audience
|
### Target audience
|
||||||
|
|
||||||
|
@ -8,21 +8,13 @@ This document is aimed at developers working on free software projects. It
|
||||||
covers the following topics:
|
covers the following topics:
|
||||||
|
|
||||||
1. PGP key best practices
|
1. PGP key best practices
|
||||||
2. Basic introduction to PGP and Git
|
2. Introduction to PGP and Git
|
||||||
3. Basic workstation security best practices
|
|
||||||
|
|
||||||
We use the term "Free" as in "Freedom," but this guide can also be used for
|
We use the term "Free" as in "Freedom," but this guide can also be used for
|
||||||
developing non-free or source-available ("Open Source") software. If you write
|
developing non-free or source-available ("Open Source") software. If you write
|
||||||
code that goes into public source repositories, you can benefit from getting
|
code that goes into public source repositories, you can benefit from getting
|
||||||
acquainted with and following this guide.
|
acquainted with and following this guide.
|
||||||
|
|
||||||
#### Topics NOT covered
|
|
||||||
|
|
||||||
This is not a "how to write secure software" guide. Please check the resources
|
|
||||||
on secure coding best practices that are available for the programming
|
|
||||||
languages, libraries, and development environments used by your free software
|
|
||||||
project.
|
|
||||||
|
|
||||||
### Structure
|
### Structure
|
||||||
|
|
||||||
Each section is split into two areas:
|
Each section is split into two areas:
|
||||||
|
@ -42,9 +34,6 @@ help guide your decision:
|
||||||
- _(NICE)_ to have items will improve the overall security, but will affect how
|
- _(NICE)_ to have items will improve the overall security, but will affect how
|
||||||
you interact with your work environment, and probably require learning new
|
you interact with your work environment, and probably require learning new
|
||||||
habits or unlearning old ones.
|
habits or unlearning old ones.
|
||||||
- _(PARANOID)_ is reserved for items we feel will significantly improve your
|
|
||||||
security, but will require making equally significant adjustments to the way
|
|
||||||
you interact with your operating system.
|
|
||||||
|
|
||||||
Remember, these are only guidelines. If you feel these priority levels do not
|
Remember, these are only guidelines. If you feel these priority levels do not
|
||||||
reflect your project's commitment to security, you should adjust them as you
|
reflect your project's commitment to security, you should adjust them as you
|
||||||
|
@ -267,7 +256,7 @@ you use the gpg commands.
|
||||||
|
|
||||||
### Checklist
|
### Checklist
|
||||||
|
|
||||||
- [ ] Generate the 4096-bit RSA master key _(ESSENTIAL)_
|
- [ ] Generate a 4096-bit RSA master key _(ESSENTIAL)_
|
||||||
- [ ] Back up the master key using paperkey _(ESSENTIAL)_
|
- [ ] Back up the master key using paperkey _(ESSENTIAL)_
|
||||||
- [ ] Add all relevant identities _(ESSENTIAL)_
|
- [ ] Add all relevant identities _(ESSENTIAL)_
|
||||||
|
|
||||||
|
@ -434,6 +423,7 @@ different from what you want, you should fix it back:
|
||||||
- [ ] Generate a 2048-bit Signing subkey _(ESSENTIAL)_
|
- [ ] Generate a 2048-bit Signing subkey _(ESSENTIAL)_
|
||||||
- [ ] Generate a 2048-bit Authentication subkey _(NICE)_
|
- [ ] Generate a 2048-bit Authentication subkey _(NICE)_
|
||||||
- [ ] Upload your public keys to a PGP keyserver _(ESSENTIAL)_
|
- [ ] Upload your public keys to a PGP keyserver _(ESSENTIAL)_
|
||||||
|
- [ ] Set up a refresh cronjob _(ESSENTIAL)_
|
||||||
|
|
||||||
### Considerations
|
### Considerations
|
||||||
|
|
||||||
|
@ -508,6 +498,20 @@ To generate the public key file to paste in, just run:
|
||||||
|
|
||||||
$ gpg --export --armor [fpr]
|
$ gpg --export --armor [fpr]
|
||||||
|
|
||||||
|
#### Set up a refresh cronjob
|
||||||
|
|
||||||
|
You will need to regularly refresh the public keys on your keyring in order to
|
||||||
|
get the latest changes on other people's keys.
|
||||||
|
|
||||||
|
$ crontab -e
|
||||||
|
|
||||||
|
Add the following on a new line:
|
||||||
|
|
||||||
|
@daily /usr/bin/gpg2 --refresh >/dev/null 2>&1
|
||||||
|
|
||||||
|
**NOTE**: check the full path to your `gpg` or `gpg2` command and use the `gpg2`
|
||||||
|
command if regular `gpg` for you is the legacy GnuPG v.1.
|
||||||
|
|
||||||
## Moving your master key to offline storage
|
## Moving your master key to offline storage
|
||||||
|
|
||||||
### Checklist
|
### Checklist
|
||||||
|
@ -867,10 +871,66 @@ This should ask for your smartcard PIN on your first command, and then show
|
||||||
Congratulations, you have successfully made it extremely difficult to steal
|
Congratulations, you have successfully made it extremely difficult to steal
|
||||||
your digital developer identity!
|
your digital developer identity!
|
||||||
|
|
||||||
### TODO: Extending expiration date
|
### Other common GnuPG operations
|
||||||
### TODO: Revoking subkeys
|
|
||||||
### TODO: Configure gpg-agent
|
Here is a quick reference for various common operations you'll need to do with
|
||||||
### TODO: Configure TOFU policy
|
your PGP key.
|
||||||
|
|
||||||
|
In all of the below commands, the `[fpr]` is your key fingerprint.
|
||||||
|
|
||||||
|
#### Mounting your master key offline storage
|
||||||
|
|
||||||
|
You will need your master key for any of the operations below, so you will
|
||||||
|
first need to mount your backup offline storage and tell GnuPG to use it.
|
||||||
|
First, find out where the media got mounted, e.g. by looking at the output of
|
||||||
|
the `mount` command. Then, locate the directory with the backup of your GnuPG
|
||||||
|
directory and tell GnuPG to use that as its home:
|
||||||
|
|
||||||
|
$ export GNUPGHOME=/media/user/disks/somename/gnupg-backup
|
||||||
|
$ gpg --list-secret-keys
|
||||||
|
|
||||||
|
You want to make sure that you see `sec` and not `sec#` in the output (the `#`
|
||||||
|
means the key is not available).
|
||||||
|
|
||||||
|
##### Updating your regular GnuPG working directory
|
||||||
|
|
||||||
|
After you make any changes to your key using the offline storage, you will
|
||||||
|
want to import these changes back into your regular working directory:
|
||||||
|
|
||||||
|
$ gpg --export | gpg --home ~/.gnupg --import
|
||||||
|
$ unset GNUPGHOME
|
||||||
|
|
||||||
|
#### Extending key expiration date
|
||||||
|
|
||||||
|
The master key we created has the default expiration date of 2 years from the
|
||||||
|
date of creation. This is done both for security reasons and to make obsolete
|
||||||
|
keys eventually disappear from keyservers.
|
||||||
|
|
||||||
|
To extend the expiry date on your key by another year, just run:
|
||||||
|
|
||||||
|
$ gpg --quick-set-expire [fpr] 1y
|
||||||
|
|
||||||
|
You can also use a specific date if that is easier to remember (e.g. your
|
||||||
|
birthday, Cinco de Mayo, or Canada Day):
|
||||||
|
|
||||||
|
$ gpg --quick-set-expire [fpr] 2020-07-01
|
||||||
|
|
||||||
|
Remember to send the updated key back to keyservers:
|
||||||
|
|
||||||
|
$ gpg --send-key [fpr]
|
||||||
|
|
||||||
|
#### Revoking identities
|
||||||
|
|
||||||
|
If you need to revoke an identity (e.g. you changed employers and your old
|
||||||
|
email address is no longer valid), you can use a one-liner:
|
||||||
|
|
||||||
|
$ gpg --quick-revoke-uid [fpr] 'Alice Engineer <aengineer@example.net>'
|
||||||
|
|
||||||
|
You can also do the same with the menu mode using `gpg --edit-key [fpr]`.
|
||||||
|
|
||||||
|
Once you are done, remember to send the updated key back to keyservers:
|
||||||
|
|
||||||
|
$ gpg --send-key [fpr]
|
||||||
|
|
||||||
## Using PGP with Git
|
## Using PGP with Git
|
||||||
|
|
||||||
|
@ -882,6 +942,7 @@ your digital developer identity!
|
||||||
- [ ] Configure git to always sign annotated tags _(NICE)_
|
- [ ] Configure git to always sign annotated tags _(NICE)_
|
||||||
- [ ] Learn how commit signing and verification works _(ESSENTIAL)_
|
- [ ] Learn how commit signing and verification works _(ESSENTIAL)_
|
||||||
- [ ] Configure git to always sign commits _(NICE)_
|
- [ ] Configure git to always sign commits _(NICE)_
|
||||||
|
- [ ] Configure gpg-agent options _(ESSENTIAL)_
|
||||||
|
|
||||||
### Considerations
|
### Considerations
|
||||||
|
|
||||||
|
@ -897,14 +958,15 @@ kinds of hashes: tree hashes and commit hashes.
|
||||||
|
|
||||||
##### Tree hashes
|
##### Tree hashes
|
||||||
|
|
||||||
Every time you commit a change to a repository, git calculates checksum hashes
|
Every time you commit a change to a repository, git records checksum hashes
|
||||||
of all objects in it -- contents (blobs), directories (trees), file names and
|
of all objects in it -- contents (blobs), directories (trees), file names and
|
||||||
permissions, etc, for each subdirectory in the repository. It only does this
|
permissions, etc, for each subdirectory in the repository. It only does this
|
||||||
for trees and blobs that have changed, so as not to re-checksum the entire
|
for trees and blobs that have changed with each commit, so as not to
|
||||||
tree unnecessarily if only a small part of it was touched.
|
re-checksum the entire tree unnecessarily if only a small part of it was
|
||||||
|
touched.
|
||||||
|
|
||||||
Then it calculates the checksum of the toplevel directory, which will
|
Then it calculates and stores the checksum of the toplevel directory, which
|
||||||
inevitably be different if any part of the repository has changed.
|
will inevitably be different if any part of the repository has changed.
|
||||||
|
|
||||||
##### Commit hashes
|
##### Commit hashes
|
||||||
|
|
||||||
|
@ -920,7 +982,7 @@ made:
|
||||||
|
|
||||||
##### Hashing function
|
##### Hashing function
|
||||||
|
|
||||||
At the time of writing, git uses the SHA1 hashing mechanism to calculate
|
At the time of writing, git still uses the SHA1 hashing mechanism to calculate
|
||||||
checksums, though work is under way to transition to a stronger algorithm that
|
checksums, though work is under way to transition to a stronger algorithm that
|
||||||
is more resistant to collisions. Note, that git already includes collision
|
is more resistant to collisions. Note, that git already includes collision
|
||||||
avoidance routines, so it is believed that a successful collision attack
|
avoidance routines, so it is believed that a successful collision attack
|
||||||
|
@ -929,16 +991,17 @@ against git remains impractical.
|
||||||
#### Annotated tags and tag signatures
|
#### Annotated tags and tag signatures
|
||||||
|
|
||||||
Git tags allow developers to mark specific commits in the history of each git
|
Git tags allow developers to mark specific commits in the history of each git
|
||||||
repository. Tags can be lightweight that are more or less just a pointer at a
|
repository. Tags can be "lightweight" -- more or less just a pointer at a
|
||||||
specific commit, or they can be annotated, which becomes its own object in the
|
specific commit, or they can be "annotated," which becomes its own object in
|
||||||
git tree. An annotated tag object contains all of the following information:
|
the git tree. An annotated tag object contains all of the following
|
||||||
|
information:
|
||||||
|
|
||||||
- the checksum hash of the commit being tagged
|
- the checksum hash of the commit being tagged
|
||||||
- the tag name
|
- the tag name
|
||||||
- the information about the tagger (name, email, time of tagging)
|
- information about the tagger (name, email, time of tagging)
|
||||||
- the tag message
|
- the tag message
|
||||||
|
|
||||||
A PGP-signed tag is simply an annotated tag with all these contents wrapped
|
A PGP-signed tag is simply an annotated tag with all these entries wrapped
|
||||||
around in a PGP signature. When a developer signs their git tag, they
|
around in a PGP signature. When a developer signs their git tag, they
|
||||||
effectively assure you of the following:
|
effectively assure you of the following:
|
||||||
|
|
||||||
|
@ -950,17 +1013,19 @@ effectively assure you of the following:
|
||||||
- it also includes all information about authorship
|
- it also includes all information about authorship
|
||||||
- including exact times when changes were made
|
- including exact times when changes were made
|
||||||
|
|
||||||
When you clone a git repository and verify a signed tag, this gives you
|
When you clone a git repository and verify a signed tag, that gives you
|
||||||
assurances that _all contents in the repositry are exactly the same as the
|
assurances that _all contents in the repository, including all of its
|
||||||
contents of the repository on the developer's computer at the time of
|
history, are exactly the same as the contents of the repository on the
|
||||||
signing_.
|
developer's computer at the time of signing_.
|
||||||
|
|
||||||
#### Signed commits
|
#### Signed commits
|
||||||
|
|
||||||
Signed commits are very similar to signed tags, except that the contents of
|
Signed commits are very similar to signed tags -- the contents of the commit
|
||||||
the commit object are PGP-signed instead of the contents of the tag object. A
|
object are PGP-signed instead of the contents of the tag object. A commit
|
||||||
commit signature also gives you full verifiable information about the state of
|
signature also gives you full verifiable information about the state of the
|
||||||
the developer's tree at the time the signature was made.
|
developer's tree at the time the signature was made. Tag signatures and commit
|
||||||
|
PGP signatures provide exact same security assurances about the repository and
|
||||||
|
its entire history.
|
||||||
|
|
||||||
#### Signed pushes
|
#### Signed pushes
|
||||||
|
|
||||||
|
@ -1012,8 +1077,8 @@ To verify a signed tag, simply pass the `-v` switch to the tag command:
|
||||||
$ git tag -v [tagname]
|
$ git tag -v [tagname]
|
||||||
|
|
||||||
If you are verifying someone else's git tag, then you will need to import
|
If you are verifying someone else's git tag, then you will need to import
|
||||||
their PGP key. Please refer to the "maintaining the project keyring" section
|
their PGP key. Please refer to the "Trusted Team communication" document in
|
||||||
below.
|
the same repository for guidance on this topic.
|
||||||
|
|
||||||
##### Verifying at pull time
|
##### Verifying at pull time
|
||||||
|
|
||||||
|
@ -1048,18 +1113,20 @@ switch:
|
||||||
#### How to work with signed commits
|
#### How to work with signed commits
|
||||||
|
|
||||||
It is easy to create signed commits, but it is much more difficult to
|
It is easy to create signed commits, but it is much more difficult to
|
||||||
incorporate them into your workflow. Most projects use signed commits as a
|
incorporate them into your workflow. Many projects use signed commits as a
|
||||||
cryptographically-verifiable "Committed-by:" line that records code
|
sort of "Committed-by:" line equivalent that records code provenance -- the
|
||||||
provenance -- the commits are rarely verified by others except when tracking
|
signatures are rarely verified by others except when tracking down project
|
||||||
down project history.
|
history. In a sense, signed commits are used for "tamper evidence," and not to
|
||||||
|
"tamper-proof" the repository.
|
||||||
|
|
||||||
To create a signed commit, you just need to pass the `-S` flag to the `git
|
To create a signed commit, you just need to pass the `-S` flag to the `git
|
||||||
commit` command:
|
commit` command (it's capital `-S` due to collision with another flag):
|
||||||
|
|
||||||
$ git commit -S
|
$ git commit -S
|
||||||
|
|
||||||
Our recommendation is to always sign commits and to require them of all
|
Our recommendation is to always sign commits and to require them of all
|
||||||
project members, regardless of whether anyone is verifying them.
|
project members, regardless of whether anyone is verifying them (that can
|
||||||
|
always come at a later time).
|
||||||
|
|
||||||
##### How to verify signed commits
|
##### How to verify signed commits
|
||||||
|
|
||||||
|
@ -1067,7 +1134,7 @@ To verify a single commit you can use `verify-commit`:
|
||||||
|
|
||||||
$ git verify-commit [hash]
|
$ git verify-commit [hash]
|
||||||
|
|
||||||
You can also look at the repository log and request that all commit signatures
|
You can also look at repository logs and request that all commit signatures
|
||||||
are verified and shown:
|
are verified and shown:
|
||||||
|
|
||||||
$ git log --pretty=short --show-signatures
|
$ git log --pretty=short --show-signatures
|
||||||
|
@ -1080,9 +1147,10 @@ the `-S` flag):
|
||||||
|
|
||||||
$ git merge --verify-signatures -S merged-branch
|
$ git merge --verify-signatures -S merged-branch
|
||||||
|
|
||||||
Note, that this will break if there is even one commit that is not signed or
|
Note, that the merge will fail if there is even one commit that is not signed
|
||||||
does not pass verification. As it is often the case, technology is the easy
|
or does not pass verification. As it is often the case, technology is the easy
|
||||||
part here, but the human side of the equation is what makes it difficult.
|
part -- the human side of the equation is what makes adopting strict commit
|
||||||
|
signing difficult.
|
||||||
|
|
||||||
##### If your project uses mailing lists for patch management
|
##### If your project uses mailing lists for patch management
|
||||||
|
|
||||||
|
@ -1090,7 +1158,108 @@ If your project uses a mailing list for submitting and processing patches,
|
||||||
then there is little use in signing commits, because all signature information
|
then there is little use in signing commits, because all signature information
|
||||||
will be lost when sent through that medium. It is still useful to sign your
|
will be lost when sent through that medium. It is still useful to sign your
|
||||||
commits, just so others can refer to your publicly hosted git trees for
|
commits, just so others can refer to your publicly hosted git trees for
|
||||||
reference, but the upstream developer will not benefit from it in a direct
|
reference, but the upstream project receiving your patches will not be able to
|
||||||
way.
|
verify them directly with git.
|
||||||
|
|
||||||
You can still sign the emails containing the patches, though.
|
You can still sign the emails containing the patches, though.
|
||||||
|
|
||||||
|
#### Configure git to always sign commits
|
||||||
|
|
||||||
|
You can tell git to always sign commits:
|
||||||
|
|
||||||
|
git config --global commit.gpgSign true
|
||||||
|
|
||||||
|
Or you can train your muscle memory to always pass the -S flag to all git
|
||||||
|
commit operations.
|
||||||
|
|
||||||
|
#### Configure gpg-agent options
|
||||||
|
|
||||||
|
The GnuPG agent is a helper tool that will start automatically whenever you
|
||||||
|
use the `gpg` command and run on the background with the purpose of caching
|
||||||
|
the private key passphrase. This way you only have to unlock your key once to
|
||||||
|
use it repeatedly (very handy if you need to sign a bunch of git operations in
|
||||||
|
an automated script without having to continuously retype your passphrase).
|
||||||
|
|
||||||
|
There are two options you should know in order to tweak when the passphrase
|
||||||
|
should be expired from cache:
|
||||||
|
|
||||||
|
- `default-cache-ttl` (seconds): If you use the same key again before the
|
||||||
|
time-to-live expires, the countdown will reset for another period.
|
||||||
|
The default is 600 (10 minutes).
|
||||||
|
- `max-cache-ttl` (seconds): Regardless of how recently you've used the key
|
||||||
|
since initial passphrase entry, if the maximum time-to-live countdown
|
||||||
|
expires, you'll have to enter the passphrase again. The default is 30
|
||||||
|
minutes.
|
||||||
|
|
||||||
|
If you find either of these defaults too short (or too long), you can edit
|
||||||
|
your `~/.gnupg/gpg-agent.conf` file to set your own values:
|
||||||
|
|
||||||
|
# set to 30 minutes for regular ttl, and 2 hours for max ttl
|
||||||
|
default-cache-ttl 1800
|
||||||
|
max-cache-ttl 7200
|
||||||
|
|
||||||
|
##### Bonus: Using gpg-agent with ssh
|
||||||
|
|
||||||
|
If you've created an **[A]** (Authentication) key and moved it to the
|
||||||
|
smartcard, you can use it with ssh for adding 2-factor authentication for your
|
||||||
|
ssh sessions. You just need to tell your environment to use the correct socket
|
||||||
|
file for talking to the agent.
|
||||||
|
|
||||||
|
First, add the following to your `~/.gnupg/gpg-agent.conf`:
|
||||||
|
|
||||||
|
enable-ssh-support
|
||||||
|
|
||||||
|
Then, add this to your `.bashrc`:
|
||||||
|
|
||||||
|
export SSH_AUTH_SOCK=$(gpgconf --list-dirs agent-ssh-socket)
|
||||||
|
|
||||||
|
You will need to kill existing `gpg-agent` sessions and start a new login
|
||||||
|
session:
|
||||||
|
|
||||||
|
$ killall gpg-agent
|
||||||
|
$ bash
|
||||||
|
$ ssh-add -L
|
||||||
|
|
||||||
|
The last command should list the SSH representation of your PGP Auth key (the
|
||||||
|
comment should say `cardno:XXXXXXX` at the end to indicate it's coming from
|
||||||
|
the smartcard).
|
||||||
|
|
||||||
|
To enable key-based logins with ssh, just add the above output to
|
||||||
|
`~/.ssh/authorized_keys` on remote systems you log in to. Congratulations,
|
||||||
|
you've just made your ssh credentials extremely difficult to steal.
|
||||||
|
|
||||||
|
As a bonus, you can get other people's PGP-based ssh keys from public
|
||||||
|
keyservers, should you need to grant them ssh-based access to anything:
|
||||||
|
|
||||||
|
$ gpg --export-ssh-key [keyid]
|
||||||
|
|
||||||
|
This can come in super handy if you need to allow developers access to git
|
||||||
|
repositories over ssh.
|
||||||
|
|
||||||
|
## TODO: Tarball release signatures
|
||||||
|
|
||||||
|
## Further reading
|
||||||
|
|
||||||
|
By this point you have accomplished the following important tasks:
|
||||||
|
|
||||||
|
1. Created your developer identity and protected it using PGP cryptography.
|
||||||
|
2. Configured your environment so your identity is not easily stolen by moving
|
||||||
|
your master key offline and your subkeys to an external hardware device.
|
||||||
|
3. Configured your git environment to ensure that anyone using your project is
|
||||||
|
able to verify the integrity of the repository and its entire history.
|
||||||
|
|
||||||
|
You are already in a good place, but you should also read up on the following
|
||||||
|
topics:
|
||||||
|
|
||||||
|
- How to secure your team communication (see the document in this repository).
|
||||||
|
Decisions regarding your project development and governance require just as
|
||||||
|
much careful protection as any committed code, if not so. Make sure that
|
||||||
|
your team communication is trusted and the integrity of all decisions is
|
||||||
|
verified.
|
||||||
|
- How to secure your workstation (see the document in this repository). Your
|
||||||
|
goal is to minimize risky behaviour that would cause your project code to be
|
||||||
|
contaminated, or your developer identity to be stolen.
|
||||||
|
- How to write secure code (see various documentation related to the
|
||||||
|
programming languages and libraries used by your project). Bad, insecure
|
||||||
|
code is still bad, insecure code even if there is a PGP signature on the
|
||||||
|
commit that introduced it.
|
Loading…
Reference in New Issue