Skip to main content

Integrating Secrets

Install Doctor implements many optional features that rely on user-specific variables. Most of these user-specific variables are API keys. To augment that capabilities of your device, Install Doctor can leverage API keys to many free and popular cloud services. These API keys, as well as other user-specific variables, can be utilized by defining them as environment variables prior to running the start script. It is also possible to encrypt and bundle some of these secrets into your own fork of the Install Doctor repository.

Environment Variables

The easiest way to get started with automating the Install Doctor provisioning process and leveraging optional features is to define user-specific variables as environment variables.

For instance, if you want to automate the provisioning process the first time you run the Install Doctor quick start script (without deep diving into all the variables yet), you can run the following (depending on your operating system):

Linux / macOS:

export HEADLESS_INSTALL=true
export SOFTWARE_GROUP=Standard-Desktop
export FULL_NAME="Joe Shmoe"
export PRIMARY_EMAIL="[email protected]"
export PUBLIC_SERVICES_DOMAIN="megabyte.space"
export START_REPO=my-gh-user/my-fork-name
bash <(curl -sSL https://install.doctor/start)

Windows:

$env:HEADLESS_INSTALL = true
$env:SOFTWARE_GROUP = Standard-Desktop
$env:FULL_NAME = 'Joe Shmoe'
$env:PRIMARY_EMAIL = '[email protected]'
$env:PUBLIC_SERVICES_DOMAIN = 'megabyte.space'
$env:START_REPO = 'my-gh-user/my-fork-name'
iex ((New-Object System.Net.WebClient).DownloadString('https://install.doctor/windows'))

Qubes:

export HEADLESS_INSTALL=true
export SOFTWARE_GROUP=Standard-Desktop
export FULL_NAME="Joe Shmoe"
export PRIMARY_EMAIL="[email protected]"
export PUBLIC_SERVICES_DOMAIN="megabyte.space"
export START_REPO=my-gh-user/my-fork-name
qvm-run --pass-io sys-firewall "curl -sSL https://install.doctor/qubes" > ~/setup.sh
bash ~/setup.sh

Running the blocks of code listed above will bypass the Install Doctor TUI prompt system, allowing a completely headless setup process for your device.

There are many other variables you can make use of which are detailed in one of the following sections below.

Encrypted Secrets

For added security and convienience, Install Doctor supports embedding your secret variables into your own fork of Install Doctor by encrypting them with the Chezmoi default encryption provider called Age. By using this method, you will no longer have to manage a long set of environment variables and you will be able to store your encrypted secrets in a public git that you can use to provision any of your devices.

The first thing you should do when leveraging your own Age encryption key in your own fork of Install Doctor is to delete all the keys stored in the home/.chezmoitemplates/secrets/ folder. If you do not do this, then Install Doctor will try unencrypting the test secrets using your key.

Creating an Age Key (Password Protected)

Install Doctor's default recommended encryption method involves:

  1. Creating a password protected Age encryption key
  2. Using the encryption key to encrypt your variables which are stored in home/.chezmoitemplates/secrets/

To create an encryption key, first ensure Age is installed and available in your PATH (the quick start script will automatically install Homebrew / Age). With age available from your terminal, run the following:

age-keygen | age -p > key.age

The output should look something like the following if you decided to forego creating a custom password:

Public key: age1wzpkjhk6jtgq2d3q97jdq7ptwpra7kdplnt38p5ruetcwfzahvps40l6a5
age: using autogenerated passphrase "act-client-permit-coil-clinic-cushion-wheat-beauty-ski-alarm"

Make note of the public key and your password. The public key should be added to the home/.chezmoi.yaml.tmpl file under the age.recipient key (just search for, "recipient" in the file). The key.age file that was generated should be added to home/key.txt.age. The quick start script that Install Doctor hosts expects the key to be stored in home/key.txt.age.

With all of this done, you can commit your changes. Your key.txt.age file is useless to anyone who does not know the password to decrypt it. The Install Doctor start script (that you can find on the homepage) will prompt you to decrypt the key.txt.age file using your selected password and store it in the location specified in the home/.chezmoi.yaml.tmpl file. For automation purposes, if you are using the default home/.chezmoi.yaml.tmpl file settings, the unencrypted file is temporarily stored on the disk in the ~/.config/age/chezmoi.txt location. This makes it easier to re-run the provisioning process and add new secrets to your custom fork. After you are done using the encryption key, you should delete the unencrypted Age key file stored at ~/.config/age/chezmoi.txt.

Adding Secrets Using the Age Key

To add secrets to your custom fork, you need to ensure Chezmoi is configured to use the file stored in ~/.config/age/chezmoi.txt that is decrypted using a password from the file stored in home/key.txt.age of your custom fork. The easiest way to do this for the first time is to ensure the START_REPO environment variable is pointing to your custom fork and run the quick start script:

Linux / macOS:

export START_REPO=my-gh-user/my-fork-name
bash <(curl -sSL https://install.doctor/start)

Windows:

$env:START_REPO = 'my-gh-user/my-fork-name'
iex ((New-Object System.Net.WebClient).DownloadString('https://install.doctor/windows'))

Qubes:

export START_REPO=my-gh-user/my-fork-name
qvm-run --pass-io sys-firewall "curl -sSL https://install.doctor/qubes" > ~/setup.sh
bash ~/setup.sh

Early in the provisioning process, the script will prompt you for your Age encryption passphrase. After this happens, you can CTRL+C out of the script and begin adding your encrypted secrets to your git repository fork.

With Chezmoi and Age configured, you can now begin encrypting all the pieces of data that you do not want to have to pass in as environment variables. You can also encrypt other things like SSH keys, GPG keys, etc.

Encrypting an API Key

To encrypt an API key, you would run:

echo "apikey-xXxxXxX" | chezmoi encrypt

The resulting text that outputs to the terminal would look something like this:

-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBCamNtb0pvOFRmYXRyNEhY
R1BpS09IYVBSSG83Rmh2MVJpdExKdjZrY2hVCmRmTTdjUDJyU2ZZK0NObmUwZ0dH
eGtFQUV6NXVPdjR1WFljNUkyczZuVjAKLS0tIDVTZDFPLytQVTJMNStUK21KYVF4
QVBhb0hIMG9tRjVuYWlHejN4cDJrQkkKTx7EQBmWL+0mXeaOOAtqo9rbJjWYx7mj
Jr4Trf/qfrmiDXX/
-----END AGE ENCRYPTED FILE-----

Copy the text including the lines that contain BEGIN AGE ENCRYPTED FILE and END AGE ENCRYPTED FILE and store them into the appropriate file nested inside home/.chezmoitemplates/secrets/. You can determine the supported key file names by referring to the Supported Variables / Secrets Table below.

Encrypting a File

To encrypt an entire file (named myfile.ext in the following example), you can run:

chezmoi encrypt myfile.ext

The resulting encrypted text can then be copied / pasted into the appropriate key file location. Supported targets are listed below in the Supported Variables / Secrets Table section.

Using GPG

If you prefer to use GPG instead of Age as your encryption mechanism, you can define this option in the home/.chezmoi.yaml.tmpl file. For more details on this implementation, see the GPG page in the Chezmoi docs.

Users may want to do this so they can protect their secrets with hardware-backed encryption via devices like a YubiKey. For details on how to setup GPG with a YubiKey, refer to this guide.

Supported Variables / Secrets Table

The following table details all the custom variables supported by Install Doctor. The variable column denotes the name of the environment variable that can be specified to enable the automated deployment and the description details where the variable is used along with relevant details.

Unless otherwise specified in the description column, all of the variables in this chart can be stored as secrets by saving the encrypted secret environment variable in the home/.chezmoitemplates/secrets/ folder (with a file name equal to the environment variable name). So, if you wanted to saved your GITHUB_TOKEN as an encrypted secret in your fork then you would encrypt following the instructions detailed above and then saved the encrypted secret in the home/.chezmoitemplates/secrets/GITHUB_TOKEN location (e.g. echo "MY_GITHUB_API_TOKEN" | chezmoi encrypt > "home/.chezmoitemplates/secrets/GITHUB_TOKEN").

AGE_PASSWORDAutomate the Chezmoi Age decryption process by passing in the passphrase for the key.txt.age file stored in the home/ folder (which is used to encrypt all your secrets). This variable can only be passed in as an environment variable.
ANSIBLE_GALAXY_TOKENAnsible Galaxy API token stored in ~/.config/shell/private.sh so developer API keys can be loaded by running source "~/.config/shell/private.sh".
ANSIBLE_VAULT_PASSWORDAnsible Vault password stored in ~/.config/shell/private.sh so developer API keys can be loaded by running source "~/.config/shell/private.sh".
APPLE_USERNAMEUsername of your Apple developer account which is assigned to the XCODES_USERNAME variable for automated use of the Xcodes CLI and app
APPLE_PASSWORDPassword of your Apple developer account which is assigned to the XCODES_PASSWORD variable for automated use of the Xcodes CLI and app
ATUIN_EMAILE-mail address used for registering with Atuin
ATUIN_PASSWORDPassword used for registering with Atuin
ATUIN_USERNAMEUsername used for registering with Atuin
AWS_ACCESS_KEY_IDAWS access key ID (used for storing / retrieving from AWS Secret Manager which is used for headless Xcode installations / developer account authentications)
AWS_DEFAULT_REGIONDefault AWS region to use when region is not passed in via commands (e.g. us-east-1)
AWS_SECRET_ACCESS_KEYAWS access key secret (used in conjunction with the AWS_ACCESS_KEY_ID)
CELL_PHONE_NUMBERCell phone number you would like to receive Twilio notifications from via Apprise. The number should follow the format of 13332224444 where the 10-digit phone number is prefixed by the country code.
CLOUDFLARE_API_KEYCloudFlare administration API key. Used by CloudFlare CLI.
CLOUDFLARE_ACCOUNT_IDThe CloudFlare account ID
CLOUDFLARE_ORIGIN_CA_KEYThe CloudFlare origin CA key (currently unused)
CLOUDFLARE_R2_ID and CLOUDFLARE_R2_ID_USERThe CloudFlare R2 read / edit access key ID used for mounting S3-compliant buckets to various locations such as /mnt/s3-public, /mnt/s3-private, and ~/.local/mnt/s3 via rclone. The configuration files are located in ~/.config/rclone/ when CLOUDFLARE_R2_SECRET and .user.cloudflare.r2 in .chezmoi.yaml are defined. The key that ends in _USER is used for the ~/.local/mnt mount.
CLOUDFLARE_R2_SECRET and CLOUDFLARE_R2_SECRET_USERThe CloudFlare R2 read / edit secret access key used for mounting S3-compliant buckets to various locations such as /mnt/s3-public, /mnt/s3-private, and ~/.local/mnt/s3 via rclone. The configuration files are located in ~/.config/rclone/ when CLOUDFLARE_R2_ID and .user.cloudflare.r2 in .chezmoi.yaml are defined. The key that ends in _USER is used for the ~/.local/mnt mount.
CLOUDFLARE_TEAMS_CLIENT_IDCloudFlare Teams client ID used to automatically connect to CloudFlare Teams network via warp-cli when CLOUDFLARE_TEAMS_CLIENT_SECRET is also available. You can acquire the secret from a CloudFlare Teams service token. See this article.
CLOUDFLARE_TEAMS_CLIENT_SECRETCloudFlare Teams client secret used to automatically connect to CloudFlare Teams network via warp-cli when CLOUDFLARE_TEAMS_CLIENT_ID is also available. You can acquire the ID from a CloudFlare Teams service token. See this article.
CLOUDFLARE_TEAMS_ORGThe organization name used to connect to CloudFlare WARP Teams Zero Trust automatically. Used to populate the organization value referenced on this page. Can only be passed in as environment variable or hardcoded into home/.chezmoi.yaml.tmpl
CLOUDSDK_CORE_PROJECTInjected into ~/.config/shell/private.sh and saved in your Chezmoi configuration. Can only be passed in as environment variable or hardcoded into home/.chezmoi.yaml.tmpl.
DIGITALOCEAN_ACCESS_TOKENDigitalOcean personal access token (generated via the Applications & API > Personal access tokens section). Used to connect to DigitalOcean-hosted Kubernetes cluster.
DOCKERHUB_USERDockerHub username which is used in combination with the DOCKERHUB_TOKEN to headlessly authenticate Docker Desktop with DockerHub
DOCKERHUB_TOKENDockerHub API token stored in ~/.config/shell/private.sh so developer API keys can be loaded by running source "~/.config/shell/private.sh".
ELEVENLABS_API_KEYAPI key for ElevenLabs which is used by ShortGPT
FIG_TOKENFig user API token used to headlessly login / authenticate with Fig. Token can be acquired on the token dashboard after logging in.
FULL_NAMEYour full name used in things like the Git config and NPM config. This can be passed in as an environment variable or hardcoded into the home/.chezmoi.yaml.tmpl file.
GCE_SERVICE_ACCOUNT_EMAILInjected into ~/.config/shell/private.sh and saved in your Chezmoi configuration. Can only be passed in as environment variable or hardcoded into home/.chezmoi.yaml.tmpl.
GITHUB_GIST_TOKENGitHub token with gist permissions. Populates ~/.local/bin/gist so that the key does not have to be stored in the ~ directory.
GITHUB_READ_TOKENGitHub token with repository read permissions. Populates ~/.config/ghorg/conf.yaml so Ghorg can backup all of your GitHub repositories locally.
GITHUB_TOKENGitHub personal access token stored in ~/.config/shell/private.sh so developer API keys can be loaded by running source "~/.config/shell/private.sh".
GITLAB_READ_TOKENGitLab token with repository read permissions. Populates ~/.config/ghorg/conf.yaml so Ghorg can backup all of your GitLab repositories locally.
GITLAB_TOKENGitLab personal access token stored in ~/.config/shell/private.sh so developer API keys can be loaded by running source "~/.config/shell/private.sh".
GITLAB_RUNNER_TOKEN_DARWINGitLab runner token to use on macOS machines.
GITLAB_RUNNER_TOKEN_LINUXGitLab runner token to use on Linux machines.
GITLAB_RUNNER_TOKEN_WINDOWSGitLab runner token to use on Windows machines.
GMAIL_PASSWORDApp password for GMail which is used as an outgoing SMTP service (for git e-mail).
GOOGLE_SEARCH_API_KEYAPI key used by search-gpt available here.
GOOGLE_SEARCH_IDSearch engine ID for custom Google search engine utilized by search-gpt available here.
HEADLESS_INSTALLSet to true if you would like all prompts to be bypassed. This variable can only be passed in as an environment variable.
HEALTHCHECKS_*Various Healthchecks secret values used to configure https://health.$PUBLIC_DOMAIN automatically.
HEALTHCHECKS_API_KEYHealthchecks global API key used to integrate Healthchecks into the autorestic / restic backup systems.
HEROKU_API_KEYHeroku API token stored in ~/.config/shell/private.sh so developer API keys can be loaded by running source "~/.config/shell/private.sh".
HISHTORY_USER_SECRETUser secret for synchronizing entries cataloged by Hishtory.
HOARD_API_TOKENThe API token for [Hoard]https://github.com/Hyde46/hoard).
HOSTThe hostname of the device. Used for a FQDN that resolves to the device when combined with the _ PUBLIC_SERVICES_DOMAIN.
IFTTT_WEBHOOK_IDWebhook ID from the Maker webhook on IFTTT. This is used to integrate Apprise with IFTTT integration.
JUMPCLOUD_CONNECT_KEYJumpCloud device enrollment key.
KEYIDYour GPG key ID available on the MIT or Ubuntu servers. During provisioning, the key will be imported and trusted with an elevated trust level. This can only be passed in as an environment variable or be hardcoded into the home/.chezmoi.yaml.tmpl file.
MATRIX_PASSWORDPassword for user account for matrix.org through Element. Used to integrate Matrix chatting with Apprise.
MATRIX_USERNAMEUsername for user account for matrix.org through Element. Used to integrate Matrix chatting with Apprise.
NETDATA_ROOMNetdata Cloud room ID to assign the device to. Requires NETDATA_TOKEN to also be defined.
NETDATA_TOKENNetdata Cloud API token to use when enrolling. Requires NETDATA_ROOM to also be defined.
NGINX_AMPLIFY_API_KEYIf NGINX is installed on the system, then this key will be used to join the NGINX Amplify cloud service.
NGROK_AUTH_TOKENPopulates Ngrok token in ~/.config/ngrok/ngrok.yml which allows you to use logged-in-only Ngrok services
NOTIFICO_WEBHOOK_URLFull webhook URL generated on Notifico.
NO_RESTARTPrevents computer from automatically restarting. This may interfere with system updates.
NTFY_SH_ACCESS_TOKENAccess token retrieved from ntfy.sh.
NPM_TOKENNPM API token stored in ~/.config/shell/private.sh so developer API keys can be loaded by running source "~/.config/shell/private.sh".
OPENAI_API_KEYOpenAI API key available in the OpenAPI account settings manager. Used for OpenCommit (CLI that automatically generates commit messages).
OVPN_PASSWORDThe OpenVPN password to apply to VPN profiles located at ~/.config/vpn/*.ovpn (e.g. encrypted ProtonVPN OpenVPN profiles). OVPN_USERNAME must also be defined.
OVPN_USERNAMEThe OpenVPN username to apply to VPN profiles located at ~/.config/vpn/*.ovpn (e.g. encrypted ProtonVPN OpenVPN profiles). OVPN_PASSWORD must also be defined.
OPENWEATHERMAP_API_KEYFree API key retrieved from OpenWeatherMap which powers the wego terminal weather application
PORTAINER_BUSINESS_LICENSEPortainer business license key (free for up to 5 nodes)
PRIMARY_EMAILYour primary e-mail address used in things like the Git config and NPM config. You must pass this as an environment variable or hardcode it into the home/.chezmoi.yaml.tmpl file.
PROWL_API_KEYProwl API key used by ntfy that you can acquire from the Prowl API page.
PROWL_PROVIDER_KEYProwl provider API key used by ntfy. See PROWL_API_KEY.
PUSHBULLET_ACCESS_TOKENAccess token acquired from the Pushbullet account settings page for integration with nfty.
PUBLIC_SERVICES_DOMAINThe CloudFlare domain that you would like to use for CloudFlare DNS integration features. Can only be passed in as an environment variable.
PYPI_TOKENPyPi.org API token stored in ~/.config/shell/private.sh
RAYCAST_ACCESS_TOKENRaycast access token which is retrieved and stored in ~/.config/raycast/config.json
RAYCAST_TOKENRaycast token which is retrieved and stored in ~/.config/raycast/config.json
REDDIT_APP_IDApplication ID for Reddit app created according to the Apprise integration documentation.
REDDIT_APP_SECRETApplication secret for Reddit app created according to the Apprise integration documentation.
REDDIT_PASSWORDPassword for the Reddit developer account linked to the Reddit app created according to the Apprise integration documentation.
REDDIT_USERNAMEUsername for the Reddit developer account linked to the Reddit app created according to the Apprise integration documentation.
REPLICATE_API_KEYReplicate API key (used for AgentGPT)
RESTIC_PASSWORDPassword used to encrypt user-level Restic backups (leveraged by autorestic). This includes $HOME directory backups and application data backups.
RESTIC_SYSTEM_PASSWORDPassword used to encrypt system-level Restic backups (leveraged by autorestic). This includes Docker volume backups and any other types of backups that might require sudo privileges.
RESTRICTED_ENVIRONMENTSet to true if you are setting up a device that should not use sudo / administrator privileges. This can only be specified as an environment variable. This feature is a WIP. Pull requests welcome.
SAMBA_NETBIOS_NAMENetBIOS name to use with Samba. Default value is the same value as the HOSTNAME variable. Can only be specified as an environment variable.
SAMBA_WORKGROUPSamba workgroup name. Default value is BETELGEUSE. Can only be specified as an environment variable.
SENDGRID_API_KEYSendGrid API token with the "Mail" permission. Used for sending mail with Postfix.
SERP_API_KEYAPI key for Serper.dev (used by AgentGPT)
SFTPGO_DEFAULT_ADMIN_PASSWORDPassword for default admin user that can login to the SFTPGo web interface.
SFTPGO_DEFAULT_ADMIN_USERNAMEUsername for default admin user that can login to the SFTPGo web interface.
SLACK_API_TOKENSlack API personal access token that populates ~/.config/slack-term/config so that you can use Slack from your terminal with slack-term.
SLACK_BOT_USER_OAUTH_TOKENSlack bot OAuth token. Used by Apprise Slack integration so that user can post to Slack (with support for attachments) from the CLI.
SNAPCRAFT_EMAILSnapcraft.io e-mail address associated with the SNAPCRAFT_MACAROON and SNAPCRAFT_UNBOUND_DISCHARGE (detailed below). Stored in ~/.config/shell/private.sh so developer API credentials can be loaded by running source "~/.config/shell/private.sh". *Can only be included as an environment variable or hardcoded into home/.chezmoi.yaml.tmpl.
SNAPCRAFT_MACAROONSnapcraft.io authentication variable stored in ~/.config/shell/private.sh so developer API keys can be loaded by running source "~/.config/shell/private.sh".
SNAPCRAFT_UNBOUND_DISCHARGESnapcraft.io authentication variable stored in ~/.config/shell/private.sh so developer API keys can be loaded by running source "~/.config/shell/private.sh".
SOFTWARE_GROUPShould be equal to only of the software lists defined in home/.chezmoidata.yaml (see the Customization page for more details).
START_REPOSet to your GitHub username (if your fork is named install.doctor) to be able to provision your device with the one-liner outlined on the homepage. You can also specify any GitHub project by setting this equal to GITHUB_USERNAME/PROJECT_NAME or a full git address (e.g. [email protected]:megabyte-labs/install.doctor). This can only be passed in as an environment variable.
SUDO_PASSWORDProvide the sudo / administrator password to completely automate sudo password input. This is required on for automated deployments where the user running the script has a sudo password.
SURGE_LOGINThe e-mail address associated with your surge.sh account.
SURGE_TOKENThe surge.sh authentication token acquired by logging in and running surge token.
TIMEZONEYour timezone in the America/New_York format. It should be available in the TZ database. If not passed in as an environment variable, then the device's current timezone will be assumed to be correct.
TWILIO_ACCOUNT_SIDThe account SID for Twilio which can be acquired on the bottom of the homepage of the Twilio console. Used for Apprise integration of Twilio.
TWILIO_AUTH_TOKENThe Twilio auth token found on the bottom of the homepage of the Twilio console. Used for Apprise integration of Twilio.
TWILIO_FROM_NUMBERThe number to send out Twilio messages from. Should be registered in your Twilio account and be in the form 13332224444 with the country code included (e.g. 1 for the US in the sample phone number).
TWITTER_ACCESS_TOKEN, TWITTER_ACCESS_TOKEN_SECRET, TWITTER_API_KEY, TWITTER_API_KEY_SECRETVarious secrets you can generate using the Twitter Developer portal. Integrated into Apprise to allow tweeting from the command-line. Apprise provides details.
VAGRANT_CLOUD_TOKENVagrantUp API token stored in ~/.config/shell/private.sh so developer API keys can be loaded by running source "~/.config/shell/private.sh".
VMWARE_WORKSTATION_LICENSE_KEYLicense key used for pre-registering VMWare Pro or VMWare Fusion
VNC_PASSWORDVNC password used by macOS VNC system utility or TigerVNC on Linux. Default if not passed in is vncpass.
VNC_READ_PASSWORDVNC password used for a read-only session. Default if not passed in is readonly.
WAKATIME_API_KEYAPI key for the Wakatime VSCode time tracking plugin which gets injected into ~/.config/wakatime/.wakatime.cfg
WAZUH_MANAGERAddress of the Wazuh server to configure the Wazuh client to send data to. Defaults to wazuh.{{ PUBLIC_SERVICES_DOMAIN }}
WINDOWS_11_PRO_KEYActivation key for Windows 11 Professional
WORK_ENVIRONMENTSet to true if you are setting up a corporate device. When true, the provisioning process will avoid installing / configuring things like Tor, which are likely to be banned by corporate networks. This can only be specified as an environment variable. This feature is a WIP. Pull requests welcome.

*Note: When creating a pull request that introduces new variables, please also open a pull request updating this table to reflect those changes. Include a short description of where and how the variable / secret is used in the description column.

SSH Keys

SSH keys can be saved into your fork by encrypting your files and storing them into the home/.chezmoitemplates/ssh/ folder. During the provisioning process, all of the files in the home/.chezmoitemplates/ssh/ folder are decrypted and placed into your ~/.ssh folder. For example, if you wanted to encrypt your current id_rsa file into your fork of Install Doctor, you would run:

chezmoi encrypt "$HOME/id_rsa" > "${XDG_DATA_HOME:-$HOME/.local/share}/chezmoi/home/.chezmoitemplates/ssh/id_rsa"

After that, your id_rsa file will be encrypted in your fork and you can safely git commit / git push your encrypted id_rsa file to your public GitHub fork.

VPN Profiles

VPN profiles can be stored in encrypted format into your fork by placing *.ovpn, *.conf (WireGuard for macOS), and *.nmconnection (WireGuard for Linux) files into the home/dot_config/vpn/ folder. Before committing the encrypted configurations into your source, you should encrypt the files with chezmoi encrypt and then add the following Go template logic to make sure that the files are only decrypted when the decryption key is present:

{{- if (stat (joinPath .host.home ".config" "age" "chezmoi.txt")) -}}
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAraUZ1dkFITlJMbmdhbXUx
ekQ2SjRhaE5uT3J1czh0V09hSkZuanYvUTJvCklTcHZ6RFBrUGx0VlhqQytEb01a
...
-----END AGE ENCRYPTED FILE-----
{{- end -}}

You also have to make sure that the file names are named exactly how you want the profiles to show up in the operating system UI and also:

  1. Add private_ to the beginning of the file name so that only your user can read the configurations
  2. Add .tmpl to the end of the file name so that the Go template bindings referenced above are handled properly

OVPN Example

The following is an example of the original .ovpn file that is encrypted and stored in home/dot_config/vpn/:

client
dev tun
proto tcp

remote 1xx.1xx.1xx.1xx 8443
remote 1xx.1xx.1xx.1xx 7770
remote 1xx.1xx.1xx.1xx 443

remote-random
resolv-retry infinite
nobind

# The following setting is only needed for old OpenVPN clients compatibility. New clients
# automatically negotiate the optimal cipher.
cipher AES-256-CBC

auth SHA512
verb 3

setenv CLIENT_CERT 0
tun-mtu 1500
tun-mtu-extra 32
mssfix 1450
persist-key
persist-tun

reneg-sec 0

remote-cert-tls server
auth-user-pass
pull
fast-io

script-security 2
up /etc/openvpn/update-resolv-conf
down /etc/openvpn/update-resolv-conf

<ca>
-----BEGIN CERTIFICATE-----
MIIFozCCA4ugAwIBAgIBATANBgkqhkiG9w0BAQ0FADBAMxxxxxxxxxxxxxxxxxxx
DK/yPwECUcPgHIeXiRjHnJt0Zcm23O2Q3RphpU+1SO3Xixxxxxxxxxxxxxxxxxxx
A1gTTlpi7A==
-----END CERTIFICATE-----
</ca>

key-direction 1
<tls-auth>
# 2048 bit OpenVPN static key
-----BEGIN OpenVPN Static key V1-----
6acef03f62675b4b1bbd0xxxxxxxxxxx
...
16672ea16c012664f8a9fxxxxxxxxxxx
-----END OpenVPN Static key V1-----
</tls-auth>

After the .ovpn file is decrypted to ~/.config/vpn/ during the provision process, if the OVPN_PASSWORD and OVPN_USERNAME are defined (as either environment variables or secrets stored at home/.chezmoitemplates/secrets/), then the file will be properly loaded into the NetworkManager on Linux and equivalent programs on macOS / Windows.

It is important to note that we only support passing one set of OVPN_PASSWORD and OVPN_USERNAME to the provision process so if you have OpenVPN connections from multiple providers with different usernames and passwords then you will have to extend our default logic.

WireGuard Example

The following is an example of a decrypted .nmconnection file stored in home/dot_config/vpn/:

[connection]
id=Proton WG Cambodia (UDP 51820)
uuid=2xxxxxx-30xx-xxxx-xxxx-85xxxxxxxxxx
type=vpn
autoconnect=true
permissions=user:xxx:;

[vpn]
connection-dns=10.2.0.1
local-ip4=10.2.0.2/32
local-private-key=oKHXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX=
peer-allowed-ips=0.0.0.0/0
peer-endpoint=185.159.156.83:51820
peer-public-key=D4M0XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
service-type=org.freedesktop.NetworkManager.wireguard

[ipv4]
dns-search=
method=auto

[ipv6]
addr-gen-mode=stable-privacy
dns-search=
method=auto

[proxy]

As you can see, the authentication keys are located directly in the file so there is no need to load a username and password like in the case of the OpenVPN profiles. The .nmconnection files are specific to the NetworkManager on Linux systems. You may have to craft the .nmconnection files by loading your configuration into the NetworkManager program and then grabbing the source from the resulting .nmconnection file.

On macOS / Windows, the WireGuard .conf file format is used. The same .nmconnection file above would look like this in .conf format:

[Interface]
# NetShield = 1
# Moderate NAT = off
# VPN Accelerator = on
PrivateKey = oKHXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX=
Address = 10.2.0.2/32
DNS = 10.2.0.1

[Peer]
# SE-KH#1
PublicKey = D4M0XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
AllowedIPs = 0.0.0.0/0
Endpoint = 185.159.156.83:51820

Encrypted Files

There are a few cases where entire files need to be encrypted. Whenever possible, in cases like Tabby's configuration stored at home/dot_config/tabby/config.yaml.tmpl the optimized configuration stripped of secrets is provided as a fallback within the template. In cases like this, where no environment variable configuration parameter is made available, the encrypted file parts are stored in home/.chezmoitemplates/files/. The table below describes the capabilities that are offered by these files:

File in .chezmoitemplates/files/Description
tabbyThe Tabby configuration file which can include several types of encryption keys in it. Used in home/dot_config/tabby/config.yaml.tmpl.
google-assistant-client-secretThe client secret required for the unofficial Google Assistant desktop applications. Details on how to set it can be found on the project's wiki. Used in home/dot_config/Google Assistant/client-secret.json.tmpl. The google-assistant-tokens file is also required (details below).
google-assistant-tokensThe tokens required for setting up the unofficial Google Assistant. Used in home/dot_config/Google Assistant/tokens.json.tmpl. The google-assistant-client-secret is also required (details above)
gphotos-syncThe Google Photos API client secret file required for using gphotos-sync to backup your Google Photos content. Used in home/dot_config/gphotos-sync/client_secret.json.tmpl.

If you inspect the files above, you will see that they are stored in their target location as a template that first checks if the encryption key is present. If the key is present, the files get decrypted. Ideally, if the key is not present, then a version of the file without sensitive information should be rendered. An example of this can be found in the Tabby configuration (home/dot_config/tabby/config.yaml). When contributing to this project, be sure to include this fallback logic whenever possible so that users who have not populated their fork with encrypted secrets can still retain functionality.

Creating New Variables / Secrets

If you want to expand the capabilities of your fork beyond what we have provided, we recommend you follow the same approach we use when handling environment variables and secret encrypted keys.

Combining Variables and Secrets

Ideally, whenever possible, the user should be able to pass in a variable as either an environment variable or as a secret included in the fork. In the ~/.config/shell/private.sh file which houses private keys for cloud APIs that the user may want to source by running . "~/.config/shell/private.sh", the DockerHub token is defined with:

export DOCKERHUB_TOKEN="{{ if (stat (joinPath .chezmoi.sourceDir ".chezmoitemplates" "secrets" "key-DOCKERHUB_TOKEN")) }}{{ includeTemplate "secrets/key-DOCKERHUB_TOKEN" }}{{ else }}{{ env "DOCKERHUB_TOKEN" }}{{ end }}"
export DOCKERHUB_REGISTRY_PASSWORD="$DOCKERHUB_TOKEN"

This approach works well because it first checks for the presence of the home/.chezmoitemplates/secrets/key-DOCKERHUB_TOKEN file and then decrypts the value and injects it into the results private.sh file if the key-DOCKERHUB_TOKEN is present. Otherwise, it attempts to use the DOCKERHUB_TOKEN key.

Note: Preferrably, you should store the secret as the same name as the corresponding environment variable. That is, an environment variable named DOCKERHUB_TOKEN should be stored as a key stored in home/.chezmoitemplates/secrets/key-DOCKERHUB_TOKEN. Additionally, encrypted files that decrypt to files stored in ~/.ssh/ should be prefixed with ssh- instead of key-.

Encrypted Files

For encrypted files, you should mark the file as a templated file by ending the file name with .tmpl and then you should include a check to make sure both the decryption key and the encrypted secret data location is available. For example, our ~/.ssh/id_rsa file is generated by placing a file in home/private_dot_ssh/private_id_rsa.tmpl with the following content that makes these checks:

{{- if and (stat (joinPath .host.home ".config" "age" "chezmoi.txt")) (stat (joinPath .chezmoi.sourceDir ".chezmoitemplates" "secrets" "ssh-id-rsa")) -}}
{{ includeTemplate "secrets/ssh-id-rsa" | decrypt -}}
{{ end -}}

At provisioning time, if the necessary files are available, the encrypted secret data stored in home/.chezmoitemplates/secrets/ssh-id-rsa will be decrypted and added to the ~/.ssh/id_rsa file.