Use git-secret to encrypt secrets in the repository [Tutorial Part 6]
Tags: git, gitsecret, docker, security
Published: 2022-07-04 05:00:51

How to use git-secret to encrypt secrets and store them in a git repository

<p>This article appeared first on <a href='https://www.pascallandau.com/'>https://www.pascallandau.com/</a> at <a href='https://www.pascallandau.com/blog/git-secret-encrypt-repository-docker/'>Use git-secret to encrypt secrets in the repository [Tutorial Part 6]</a></p>
<hr />

In the sixth part of this tutorial series on developing PHP on Docker we will **setup `git-secret` to store secrets directly in the repository**. Everything will be handled through Docker and  added as make targets for a convenient workflow.

[![git-secret example](https://www.pascallandau.com/img/git-secret-encrypt-repository-docker/git-secret-example.gif "git-secret example")](https://www.pascallandau.com/img/git-secret-encrypt-repository-docker/git-secret-example.gif)

<small>
FYI:  This tutorial is a precursor to the next a part 
<a href="https://www.pascallandau.com/blog/ci-pipeline-docker-php-gitlab-github/">Create a CI pipeline for dockerized PHP Apps</a>
because dealing with secrets is an important aspect when setting up a CI system (and later when  deploying to production) - but I feel it's complex enough to warrant its own article.
</small>


{% youtube pZ-vFMfKcLY %}


**All code samples are publicly available** in my [Docker PHP Tutorial repository on Github](https://github.com/paslandau/docker-php-tutorial/).   You find the branch with the final result of this tutorial at [part-6-git-secret-encrypt-repository-docker](https://github.com/paslandau/docker-php-tutorial/tree/part-6-git-secret-encrypt-repository-docker).

**All published parts of the Docker PHP Tutorial** are collected under a dedicated page at [Docker PHP Tutorial](https://www.pascallandau.com/docker-php-tutorial/). The previous part was [Set up PHP QA tools and control them via make](https://www.pascallandau.com/blog/php-qa-tools-make-docker/) and the following one is [Create a CI pipeline for dockerized PHP Apps](https://www.pascallandau.com/blog/ci-pipeline-docker-php-gitlab-github/).

If you want to follow along, please subscribe to the [RSS feed](https://www.pascallandau.com/feed.xml) or [via email](https://www.pascallandau.com/blog/#newsletter) to get **automatic notifications** when the next part comes out :)

<!-- generated -->
<a id='table-of-contents'> </a>
<!-- /generated -->

## Table of contents

<!-- toc -->

- [Introduction](#introduction)
- [Tooling](#tooling)
    - [gpg](#gpg)
        - [gpg installation](#gpg-installation)
        - [gpg usage](#gpg-usage)
            - [Create GPG key pair](#create-gpg-key-pair)
            - [Export, list and import private GPG keys](#export-list-and-import-private-gpg-keys)
            - [Export, list and import public GPG keys](#export-list-and-import-public-gpg-keys)
    - [git-secret](#git-secret)
        - [git-secret installation](#git-secret-installation)
            - [The `git` permission issue](#the-git-permission-issue)
        - [git-secret usage](#git-secret-usage)
            - [Initialize git-secret](#initialize-git-secret)
                - [The `git-secret` directory and the `gpg-agent` socket](#the-git-secret-directory-and-the-gpg-agent-socket)
            - [Adding, listing and removing users](#adding-listing-and-removing-users)
                - [Reminder: Rotate the encrypted secrets](#reminder-rotate-the-encrypted-secrets)
            - [Adding, listing and removing files for encryption](#adding-listing-and-removing-files-for-encryption)
            - [Encrypt files](#encrypt-files)
            - [Decrypting files](#decrypting-files)
            - [Show changes between encrypted and decrypted files](#show-changes-between-encrypted-and-decrypted-files)
- [Makefile adjustments](#makefile-adjustments)
- [Workflow](#workflow)
    - [Process challenges](#process-challenges)
        - [Updating secrets](#updating-secrets)
        - [Code reviews and merge conflicts](#code-reviews-and-merge-conflicts)
        - [Local `git-secret` and `gpg` setup](#local-git-secret-and-gpg-setup)
    - [Scenarios](#scenarios)
        - [Initial setup of `gpg` keys](#initial-setup-of-gpg-keys)
        - [Initial setup of `git-secret`](#initial-setup-of-git-secret)
        - [Initialize `gpg` after container startup](#initialize-gpg-after-container-startup)
        - [Adding (new) team members](#adding-new-team-members)
        - [Adding and encrypting files](#adding-and-encrypting-files)
        - [Decrypt files](#decrypt-files)
        - [Removing files](#removing-files)
        - [Removing team members](#removing-team-members)
- [Pros and cons](#pros-and-cons)
    - [Pro](#pro)
    - [Cons](#cons)
- [Wrapping up](#wrapping-up)

<!-- /toc -->

<!-- generated -->
<a id='introduction'> </a>
<!-- /generated -->

## Introduction

Dealing with secrets (passwords, tokens, key files, etc.) is close to "naming things" when it comes to hard problems in software engineering. Some things to consider:

- **security is paramount** - but high security often goes hand in hand with high inconvenience
  - and if things get too complicated, people look for shortcuts...
- in a team, **sharing certain secret values** is often mandatory
  - so now we need to think about secure ways to distribute and update secrets across multiple
    people
- concrete secret values often **depend on the environment**
  - inherently tricky to "test" or even "review", because those values are "by definition"
    different on "your machine" than on "production"

In fact, entire products have been build around dealing with secrets, e.g. [HashiCorp Vault](https://www.vaultproject.io/), [AWS Secrets Manager](https://aws.amazon.com/secrets-manager/) or the [GCP Secret Manager](https://cloud.google.com/secret-manager). Introducing those in a project comes with a certain overhead as it's yet another service that needs to be integrated and   maintained. Maybe it is the exactly right decision for your use-case - maybe it's overkill. By the end of this article you'll at least be aware of an alternative with a lower barrier to entry. See also the [Pros and cons](#pros-and-cons) section in the end for an overview.

Even though it's [generally not advised to store secrets in a repository](https://withblue.ink/2021/05/07/storing-secrets-and-passwords-in-git-is-bad.html), I'll propose exactly that in this tutorial:

- identify files that contain secret values
- make sure they are added to `.gitignore`
- encrypt them via `git-secret`
- commit the encrypted files to the repository

In the end, we will be able to call

```bash
make secret-decrypt
```

to reveal secrets in the codebase, make modifications to them if necessary and then run

```bash
make secret-encrypt
```

to encrypt them again so that they can be committed (and pushed to the remote repository). To  see it in action, check out branch [part-6-git-secret-encrypt-repository-docker](https://github.com/paslandau/docker-php-tutorial/tree/git-secret-encrypt-repository-docker) and run the following commands:

```bash
# checkout the branch
git checkout part-6-git-secret-encrypt-repository-docker

# build and start the docker setup
make make-init
make docker-build
make docker-up

# "create" the secret key - the file "secret.gpg.example" would usually NOT live in the repo!
cp secret.gpg.example secret.gpg

# initialize gpg
make gpg-init

# ensure that the decrypted secret file does not exist
ls passwords.txt

# decrypt the secret file
make secret-decrypt

# show the content of the secret file
cat passwords.txt
```

<!-- generated -->
<a id='tooling'> </a>
<!-- /generated -->

## Tooling

We will set up `gpg` and `git-secret` in the php `base` image, so that the tools become available in all other containers. Please refer to [Docker from scratch for PHP 8.1 Applications in 2022](https://www.pascallandau.com/blog/docker-from-scratch-for-php-applications-in-2022/) for an in-depth explanation of the docker images.

<div class="panel panel-default">
  <div class="panel-heading">
    <strong>Caution</strong>
  </div>
  <div class="panel-body bg-danger">
    All following commands are 
    <strong>executed <em>in</em> the <code>application</code> container.</strong>
    <br />
    <br />
    <strong>Tip:</strong>
    <br />
    See <a href="https://www.pascallandau.com/blog/structuring-the-docker-setup-for-php-projects/#easy-container-access-via -din-bashrc-helper">Easy container access via din .bashrc helper</a>
    for a convenient shortcut to log into docker containers.
  </div>
</div>


Please note, that there is a caveat when using `git-secret` in a folder that is shared between  the host system and a docker container. I'll explain that in more detail (including a workaround)  in section  [The `git-secret` directory and the `gpg-agent` socket](#the-git-secret-directory-and-the-gpg-agent-socket).

<!-- generated -->
<a id='gpg'> </a>
<!-- /generated -->

### gpg

`gpg` is short for [The GNU Privacy Guard](https://gnupg.org/) and is an open source implementation of the OpenPGP standard. In short, it allows us to create a personal key file pair (similar to SSH keys) with a private secret key and a public key that can be shared with other parties whose messages you want to decrypt.

<!-- generated -->
<a id='gpg-installation'> </a>
<!-- /generated -->

#### gpg installation

To install it, we can simply run `apk add gnupg` and thus update  `.docker/images/php/base/Dockerfile` accordingly

```Dockerfile
# File: .docker/images/php/base/Dockerfile

RUN apk add --update --no-cache \
        bash \
        gnupg \
        make \
#...
```

<!-- generated -->
<a id='gpg-usage'> </a>
<!-- /generated -->

#### gpg usage

I'll only cover the strictly necessary `gpg` commands here. Please refer to [the "Using GPG" section in the `git-secret` docu](https://git-secret.io/#using-gpg) and/or [How to generate PGP keys with GPG](https://linuxhint.com/generate-gpg-keys-gpg/) for further information.

<!-- generated -->
<a id='create-gpg-key-pair'> </a>
<!-- /generated -->

##### Create GPG key pair

We need `gpg` to **create the gpg key pair** via

```bash
name="Pascal Landau"
email="pascal.landau@example.com"
gpg --batch --gen-key <<EOF
Key-Type: 1
Key-Length: 2048
Subkey-Type: 1
Subkey-Length: 2048
Name-Real: $name
Name-Email: $email
Expire-Date: 0
%no-protection
EOF
```

The `%no-protection` will create a key without password, see also [this gist to "Creating gpg keys non-interactively"](https://gist.github.com/woods/8970150). To use a password (e.g. `12345678`, we could have replace the `%no-protection` line with

```text
Passphrase: 12345678
```

All options for the unattended creation are defined in the  [official docs at "Unattended key generation"](https://www.gnupg.org/documentation/manuals/gnupg/Unattended-GPG-key-generation.html).

Output:

```text
$ name="Pascal Landau"
$ email="pascal.landau@example.com"
$ gpg --batch --gen-key <<EOF
> Key-Type: 1
> Key-Length: 2048
> Subkey-Type: 1
> Subkey-Length: 2048
> Name-Real: $name
> Name-Email: $email
> Expire-Date: 0
> %no-protection
> EOF
gpg: key E1E734E00B611C26 marked as ultimately trusted
gpg: revocation certificate stored as '/root/.gnupg/opengpg-revocs.d/74082D81525723F5BF5B2099E1E734E00B611C26.rev'
```

You could also run `gpg --gen-key` without the `--batch` flag to be guided interactively through the process.

<!-- generated -->
<a id='export-list-and-import-private-gpg-keys'> </a>
<!-- /generated -->

##### Export, list and import private GPG keys

The **private key can be exported** via

```bash
email="pascal.landau@example.com"
path="secret.gpg"
gpg --output "$path" --armor --export-secret-key "$email"
```

**This secret key must never be shared**!

It looks like this:

```text
-----BEGIN PGP PRIVATE KEY BLOCK-----

lQOYBF7VVBwBCADo9un+SySu/InHSkPDpFVKuZXg/s4BbZmqFtYjvUUSoRAeSejv
G21nwttQGut+F+GdpDJL6W4pmLS31Kxpt6LCAxhID+PRYiJQ4k3inJfeUx7Ws339
XDPO3Rys+CmnZchcEgnbOfQlEqo51DMj6mRF2Ra/6svh7lqhrixGx1BaKn6VlHkC
...
ncIcHxNZt7eK644nWDn7j52HsRi+wcWsZ9mjkUgZLtyMPJNB5qlKQ18QgVdEAhuZ
xT3SieoBPd+tZikhu3BqyIifmLnxOJOjOIhbQrgFiblvzU1iOUOTOcSIB+7A
=YmRm
-----END PGP PRIVATE KEY BLOCK-----
```

All **secret keys can be listed** via

```bash
gpg --list-secret-keys
```

Output:

```text
$ gpg --list-secret-keys
/root/.gnupg/pubring.kbx
------------------------
sec   rsa2048 2022-03-27 [SCEA]
      74082D81525723F5BF5B2099E1E734E00B611C26
uid           [ultimate] Pascal Landau <pascal.landau@example.com>
ssb   rsa2048 2022-03-27 [SEA]

```

You can **import the private key** via

```bash
path="secret.gpg"
gpg --import "$path"
```

and get the following output:

```text
$ path="secret.gpg"
$ gpg --import "$path"
gpg: key E1E734E00B611C26: "Pascal Landau <pascal.landau@example.com>" not changed
gpg: key E1E734E00B611C26: secret key imported
gpg: Total number processed: 1
gpg:              unchanged: 1
gpg:       secret keys read: 1
gpg:  secret keys unchanged: 1
```

**Caution:** If the secret key requires a password, you would now be prompted for it. We can  circumvent the prompt by using `--batch --yes --pinentry-mode loopback`:

```bash
path="secret.gpg"
gpg --import --batch --yes --pinentry-mode loopback "$path"
```

See also [Using Command-Line Passphrase Input for GPG](https://betakuang.medium.com/using-command-line-passphrase-input-for-gpg-with-git-for-windows-f78ae2c7cd2e). In doing so, we don't need to provide the password just yet - but we must pass it later when we  attempt to [decrypt files](#decrypting-files).

<!-- generated -->
<a id='export-list-and-import-public-gpg-keys'> </a>
<!-- /generated -->

##### Export, list and import public GPG keys

The **public key can be exported** to `public.gpg` via

```bash
email="pascal.landau@example.com"
path="public.gpg"
gpg --armor --export "$email" > "$path"
```

It looks like this:

```text
-----BEGIN PGP PUBLIC KEY BLOCK-----

mQENBF7VVBwBCADo9un+SySu/InHSkPDpFVKuZXg/s4BbZmqFtYjvUUSoRAeSejv
G21nwttQGut+F+GdpDJL6W4pmLS31Kxpt6LCAxhID+PRYiJQ4k3inJfeUx7Ws339
...
3LLbK7Qxz0cV12K7B+n2ei466QAYXo03a7WlsPWn0JTFCsHoCOphjaVsncIcHxNZ
t7eK644nWDn7j52HsRi+wcWsZ9mjkUgZLtyMPJNB5qlKQ18QgVdEAhuZxT3SieoB
Pd+tZikhu3BqyIifmLnxOJOjOIhbQrgFiblvzU1iOUOTOcSIB+7A
=g0hF
-----END PGP PUBLIC KEY BLOCK-----
```

**List all public keys** via

```bash
gpg --list-keys
```

Output:

```text
$ gpg --list-keys
/root/.gnupg/pubring.kbx
------------------------
pub   rsa2048 2022-03-27 [SCEA]
      74082D81525723F5BF5B2099E1E734E00B611C26
uid           [ultimate] Pascal Landau <pascal.landau@example.com>
sub   rsa2048 2022-03-27 [SEA]
```

The **public key can be imported** in the same way as private keys via

```bash
path="public.gpg"
gpg --import "$path"
```

Example:

```text
$ gpg --import /var/www/app/public.gpg
gpg: key E1E734E00B611C26: "Pascal Landau <pascal.landau@example.com>" not changed
gpg: Total number processed: 1
gpg:              unchanged: 1
```

<!-- generated -->
<a id='git-secret'> </a>
<!-- /generated -->

### git-secret

The official website of [git-secret](https://git-secret.io/) is already doing a great job of introducing the tool. In short, it allows us to **declare certain files as "secrets"** and **encrypt them via `gpg`** - using the keys of all trusted parties. The encrypted file can then by **stored safely directly in the git repository** and **decrypted if required**.

In this tutorial I'm using `git-secret v0.4.0`

```text
$ git secret --version
0.4.0
```

<!-- generated -->
<a id='git-secret-installation'> </a>
<!-- /generated -->

#### git-secret installation

The [installation instructions for Alpine](https://git-secret.io/installation#alpine) read as follows:

```bash
sh -c "echo 'https://gitsecret.jfrog.io/artifactory/git-secret-apk/all/main'" >> /etc/apk/repositories
wget -O /etc/apk/keys/git-secret-apk.rsa.pub 'https://gitsecret.jfrog.io/artifactory/api/security/keypair/public/repositories/git-secret-apk'
apk add --update --no-cache git-secret
```

Plus, we need to account for a recent change in `git` that requires that the parent directory is  owned by the user executing the `git` command. See also the more detailed explanation in section [The `git` permission issue](#the-git-permission-issue).

We update the `.docker/images/php/base/Dockerfile` accordingly:

```Dockerfile
# File: .docker/images/php/base/Dockerfile

# install git-secret
# @see https://git-secret.io/installation#alpine
ADD https://gitsecret.jfrog.io/artifactory/api/security/keypair/public/repositories/git-secret-apk /etc/apk/keys/git-secret-apk.rsa.pub

RUN echo "https://gitsecret.jfrog.io/artifactory/git-secret-apk/all/main" >> /etc/apk/repositories  && \
    apk add --update --no-cache \
        bash \
        git-secret \
        gawk \
        gnupg \
        make \
#...

# Fix the git permission issue
RUN git config --system --add safe.directory "$APP_CODE_PATH"
```

<!-- generated -->
<a id='the-git-permission-issue'> </a>
<!-- /generated -->

##### The `git` permission issue

In April 2022,  [Github accounced the security vulnerability `CVE-2022-24765`](https://github.blog/2022-04-12-git-security-vulnerability-announced/), that was fixed in `git v2.35.2`

> This version changes Git’s behavior when looking for a top-level `.git` directory to stop when 
> its directory traversal changes ownership from the current user. 

In practice, the following error occurs if the parent directory is not owned by the user that  executes the `git` command

```text
Error: fatal: unsafe repository ('/parent/dir/of/.git-folder' is owned by someone else)
To add an exception for this directory, call:

	git config --global --add safe.directory /parent/dir/of/.git-folder
```

When using `git secret`, we would get the slightly misleading error message

```text
git-secret: abort: not in dir with git repo. Use 'git init' or 'git clone', then in repo use 'git secret init'
```

We can "fix" the issue by using the new multi-valued  [safe.directory](https://git-scm.com/docs/git-config/2.36.0#Documentation/git-config.txt-safedirectory)  configuration via

```
git config --system --add safe.directory /parent/dir/of/.git-folder
```

Note, that we didn't use the suggested `--global` option but `--system` instead, so that the  configuration is set for _any_ user.

Wait - why not just ensure **that the parent directory of the `.git` folder has the correct  permissions**?

Well... there's currently (2022-05-28) a **bug in Docker Desktop that makes the permissions of bind  mounts kinda unpredictable**, see  [Ownership of files set via bind mount is set to user who accesses the file first](https://github.com/docker/for-win/issues/12742) and by applying the fix directly in the `Dockerfile` we can solve the issue reliably.

<!-- generated -->
<a id='git-secret-usage'> </a>
<!-- /generated -->

#### git-secret usage

<!-- generated -->
<a id='initialize-git-secret'> </a>
<!-- /generated -->

##### Initialize git-secret

`git-secret` is initialized via the following command _run in the root of the git repository_

```bash
git secret init
```

```text
$ git secret init
git-secret: init created: '/var/www/app/.gitsecret/'
```

We only need to do this once, because we'll commit the folder to git later. It contains the following files:

```text
$ git status | grep ".gitsecret"
        new file:   .gitsecret/keys/pubring.kbx
        new file:   .gitsecret/keys/pubring.kbx~
        new file:   .gitsecret/keys/trustdb.gpg
        new file:   .gitsecret/paths/mapping.cfg
```

The `pubring.kbx~` file (with the trailing tilde `~`) is only a temporary file and can safely be git-ignored. See also [Can't find any docs about keyring.kbx~ file](https://github.com/sobolevn/git-secret/issues/566#issuecomment-570059374).

<!-- generated -->
<a id='the-git-secret-directory-and-the-gpg-agent-socket'> </a>
<!-- /generated -->

###### The `git-secret` directory and the `gpg-agent` socket

To use `git-secret` in a directory that is **shared between the host system and docker**, we need to  also run the following commands:

```bash
tee .gitsecret/keys/S.gpg-agent <<EOF
%Assuan%
socket=/tmp/S.gpg-agent
EOF

tee .gitsecret/keys/S.gpg-agent.ssh <<EOF
%Assuan%
socket=/tmp/S.gpg-agent.ssh
EOF

tee .gitsecret/keys/gpg-agent.conf <<EOF
extra-socket /tmp/S.gpg-agent.extra
browser-socket /tmp/S.gpg-agent.browser
EOF
```

This is necessary because there is an issue **when `git-secret` is used in a setup where the  codebase is shared between the host system and a docker container**.  I've explained the details in the Github issue ["gpg: can't connect to the agent: IPC connect call failed" error in docker alpine on shared volume](https://github.com/sobolevn/git-secret/issues/806).

In short:
- `gpg` uses a `gpg-agent` to perform its tasks and the two tools communicate through sockets 
  that are created in the `--home-directory` of the `gpg-agent`
- the agent is started implicitly through a `gpg` command used by `git-secret`, using the 
  `.gitsecret/keys` directories as a `--home-directory`
- because the location of the `--home-directory` is shared with the host system, the socket 
  creation fails (potentially only an issue for Docker Desktop, see the related discussion in 
  Github issue [Support for sharing unix sockets](https://github.com/docker/for-mac/issues/483#issuecomment-647325015))
  The corresponding error messages are

```text
gpg: can't connect to the agent: IPC connect call failed

gpg-agent: error binding socket to '/var/www/app/.gitsecret/keys/S.gpg-agent': I/O error
```

The **workaround for this problem** can be found in  [this thread](https://askubuntu.com/a/1053594/1583296): Configure `gpg` to use different  locations for the sockets by  [placing additional `gpg` configuration files in the `.gitsecret/keys` directory](https://github.com/sobolevn/git-secret/issues/806#issuecomment-1084202671):

**S.gpg-agent**

```text
%Assuan%
socket=/tmp/S.gpg-agent
```

**S.gpg-agent.ssh**

```text
%Assuan%
socket=/tmp/S.gpg-agent.ssh
```

**gpg-agent.conf**

```text
extra-socket /tmp/S.gpg-agent.extra
browser-socket /tmp/S.gpg-agent.browser
```

<!-- generated -->
<a id='adding-listing-and-removing-users'> </a>
<!-- /generated -->

##### Adding, listing and removing users

To **add a new user**, you must first [import its public gpg key](#export-list-and-import-public-gpg-keys). Then run:

```bash
email="pascal.landau@example.com"
git secret tell "$email"
```

In this case, the user `pascal.landau@example.com` will now be able to decrypt the secrets.

To **show the users** run

```bash
git secret whoknows
```

```text
$ git secret whoknows
pascal.landau@example.com
```

**To remove a user**, run

```bash
email="pascal.landau@example.com"
git secret killperson "$email"
```

FYI: This command was renamed to `removeperson` in `git-secret >= 0.5.0`

```text
$ git secret killperson pascal.landau@example.com
git-secret: removed keys.
git-secret: now [pascal.landau@example.com] do not have an access to the repository.
git-secret: make sure to hide the existing secrets again.
```

User `pascal.landau@example.com` will no longer be able to decrypt the secrets.

**Caution: The secrets need to be re-encrypted after removing a user!**

<!-- generated -->
<a id='reminder-rotate-the-encrypted-secrets'> </a>
<!-- /generated -->

###### Reminder: Rotate the encrypted secrets

Please be aware that **not only your secrets are stored in git, but who had access as well**. I.e.  even if you remove a user and re-encrypt the secrets, that user would **still be able to decrypt  the secrets of a previous commit** (when the user was still added). In consequence, **you need  to rotate the encrypted secrets themselves as well after removing a user**.

But isn't that a great flaw in the system, making it a bad idea to use `git-secret` in general? 

In my opinion: No. 

If the removed user had access to the secrets at **any** point in time (no  matter where they have been stored), he could very well have just created a local copy or simply  "written them down". In terms of security there is really no "added downside" due to `git-secret`. It just makes it _very_ clear that you _must_ rotate the secrets ¯\\\_(ツ)\_/¯

See also this  [lengthy discussion on `git-secret` on Hacker News](https://news.ycombinator.com/item?id=11663403).

<!-- generated -->
<a id='adding-listing-and-removing-files-for-encryption'> </a>
<!-- /generated -->

##### Adding, listing and removing files for encryption

Run `git secret add [filenames...]` for **files you want to encrypt**. Example:

```bash
git secret add .env
```

If `.env` is not added in `.gitignore`, `git-secret` will display a warning and add it  automatically.

```text
git-secret: these files are not in .gitignore: .env
git-secret: auto adding them to .env
git-secret: 1 item(s) added.
```

Otherwise, the file is added with no warning.

```text
$ git secret add .env
git-secret: 1 item(s) added.
```

You only need to add files once. They are then stored in `.gitsecret/paths/mapping.cfg`:

```text
$ cat .gitsecret/paths/mapping.cfg
.env:505070fc20233cb426eac6a3414399d0f466710c993198b1088e897fdfbbb2d5
```

You can also show the added files via

```bash
git secret list
```

```text
$ git secret list
.env
```

**Caution: The files are not yet encrypted!**


If you want to **remove a file from being encrypted**, run

```bash
git secret remove .env
```

Output

```text
$ git secret remove .env
git-secret: removed from index.
git-secret: ensure that files: [.env] are now not ignored.
```

<!-- generated -->
<a id='encrypt-files'> </a>
<!-- /generated -->

##### Encrypt files

To actually **encrypt the files**, run:

```bash
git secret hide
```

Output:

```text
$ git secret hide
git-secret: done. 1 of 1 files are hidden.
```

The encrypted (binary) file is stored at `$filename.secret`, i.e. `.env.secret` in this case:

```text
$ cat .env.secret
�☺♀♥�H~�B�Ӯ☺�"��▼♂F�►���l�Cs��S�@MHWs��e������{♣♫↕↓�L� ↕s�1�J$◄♥�;���ǆ֕�Za�����\u�ٲ& ¶��V�► ���6��
;<�d:��}ҨD%.�;��&��G����vWW�]>���߶��▲;D�+Rs�S→�Y!&J��۪8���ٔF��→f����*��$♠���&RC�8▼♂�☻z h��Z0M�T>
```

The encrypted files are de-cryptable **for all users that have been added via `git secret tell`**.  That also means that you need to **run this command again whenever a new user is added**.

<!-- generated -->
<a id='decrypting-files'> </a>
<!-- /generated -->

##### Decrypting files

You can **decrypt files** via

```bash
git secret reveal
```

Output:

```text
$ git secret reveal
File '/var/www/app/.env' exists. Overwrite? (y/N) y
git-secret: done. 1 of 1 files are revealed.
```

- the files are decrypted and will overwrite the current, unencrypted files (if they already exist)
  - use the `-f` option to force the overwrite and run non-interactively
- if you only want to check the content of an encrypted file, you can use
  `git secret cat $filename` (e.g. `git secret cat .env`)

In case the secret `gpg` key is password protected, you must pass the password  [via the `-p` option](https://git-secret.io/git-secret-reveal). E.g. for password `123456`

```bash
git secret reveal -p 123456
```

<!-- generated -->
<a id='show-changes-between-encrypted-and-decrypted-files'> </a>
<!-- /generated -->

##### Show changes between encrypted and decrypted files

One problem that comes with encrypted files: **You can't review them during a code review in a remote tool**. So in order to understand what changes have been made, it is helpful to
**show the changes between the encrypted and the decrypted files**. This can be done via

```bash
git secret changes
```

Output:

```text
$ echo "foo" >> .env
$ git secret changes
git-secret: changes in /var/www/app/.env:
--- /dev/fd/63
+++ /var/www/app/.env
@@ -34,3 +34,4 @@
 MAIL_ENCRYPTION=null
 MAIL_FROM_ADDRESS=null
 MAIL_FROM_NAME="${APP_NAME}"
+foo
```

Note the `+foo` at the bottom of the output. It was added in the first line via  `echo "foo"> >> .env`.

<!-- generated -->
<a id='makefile-adjustments'> </a>
<!-- /generated -->

## Makefile adjustments

Since I won't be able to remember all the commands for `git-secret` and `gpg`, I've added them to the Makefile at `.make/01-00-application-setup.mk`:

```makefile
# File: .make/01-00-application-setup.mk

#...

# gpg

DEFAULT_SECRET_GPG_KEY?=secret.gpg
DEFAULT_PUBLIC_GPG_KEYS?=.dev/gpg-keys/*

.PHONY: gpg
gpg: ## Run gpg commands. Specify the command e.g. via ARGS="--list-keys"
	$(EXECUTE_IN_APPLICATION_CONTAINER) gpg $(ARGS)

.PHONY: gpg-export-public-key
gpg-export-public-key: ## Export a gpg public key e.g. via EMAIL="john.doe@example.com" PATH=".dev/gpg-keys/john-public.gpg"
	@$(if $(PATH),,$(error PATH is undefined))
	@$(if $(EMAIL),,$(error EMAIL is undefined))
	"$(MAKE)" -s gpg ARGS="gpg --armor --export $(EMAIL) > $(PATH)"

.PHONY: gpg-export-private-key
gpg-export-private-key: ## Export a gpg private key e.g. via EMAIL="john.doe@example.com" PATH="secret.gpg"
	@$(if $(PATH),,$(error PATH is undefined))
	@$(if $(EMAIL),,$(error EMAIL is undefined))
	"$(MAKE)" -s gpg ARGS="--output $(PATH) --armor --export-secret-key $(EMAIL)"

.PHONY: gpg-import
gpg-import: ## Import a gpg key file e.g. via GPG_KEY_FILES="/path/to/file /path/to/file2"
	@$(if $(GPG_KEY_FILES),,$(error GPG_KEY_FILES is undefined))
	"$(MAKE)" -s gpg ARGS="--import --batch --yes --pinentry-mode loopback $(GPG_KEY_FILES)"

.PHONY: gpg-import-default-secret-key
gpg-import-default-secret-key: ## Import the default secret key
	"$(MAKE)" -s gpg-import GPG_KEY_FILES="$(DEFAULT_SECRET_GPG_KEY)"

.PHONY: gpg-import-default-public-keys
gpg-import-default-public-keys: ## Import the default public keys
	"$(MAKE)" -s gpg-import GPG_KEY_FILES="$(DEFAULT_PUBLIC_GPG_KEYS)" 

.PHONY: gpg-init
gpg-init: gpg-import-default-secret-key gpg-import-default-public-keys ## Initialize gpg in the container, i.e. import all public and private keys

# git-secret

.PHONY: git-secret
git-secret: ## Run git-secret commands. Specify the command e.g. via ARGS="hide"
	$(EXECUTE_IN_APPLICATION_CONTAINER) git-secret $(ARGS)

.PHONY: secret-init
secret-init: ## Initialize git-secret in the repository via `git-secret init`
	"$(MAKE)" -s git-secret ARGS="init"

.PHONY: secret-init-gpg-socket-config
secret-init-gpg-socket-config: ## Initialize the config files to change the gpg socket locations
	echo "%Assuan%" > .gitsecret/keys/S.gpg-agent
	echo "socket=/tmp/S.gpg-agent" >> .gitsecret/keys/S.gpg-agent
	echo "%Assuan%" > .gitsecret/keys/S.gpg-agent.ssh
	echo "socket=/tmp/S.gpg-agent.ssh" >> .gitsecret/keys/S.gpg-agent.ssh
	echo "extra-socket /tmp/S.gpg-agent.extra" > .gitsecret/keys/gpg-agent.conf
	echo "browser-socket /tmp/S.gpg-agent.browser" >> .gitsecret/keys/gpg-agent.conf

.PHONY: secret-encrypt
secret-encrypt: ## Decrypt secret files via `git-secret hide`
	"$(MAKE)" -s git-secret ARGS="hide"

.PHONY: secret-decrypt
secret-decrypt: ## Decrypt secret files via `git-secret reveal -f`
	"$(MAKE)" -s git-secret ARGS="reveal -f" 

.PHONY: secret-decrypt-with-password
secret-decrypt-with-password: ## Decrypt secret files using a password for gpg via `git-secret reveal -f -p $(GPG_PASSWORD)`
	@$(if $(GPG_PASSWORD),,$(error GPG_PASSWORD is undefined))
	"$(MAKE)" -s git-secret ARGS="reveal -f -p $(GPG_PASSWORD)" 

.PHONY: secret-add
secret-add: ## Add a file to git secret via `git-secret add $FILE`
	@$(if $(FILE),,$(error FILE is undefined))
	"$(MAKE)" -s git-secret ARGS="add $(FILE)"

.PHONY: secret-cat
secret-cat: ## Show the contents of file to git secret via `git-secret cat $FILE`
	@$(if $(FILE),,$(error FILE is undefined))
	"$(MAKE)" -s git-secret ARGS="cat $(FILE)"

.PHONY: secret-list
secret-list: ## List all files added to git secret `git-secret list`
	"$(MAKE)" -s git-secret ARGS="list"

.PHONY: secret-remove
secret-remove: ## Remove a file from git secret via `git-secret remove $FILE`
	@$(if $(FILE),,$(error FILE is undefined))
	"$(MAKE)" -s git-secret ARGS="remove $(FILE)"

.PHONY: secret-add-user
secret-add-user: ## Remove a user from git secret via `git-secret tell $EMAIL`
	@$(if $(EMAIL),,$(error EMAIL is undefined))
	"$(MAKE)" -s git-secret ARGS="tell $(EMAIL)"

.PHONY: secret-show-users
secret-show-users: ## Show all users that have access to git secret via `git-secret whoknows`
	"$(MAKE)" -s git-secret ARGS="whoknows"

.PHONY: secret-remove-user
secret-remove-user: ## Remove a user from git secret via `git-secret killperson $EMAIL`
	@$(if $(EMAIL),,$(error EMAIL is undefined))
	"$(MAKE)" -s git-secret ARGS="killperson $(EMAIL)"

.PHONY: secret-diff
secret-diff: ## Show the diff between the content of encrypted and decrypted files via `git-secret changes`
	"$(MAKE)" -s git-secret ARGS="changes"
```

<!-- generated -->
<a id='workflow'> </a>
<!-- /generated -->

## Workflow

Working with `git-secret` is pretty straight forward:

- initialize `git-secret`
- add all users
- add all secret files and make sure they are ignored via `.gitignore`
- encrypt the files
- commit the encrypted files like "any other file"
- if any changes were made by other team members to the files:
  - => decrypt to get the most up-to-date ones
- if any modifications are required from your side:
  - => make the changes to the decrypted files and then re-encrypt them again

But: The devil is in the details. The [Process challenges](#process-challenges) section explains some of the pitfalls that we have encountered and the [Scenarios](#scenarios) section gives some concrete examples for common scenarios.

<!-- generated -->
<a id='process-challenges'> </a>
<!-- /generated -->

### Process challenges

From a process perspective we've encountered some challenges that I'd like to mention - including how we deal with them.

<!-- generated -->
<a id='updating-secrets'> </a>
<!-- /generated -->

#### Updating secrets

When updating secrets you must ensure to always **decrypt the files first** in order to avoid using "stale" files that you might still have locally. I usually check out the latest `main` branch and run `git secret reveal` to have the most up-to-date versions of the secret files. You  could also use a [`post-merge` git hook](https://stackoverflow.com/a/4185449/413531) to do  this automatically, but I personally don't want to risk overwriting my local secret files by  accident.

<!-- generated -->
<a id='code-reviews-and-merge-conflicts'> </a>
<!-- /generated -->

#### Code reviews and merge conflicts

Since the **encrypted files cannot be diffed meaningfully**, the code reviews become more difficult when secrets are involved. We use Gitlab for reviews and I usually first check the diff of the `.gitsecret/paths/mapping.cfg` file to see "which files have changed" directly in the UI.

In addition, I will

- checkout the `main` branch
- decrypt the secrets via `git secret reveal -f`
- checkout the `feature-branch`
- run `git secret changes` to see the differences between the decrypted files from `main` and the
  encrypted files from `feature-branch`

Things get even more complicated when multiple team members need to modify secret files at the same time on different branches, as **the encrypted files cannot be compared - i.e. git cannot be smart  about delta updates**. The only way around this is coordinating the pull requests, i.e. merge the first, update the secrets of the second and then merge the second.

Fortunately, this has only happened very rarely so far.

<!-- generated -->
<a id='local-git-secret-and-gpg-setup'> </a>
<!-- /generated -->

#### Local `git-secret` and `gpg` setup

Currently, all developers in our team have `git-secret` installed locally (instead of using it through docker) and use their own `gpg` keys.

This means more onboarding overhead, because

- a new dev must
  - install `git-secret` locally (*)
  - install and setup `gpg` locally (*)
  - create a `gpg` key pair
- the public key must be added by every other team member (*)
- the user of the key must be added via `git secret tell`
- the secrets must be re-encrypted

And for offboarding

- the public key must be removed by every other team member (*)
- the user of the key must be removed via `git secret killperson`
- the secrets must be re-encrypted

Plus, we need to ensure that the `git-secret` and `gpg` versions are kept up-to-date for everyone to not run into any compatibility issues.

As an alternative, I'm currently leaning more towards **handling everything through docker** (as presented in this tutorial). All steps marked with (*) are then obsolete, i.e. there is no need  to setup `git-secret` and `gpg` locally. 

But the approach also comes with some downsides, because
- the **secret key and all public keys have to be imported every time the container is started**
- **each dev needs to put his private `gpg` key "in the codebase"** (ignored by `.gitignore`) so it 
  can be shared with docker and imported by `gpg` (in docker). The alternative would be using 
  a single secret key that is   shared within the team - which feels very wrong :P

To make this a little more convenient, **we put the public gpg keys of every dev in the  repository** under `.dev/gpg-keys/` and **the private key has to be named `secret.gpg` and put  in the root of the codebase**. 

In this setup, `secret.gpg` must also be added to the`.gitignore` file. 

```gitignore
# File: .gitignore
#...
vendor/
secret.gpg
```

The import can now be simplified with `make` targets:

```makefile
# gpg

DEFAULT_SECRET_GPG_KEY?=secret.gpg
DEFAULT_PUBLIC_GPG_KEYS?=.dev/gpg-keys/*

.PHONY: gpg
gpg: ## Run gpg commands. Specify the command e.g. via ARGS="--list-keys"
	$(EXECUTE_IN_APPLICATION_CONTAINER) gpg $(ARGS)

.PHONY: gpg-import
gpg-import: ## Import a gpg key file e.g. via GPG_KEY_FILES="/path/to/file /path/to/file2"
	@$(if $(GPG_KEY_FILES),,$(error GPG_KEY_FILES is undefined))
	"$(MAKE)" -s gpg ARGS="--import --batch --yes --pinentry-mode loopback $(GPG_KEY_FILES)"

.PHONY: gpg-import-default-secret-key
gpg-import-default-secret-key: ## Import the default secret key
	"$(MAKE)" -s gpg-import GPG_KEY_FILES="$(DEFAULT_SECRET_GPG_KEY)"

.PHONY: gpg-import-default-public-keys
gpg-import-default-public-keys: ## Import the default public keys
	"$(MAKE)" -s gpg-import GPG_KEY_FILES="$(DEFAULT_PUBLIC_GPG_KEYS)" 

.PHONY: gpg-init
gpg-init: gpg-import-default-secret-key gpg-import-default-public-keys ## Initialize gpg in the container, i.e. import all public and private keys
```

"Everything" can now be handled via 

```bash
make gpg-init
```

that needs to be run one single time after a container has been started.

<!-- generated -->
<a id='scenarios'> </a>
<!-- /generated -->

### Scenarios

The scenarios assume the following preconditions:

- You have checked out branch [part-6-git-secret-encrypt-repository-docker](https://github.com/paslandau/docker-php-tutorial/tree/git-secret-encrypt-repository-docker)
  ```bash
  git checkout part-6-git-secret-encrypt-repository-docker
  ```
  and no running docker containers 
  ```bash
  make docker-down
  ```
- You have deleted the existing `git-secret` folder, the keys in `.dev/gpg-keys`, the 
  `secret.gpg` key and the `passwords.*` files 
  ```bash
  rm -rf .gitsecret/ .dev/gpg-keys/* secret.gpg passwords.*
  ```

<!-- generated -->
<a id='initial-setup-of-gpg-keys'> </a>
<!-- /generated -->

#### Initial setup of `gpg` keys

Unfortunately, I didn't find a way to create and export `gpg` keys through `make` and `docker`. You need to either run the commands interactively OR pass a string with newlines to it. Both things are horribly complicated with `make` and docker. Thus, you need to log into the `application` container and run the commands in there directly. Not great - but this needs to be done only once when a new developer is onboarded anyways.

FYI: I usually log into containers via [Easy container access via din .bashrc helper](https://www.pascallandau.com/blog/structuring-the-docker-setup-for-php-projects/#easy-container-access-via-din-bashrc-helper).

The secret key is exported to `secret.gpg` and the public key to `.dev/gpg-keys/alice-public.gpg`.

```bash
# start the docker setup
make docker-up

# log into the container ('winpty' is only required on Windows)
winpty docker exec -ti dofroscra_local-application-1 bash

# export key pair
name="Alice Doe"
email="alice@example.com"
gpg --batch --gen-key <<EOF
Key-Type: 1
Key-Length: 2048
Subkey-Type: 1
Subkey-Length: 2048
Name-Real: $name
Name-Email: $email
Expire-Date: 0
%no-protection
EOF

# export the private key
gpg --output secret.gpg --armor --export-secret-key $email

# export the public key
gpg --armor --export $email > .dev/gpg-keys/alice-public.gpg
```

```text
$ make docker-up
ENV=local TAG=latest DOCKER_REGISTRY=docker.io DOCKER_NAMESPACE=dofroscra APP_USER_NAME=application APP_GROUP_NAME=application docker compose -p dofroscra_local --env-file ./.docker/.env -f ./.docker/docker-compose/docker-compose.yml -f ./.docker/docker-compose/docker-compose.local.yml up -d
Container dofroscra_local-application-1  Created
...
Container dofroscra_local-application-1  Started
$ docker ps
CONTAINER ID   IMAGE                                COMMAND                  CREATED          STATUS          PORTS                NAMES
...
95f740607586   dofroscra/application-local:latest   "/usr/sbin/sshd -D"      21 minutes ago   Up 21 minutes   0.0.0.0:2222->22/tcp dofroscra_local-application-1

$ winpty docker exec -ti dofroscra_local-application-1 bash
root:/var/www/app# name="Alice Doe"
root:/var/www/app# email="alice@example.com"
gpg --batch --gen-key <<EOF
Key-Type: 1
Key-Length: 2048
Subkey-Type: 1
Subkey-Length: 2048
Name-Real: $name
Name-Email: $email
Expire-Date: 0
%no-protection
EOF
root:/var/www/app# gpg --batch --gen-key <<EOF
> Key-Type: 1
> Key-Length: 2048
> Subkey-Type: 1
> Subkey-Length: 2048
> Name-Real: $name
> Name-Email: $email
> Expire-Date: 0
> %no-protection
> EOF
gpg: directory '/root/.gnupg' created
gpg: keybox '/root/.gnupg/pubring.kbx' created
gpg: /root/.gnupg/trustdb.gpg: trustdb created
gpg: key BBBE654440E720C1 marked as ultimately trusted
gpg: directory '/root/.gnupg/openpgp-revocs.d' created
gpg: revocation certificate stored as '/root/.gnupg/openpgp-revocs.d/225C736E0E70AC222C072B70BBBE654440E720C1.rev'

root:/var/www/app# gpg --output secret.gpg --armor --export-secret-key $email
root:/var/www/app# head secret.gpg
-----BEGIN PGP PRIVATE KEY BLOCK-----

lQOYBGJD+bwBCADBGKySV5PINc5MmQB3PNvCG7Oa1VMBO8XJdivIOSw7ykv55PRP
3g3R+ERd1Ss5gd5KAxLc1tt6PHGSPTypUJjCng2plwD8Jy5A/cC6o2x8yubOslLa
x1EC9fpcxUYUNXZavtEr+ylOaTaRz6qwSabsAgkg2NZ0ey/QKmFOZvhL8NlK9lTI
GgZPTiqPCsr7hiNg0WRbT5h8nTmfpl/DdTgwfPsDn5Hn0TEMa79WsrPnnq16jsq0
Uusuw3tOmdSdYnT8j7m1cpgcSj0hRF1eh4GVE0o62GqeLTWW9mfpcuv7n6mWaCB8
DCH6H238gwUriq/aboegcuBktlvSY21q/MIXABEBAAEAB/wK/M2buX+vavRgDRgR
hjUrsJTXO3VGLYcIetYXRhLmHLxBriKtcBa8OxLKKL5AFEuNourOBdcmTPiEwuxH
5s39IQOTrK6B1UmUqXvFLasXghorv8o8KGRL4ABM4Bgn6o+KBAVLVIwvVIhQ4rlf

root:/var/www/app# gpg --armor --export $email > .dev/gpg-keys/alice-public.gpg
root:/var/www/app# head .dev/gpg-keys/alice-public.gpg
-----BEGIN PGP PUBLIC KEY BLOCK-----

mQENBGJD+bwBCADBGKySV5PINc5MmQB3PNvCG7Oa1VMBO8XJdivIOSw7ykv55PRP
3g3R+ERd1Ss5gd5KAxLc1tt6PHGSPTypUJjCng2plwD8Jy5A/cC6o2x8yubOslLa
x1EC9fpcxUYUNXZavtEr+ylOaTaRz6qwSabsAgkg2NZ0ey/QKmFOZvhL8NlK9lTI
GgZPTiqPCsr7hiNg0WRbT5h8nTmfpl/DdTgwfPsDn5Hn0TEMa79WsrPnnq16jsq0
Uusuw3tOmdSdYnT8j7m1cpgcSj0hRF1eh4GVE0o62GqeLTWW9mfpcuv7n6mWaCB8
DCH6H238gwUriq/aboegcuBktlvSY21q/MIXABEBAAG0HUFsaWNlIERvZSA8YWxp
Y2VAZXhhbXBsZS5jb20+iQFOBBMBCgA4FiEEIlxzbg5wrCIsBytwu75lREDnIMEF
AmJD+bwCGy8FCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQu75lREDnIMEN4Af+
```

That's it. We now have a new secret and private key for `alice@example.com` and have exported it to  `secret.gpg` resp. `.dev/gpg-keys/alice-public.gpg` (and thus shared it with the host system).  The remaining commands can now be run outside of the `application` container directly on the  host system.

<!-- generated -->
<a id='initial-setup-of-git-secret'> </a>
<!-- /generated -->

#### Initial setup of `git-secret`

Let's say we want to introduce `git-secret` "from scratch" to a new codebase. Then you would run  the following commands:

**Initialize `git-secret`**

```bash
make secret-init
```

```text
$ make secret-init
"C:/Program Files/Git/mingw64/bin/make" -s git-secret ARGS="init";
git-secret: init created: '/var/www/app/.gitsecret/'
```

**Apply the `gpg` fix for shared directories**

See [The `git-secret` directory and the `gpg-agent` socket](#the-git-secret-directory-and-the-gpg-agent-socket).

```bash
$ make secret-init-gpg-socket-config
```

```text
$ make secret-init-gpg-socket-config
echo "%Assuan%" > .gitsecret/keys/S.gpg-agent
echo "socket=/tmp/S.gpg-agent" >> .gitsecret/keys/S.gpg-agent
echo "%Assuan%" > .gitsecret/keys/S.gpg-agent.ssh
echo "socket=/tmp/S.gpg-agent.ssh" >> .gitsecret/keys/S.gpg-agent.ssh
echo "extra-socket /tmp/S.gpg-agent.extra" > .gitsecret/keys/gpg-agent.conf
echo "browser-socket /tmp/S.gpg-agent.browser" >> .gitsecret/keys/gpg-agent.conf
```

<!-- generated -->
<a id='initialize-gpg-after-container-startup'> </a>
<!-- /generated -->

#### Initialize `gpg` after container startup

After restarting the containers, we need to initialize `gpg`, i.e. import all public keys from  `.dev/gpg-keys/*` and the private key from `secret.gpg`. Otherwise we will not be able to en-  and decrypt the files.

```bash
make gpg-init
```

```text
$ make gpg-init
"C:/Program Files/Git/mingw64/bin/make" -s gpg-import GPG_KEY_FILES="secret.gpg"
gpg: directory '/home/application/.gnupg' created
gpg: keybox '/home/application/.gnupg/pubring.kbx' created
gpg: /home/application/.gnupg/trustdb.gpg: trustdb created
gpg: key BBBE654440E720C1: public key "Alice Doe <alice@example.com>" imported
gpg: key BBBE654440E720C1: secret key imported
gpg: Total number processed: 1
gpg:               imported: 1
gpg:       secret keys read: 1
gpg:   secret keys imported: 1
"C:/Program Files/Git/mingw64/bin/make" -s gpg-import GPG_KEY_FILES=".dev/gpg-keys/*"
gpg: key BBBE654440E720C1: "Alice Doe <alice@example.com>" not changed
gpg: Total number processed: 1
gpg:              unchanged: 1
```

<!-- generated -->
<a id='adding-new-team-members'> </a>
<!-- /generated -->

#### Adding (new) team members

Let's start by adding our own user to `git-secret`

```bash
make secret-add-user EMAIL="alice@example.com"
```

```text
$ make secret-add-user EMAIL="alice@example.com"
"C:/Program Files/Git/mingw64/bin/make" -s git-secret ARGS="tell alice@example.com"
git-secret: done. alice@example.com added as user(s) who know the secret.
```

And verify that it worked via

```bash
make secret-show-users
```

```text
$ make secret-show-users
"C:/Program Files/Git/mingw64/bin/make" -s git-secret ARGS="whoknows"
alice@example.com
```

<!-- generated -->
<a id='adding-and-encrypting-files'> </a>
<!-- /generated -->

#### Adding and encrypting files

Let's add a new encrypted file `secret_password.txt`.

Create the file 

```bash
echo "my_new_secret_password" > secret_password.txt
```

Add it to `.gitignore`

```bash
echo "secret_password.txt" >> .gitignore
```

Add it to `git-secret`

```bash
make secret-add FILE="secret_password.txt"
```

```text
$ make secret-add FILE="secret_password.txt"
"C:/Program Files/Git/mingw64/bin/make" -s git-secret ARGS="add secret_password.txt"
git-secret: 1 item(s) added.
```

Encrypt all files

```bash
make secret-encrypt
```

```text
$ make secret-encrypt
"C:/Program Files/Git/mingw64/bin/make" -s git-secret ARGS="hide"
git-secret: done. 1 of 1 files are hidden.

$ ls secret_password.txt.secret
secret_password.txt.secret
```

<!-- generated -->
<a id='decrypt-files'> </a>
<!-- /generated -->

#### Decrypt files

Let's first remove the "plain" `secret_password.txt` file

```bash
rm secret_password.txt
```

```text
$ rm secret_password.txt

$ ls secret_password.txt
ls: cannot access 'secret_password.txt': No such file or directory
```

and then decrypt the encrypted one.

```bash
make secret-decrypt
```

```text
$ make secret-decrypt
"C:/Program Files/Git/mingw64/bin/make" -s git-secret ARGS="reveal -f"
git-secret: done. 1 of 1 files are revealed.

$ cat secret_password.txt
my_new_secret_password
```

**Caution:** If the secret `gpg` key is password protected (e.g. `123456`), run

```bash
make secret-decrypt-with-password GPG_PASSWORD=123456
```

You could also add the `GPG_PASSWORD` variable to the  [`.make/.env`](https://www.pascallandau.com/blog/docker-from-scratch-for-php-applications-in-2022/#shared-variables-make-env) file as a local default value so that you wouldn't have to specify the value every time and  could then simply run

```bash
make secret-decrypt-with-password
```

without passing `GPG_PASSWORD`

<!-- generated -->
<a id='removing-files'> </a>
<!-- /generated -->

#### Removing files

Remove the `secret_password.txt` file we added previously:


```bash
make secret-remove FILE="secret_password.txt"
```

```text
$ make secret-remove FILE="secret_password.txt"
"C:/Program Files/Git/mingw64/bin/make" -s git-secret ARGS="remove secret_password.txt"
git-secret: removed from index.
git-secret: ensure that files: [secret_password.txt] are now not ignored.
```

**Caution: this will neither remove the `secret_password.txt` file nor  the `secret_password.txt.secret` file automatically"**

```text
$ ls -l | grep secret_password.txt
-rw-r--r-- 1 Pascal 197121     19 Mar 31 14:03 secret_password.txt
-rw-r--r-- 1 Pascal 197121    358 Mar 31 14:02 secret_password.txt.secret
```

But even though the encrypted `secret_password.txt.secret` file still exists, it will not be  decrypted:

```text
$ make secret-decrypt
"C:/Program Files/Git/mingw64/bin/make" -s git-secret ARGS="reveal -f"
git-secret: done. 0 of 0 files are revealed.
```

<!-- generated -->
<a id='removing-team-members'> </a>
<!-- /generated -->

#### Removing team members

Removing a team member can be done via

```bash
make secret-remove-user EMAIL="alice@example.com"
```

```text
$ make secret-remove-user EMAIL="alice@example.com"
"C:/Program Files/Git/mingw64/bin/make" -s git-secret ARGS="killperson alice@example.com"
git-secret: removed keys.
git-secret: now [alice@example.com] do not have an access to the repository.
git-secret: make sure to hide the existing secrets again.
```

If there are any users left, we must make sure to re-encrypt the secrets via

```bash
make secret-encrypt
```

Otherwise (if no more users are left) `git-secret` would simply error out

```text
$ make secret-decrypt
"C:/Program Files/Git/mingw64/bin/make" -s git-secret ARGS="reveal -f"
git-secret: abort: no public keys for users found. run 'git secret tell email@address'.
make[1]: *** [.make/01-00-application-setup.mk:57: git-secret] Error 1
make: *** [.make/01-00-application-setup.mk:69: secret-decrypt] Error 2
```

**Caution**: Please keep in mind to  [rotate the secrets themselves as well](#reminder-rotate-the-encrypted-secrets)!

<!-- generated -->
<a id='pros-and-cons'> </a>
<!-- /generated -->

## Pros and cons

<!-- generated -->
<a id='pro'> </a>
<!-- /generated -->

### Pro

- very low barrier to entry:
  - no third party service required
  - easy to integrate in existing codebases, because the secrets are located directly in 
    the codebase
  - everything can be handled through docker (no additional local software necessary)
- once set up, it is very easy/convenient to use and can be integrated in a team workflow
- changes to secrets can be reviewed before they are merged
  - this leads to less fuck-ups on deployments
- "everything" is in the repository, which brings a lot of familiar benefits like
  - version control
  - a single `git pull` is the only thing you need to get everything (=> good dev experience)

<!-- generated -->
<a id='cons'> </a>
<!-- /generated -->

### Cons

- some overhead during onboarding and offboarding
- the secret key must be put in the root of the repository at `./secret.gpg`
- no fine grained permissions for different secrets, e.g. the mysql password on production and 
  staging can not be treated differently
  - if somebody can decrypt secrets, ALL of them are exposed
- if the a secret key ever gets leaked, all secrets are compromised
  - => can be mitigated (to a degree) by using a passphrase on the secret key
  - => this is kinda true for any other system that stores secrets as well BUT third parties 
    could probably implement additional measures like multi factor authentication
- secrets are versioned alongside the users that have access, i.e. even if a user is removed at 
  some point, he can still decrypt a previous version of the encrypted secrets
  - => must be mitigated by
    [rotating the secrets themselves as well](#reminder-rotate-the-encrypted-secrets)

<!-- generated -->
<a id='wrapping-up'> </a>
<!-- /generated -->

## Wrapping up

Congratulations, you made it! If some things are not completely clear by now, don't hesitate to leave a comment. You are now able to encrypt and decrypt secret files so that they can be stored  directly in the git repository.

In the next part of this tutorial, we will  [set up a CI pipeline for dockerized PHP Apps on Github and Gitlab](https://www.pascallandau.com/blog/ci-pipeline-docker-php-gitlab-github/) that decrypts all necessary secrets and then runs our tests and qa tools.

Please subscribe to the [RSS feed](https://www.pascallandau.com/feed.xml) or [via email](https://www.pascallandau.com/blog/#newsletter) to get automatic notifications when this next part comes out :)

;