Mustafa Can Yücel

Secrets Galore: The Not-So-Secret Trend of Hiding API Keys and .env Files in Repositories

In recent times, the prevalence of including API keys, .env files, and other secrets in projects can be attributed to several factors. Firstly, the rise of open-source development and the proliferation of public repositories on platforms like GitHub have led to inadvertent exposure of sensitive information. Developers often commit code without realizing that they have left their secrets in the repository, especially when working under tight deadlines or in fast-paced development environments. Secondly, the use of automated build systems and continuous integration tools can sometimes lead to the unintentional inclusion of secrets during the build process, further exacerbating the issue. Additionally, inexperienced developers or those not well-versed in security practices may not fully grasp the potential consequences of including secrets in repositories. They might prioritize convenience over security, opting for a quick and easy way to share code without realizing the associated risks. Despite these reasons, it is crucial to educate developers about security best practices and foster a culture of securely managing secrets to mitigate the potential harm caused by their exposure.

Including .env files, API keys, and other secrets in repositories is highly discouraged for several reasons. Firstly, it poses a significant security risk. Exposing sensitive information publicly in a repository makes it accessible to anyone who can access the repository, potentially leading to unauthorized access, data breaches, and malicious activities. Secondly, it violates best practices for secure software development. Storing secrets in repositories contradicts the principle of separation of concerns, as secrets should be managed separately from code. Additionally, it hampers collaboration, as sharing code becomes risky, and it complicates the process of rotating or revoking credentials. To maintain a robust security posture, it is crucial to store secrets securely, preferably using secure vaults or environment-specific configurations, and ensure that they are not inadvertently shared or exposed in repositories.

When it comes to sharing secrets between collaborators, it is crucial to prioritize security and implement best practices. One common approach is to use secure communication channels, such as encrypted messaging platforms or password-protected file-sharing services, to transmit secrets directly to trusted individuals. Another option is to leverage secure key management systems or vaults, such as HashiCorp Vault or AWS Secrets Manager, where secrets can be securely stored and shared with designated team members. Alternatively, a shared password manager tool, like LastPass or 1Password, can be utilized to securely store and share secrets within a team. It is important to avoid sharing secrets through insecure communication channels like email or chat platforms, as these can be vulnerable to interception or unauthorized access. By adopting secure sharing methods, teams can maintain the confidentiality and integrity of their secrets while facilitating efficient collaboration.

Vault by HashiCorp

HashiCorp Vault is an exceptional software solution that provides robust secrets management and secure data protection for organizations. Vault acts as a central repository, allowing users to store, manage, and access sensitive information, such as API keys, passwords, and certificates, securely. With its comprehensive feature set, Vault offers numerous benefits. It enables fine-grained access control, allowing administrators to define access policies and provide individualized permissions to team members. Vault also supports encryption and cryptographic operations, ensuring that secrets are stored and transmitted securely. The software provides integration capabilities with various cloud platforms and popular authentication systems, allowing for seamless integration into existing infrastructure. Moreover, Vault supports dynamic secrets, generating temporary credentials that can be used for short-lived access, reducing the risk of long-term exposure. HashiCorp Vault is a reliable and widely adopted solution that empowers organizations to elevate their secrets management practices and enhance the overall security of their systems.


First, we need to have `gpg` installed on our system. It is a very common tool and you probably already have it installed. If not, you can install it with the following command:

sudo apt update && sudo apt install gpg
Then we add the keyring to our system:
wget -O- | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
gpg --no-default-keyring --keyring /usr/share/keyrings/hashicorp-archive-keyring.gpg --fingerprint
The fingerprint must match 798A EC65 4E5C 1542 8C8E 42EE AA16 FCBC A621 E701, which can also be verified at official site, under "Linux Package Checksum Verification":
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list

Once the prerequisites are installed, we can update the package list and install the Vault:

sudo apt update && sudo apt install vault
You can check the installation with the command vault.

While the app is being installed, we add an A DNS record to the domain/subdomain that we want to use for Vault. In this example, we will use

Vault Configuration

Vault comes with a dev server that is used for development and testing purposes. It is not recommended to use it in production. I highly recommend going over the basics of Vault before using it in production. You can find the official getting-started guide here. We will go with an actual production setup in this example.

The configuration file for the vault can be located anywhere you prefer. Vault also requires a data directory to store its data. We will create a directory named data in the same directory as the configuration file config.hcl. I will put these files in the vault directory under the user's home directory, but you can put them anywhere you want. The configuration file will look like this:

storage "raft" {
path    = "./vault/data"
node_id = "node1"

listener "tcp" {
address     = ""
tls_disable = "true"

api_addr = ""
cluster_addr = ""
ui = true
The details and possibilities for the configuration file are beyond the scope of this article, but you can find the official documentation here. We will just go over the configuration above:
  • storage: This stanza configures the storage backend for Vault. We will use raft for this example. You can find the other storage backends here. Raft is the integrated storage backend of Vault, and the Integrated Storage backend is used to persist Vault's data. Unlike other storage backends, Integrated Storage does not operate from a single source of data. Instead, all the nodes in a Vault cluster will have a replicated copy of Vault's data. Data gets replicated across all the nodes via the Raft Consensus Algorithm.
    • path: This is the path to the data directory. It is relative to the configuration file.
    • node_id: This is the unique identifier for the node. It is used to join a cluster.
  • listener: This stanza configures the listener for Vault. We will use tcp for this example. You can find the other listeners here.
    • address: This is the TCP address that Vault listens to. We will create a reverse proxy for outside access, so we listen to the default port in localhost.
    • tls_disable: Since we will use a reverse proxy, we disable TLS within localhost.
  • api_addr: Specifies the address (full URL) to advertise to other Vault servers in the cluster for client redirection. This value is also used for plugin backends.
  • cluster_addr: Specifies the address to advertise to other Vault servers in the cluster for request forwarding.
  • ui: Enables the built-in web UI, which is available to all listeners (address + port) at the /ui path. Browsers accessing the standard Vault API address will automatically redirect there.

Once the configuration file is ready, we can start the Vault server with the following command, executed in the same directory as the configuration file:

vault server -config=config.hcl
This will give you a total of five unseal keys and a root key. You should save these in a safe place. You will need them to unseal the vault. You can also use the root key to log in to the vault. You can find more information about the unseal process here.

Unsealing the Vault

When a vault starts, it starts in sealed mode and should be unsealed with at least 3 unseal keys. Run the following command 3 times, entering a different unseal key every time:

vault operator unseal

Using Web UI

To access the web UI, we need to create a reverse proxy. We should already have a running Caddy server by now, so we will just add a new site to it. The associated part of the Caddyfile will look like this: {
    reverse_proxy http://localhost:8200 {
            header_up Host {upstream_hostport}
It is usually much easier to work with the web UI, so we will use it for the rest of the article. You can access the web UI at


The default root login has too broad permissions for daily use, so we create a new login with username and password via ssh (don't forget to change the username and password):

vault auth enable userpass
vault write auth/<userpass:path>/users/myawesomeusername \ 
password=myawesomepassword \
Now we can use the above credentials to log in.

Storing Secrets

Once the configuration is completed, you can store your secrets from the web UI or the cli. Vault comes with a default PKI store that can store key-value pairs or JSON format. You can also use other backends for your secrets. You can find the list of backends here.


Vault has a very good collaboration system. You can create teams and assign policies to them. You can also create tokens for each team member and assign them to the team. This way, you can control who can access which secrets. You can find more information about the collaboration system here.