Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

GitHub

This documentation is part of the "Projects with Books" initiative at zenOSmosis.

The source code for this project is available on GitHub.

Version Control Best Practices

Loading…

Version Control Best Practices

Relevant source files

Purpose and Scope

This document explains the version control practices and configurations implemented in this repository to ensure secure handling of secrets and maintain a clean Git history. It focuses on the .gitignore configuration, the template-based approach for sensitive configuration, and recommended workflows for committing changes safely.

For information about the automated CI/CD workflows themselves, see Continuous Integration and Documentation Pipeline. For general configuration management, see Configuration Reference.


File Classification Strategy

The repository implements a strict separation between code/documentation (version controlled) and secrets/runtime data (explicitly excluded). This classification prevents accidental exposure of sensitive information.

graph TB
    subgraph VersionControlled["Version Controlled (Committed to Git)"]
DockerCompose["docker-compose.yml\nService orchestration"]
MosquittoConf["mosquitto.conf\nBroker configuration"]
EnvSample[".env.sample\nSecret template"]
Workflows["GitHub Actions workflows\n.github/workflows/*"]
README["README.md\nDocumentation"]
Gitignore[".gitignore\nExclusion rules"]
end
    
    subgraph Ignored["Explicitly Ignored (Never Committed)"]
EnvFile[".env\nContains CLOUDFLARE_TUNNEL_TOKEN"]
DataDir["data/*\nRuntime broker data"]
end
    
    subgraph Runtime["Runtime Environment"]
DockerEngine["Docker Engine"]
CloudflaredContainer["cloudflared container"]
MosquittoContainer["mosquitto container"]
end
    
 
   DockerCompose -->|orchestrates| DockerEngine
 
   MosquittoConf -->|mounted into| MosquittoContainer
 
   EnvSample -->|developer copies to| EnvFile
 
   Gitignore -->|excludes| EnvFile
 
   Gitignore -->|excludes| DataDir
 
   EnvFile -->|loaded by docker-compose| CloudflaredContainer
 
   DataDir -->|mounted into| MosquittoContainer

File Classification Diagram

Sources : .gitignore:1-2 .env.sample1 docker-compose.yml


The .gitignore Configuration

The .gitignore file serves as the primary security boundary, preventing sensitive files from being committed to version control.

Current Exclusions

PatternPurposeRisk if Committed
.envEnvironment variables containing CLOUDFLARE_TUNNEL_TOKENExposes tunnel access credentials, allowing unauthorized traffic routing
data/*Mosquitto runtime data (messages, persistence files)May contain sensitive message content, creates unnecessary repository bloat

Sources : .gitignore:1-2

Security Implications

The .env file exclusion is critical because it contains the CLOUDFLARE_TUNNEL_TOKEN value. This token provides authentication to route traffic through your Cloudflare Tunnel. If committed to a public repository (or even a private one with multiple collaborators), the token could be:

  1. Extracted from Git history - Even if deleted in a later commit, the token remains in Git history
  2. Used to route unauthorized traffic - An attacker with the token can send traffic through your tunnel
  3. Difficult to rotate - Revoking and regenerating the token requires reconfiguring the entire Cloudflare Tunnel

Sources : .env.sample1 .gitignore1


Template-Based Secret Management

The repository uses a template file pattern to document required secrets without exposing actual values.

sequenceDiagram
    participant Dev as "Developer"
    participant Repo as "Git Repository"
    participant Local as "Local Filesystem"
    participant Docker as "Docker Compose"
    participant CF as "Cloudflare API"
    
    Note over Repo: .env.sample checked into Git\nNote over Repo: .gitignore excludes .env
    
    Dev->>Repo: git clone repository
    Repo->>Local: .env.sample available
    Repo->>Local: .gitignore rules applied
    Dev->>CF: Create tunnel via dashboard
    CF-->>Dev: Receive CLOUDFLARE_TUNNEL_TOKEN
    Dev->>Local: cp .env.sample .env
    Dev->>Local: Edit .env with actual token
    Note over Local: .env now contains secret
    Dev->>Docker: docker-compose up
    Docker->>Local: Read .env file
    Local-->>Docker: CLOUDFLARE_TUNNEL_TOKEN value
    Docker->>Docker: Inject into cloudflared container
    
    Note over Dev,Local: .env never committed due to .gitignore

Template File Flow

Sources : .env.sample1 .gitignore1

The .env.sample Template

The .env.sample file serves three purposes:

  1. Documentation - Shows developers what environment variables are required
  2. Template - Provides copy-paste structure for creating the actual .env file
  3. Placeholder Values - Uses obvious placeholder text (your_token) that will fail if not replaced

Content:

CLOUDFLARE_TUNNEL_TOKEN=your_token

This template is intentionally minimal. The placeholder value your_token will cause the tunnel connection to fail, forcing developers to replace it with a real token from the Cloudflare dashboard.

Sources : .env.sample1


Protected Files and Their Purposes

Files That Are Safe to Commit

FileContains Secrets?Why It’s Safe
docker-compose.ymlNoReferences environment variable by name (${CLOUDFLARE_TUNNEL_TOKEN}), not value
mosquitto.confNoOnly contains port numbers and protocol settings
.env.sampleNoContains placeholder text, not real secrets
README.mdNoDocumentation only

Sources : .env.sample1 docker-compose.yml mosquitto.conf

Files That Must Never Be Committed

File/DirectoryContains Secrets?Contents
.envYESReal CLOUDFLARE_TUNNEL_TOKEN value
data/*PotentiallyMQTT message persistence, retained messages, subscription state

Sources : .gitignore:1-2


Git Workflow Best Practices

Pre-Commit Verification Process

Sources : .gitignore:1-2

  1. Initial Setup (One-time per developer)

    • Clone repository: git clone <repository-url>
    • Verify .gitignore exists: cat .gitignore
    • Copy template: cp .env.sample .env
    • Populate .env with real token from Cloudflare dashboard
    • Verify exclusion: git status should not show .env
  2. Before Each Commit

    • Review staged files: git status
    • Confirm .env is NOT in the staged files list
    • Confirm data/* is NOT in the staged files list
    • If either appears, investigate why (usually means .gitignore is missing or misconfigured)
  3. After Cloning on a New Machine

    • The .env file will not exist (this is correct)
    • Must manually create .env from .env.sample template
    • Must obtain new token from Cloudflare or securely transfer existing token

Sources : .gitignore:1-2 .env.sample1


Verification Commands

Checking Gitignore Effectiveness

Sources : .gitignore1

Staged Files Safety Check

Before committing, run this command to list all files that will be committed:

This output should never include:

  • .env
  • Any file under data/

If either appears, do not commit. Instead:

Sources : .gitignore:1-2


sequenceDiagram
    participant Admin as "Administrator"
    participant CF as "Cloudflare Dashboard"
    participant Local as "Local .env File"
    participant Docker as "Docker Environment"
    participant Git as "Git Repository"
    
    Note over Admin,Git: Token Rotation Process
    Admin->>CF: Regenerate tunnel token
    CF-->>Admin: New CLOUDFLARE_TUNNEL_TOKEN
    Admin->>Local: Update .env with new token
    Note over Local: Old token still in file
    Admin->>Docker: docker-compose down
    Admin->>Docker: docker-compose up
    Docker->>Local: Read updated .env
    Note over Docker: New token now active
    
    Note over Admin,Git: Verification
    Admin->>Git: git status
    Git-->>Admin: .env not in changes
    Note over Admin: Confirm .gitignore still protecting .env
    
    Admin->>CF: Verify tunnel connected
    CF-->>Admin: Tunnel active with new token

Secret Rotation Procedure

When the CLOUDFLARE_TUNNEL_TOKEN needs to be rotated (compromised token, security policy, or regular rotation):

Rotation Flow Diagram

Sources : .env.sample1 .gitignore1

Step-by-Step Rotation

  1. Generate new token in Cloudflare Dashboard (Networks → Tunnels → Select tunnel → Configure)
  2. Update local.env file with new CLOUDFLARE_TUNNEL_TOKEN value
  3. Restart services : docker-compose down && docker-compose up -d
  4. Verify exclusion : Run git status to confirm .env is not staged
  5. Test connection : Verify tunnel connects with new token
  6. Revoke old token (optional, if Cloudflare UI supports it)

Important : The .env file is never committed during this process. The token change remains local to each deployment environment.

Sources : .env.sample1 .gitignore1


Common Mistakes to Avoid

Mistake 1: Committing .env Before Adding .gitignore

Scenario : Developer commits .env file before creating .gitignore.

Result : Token is now in Git history permanently.

Prevention :

  • Ensure .gitignore exists before first commit
  • This repository includes .gitignore from initial commit

Recovery (if it happens):

Sources : .gitignore1

Mistake 2: Using .env.sample Directly

Scenario : Developer renames .env.sample to .env instead of copying.

Result : The template file disappears from version control.

Prevention :

  • Always use cp .env.sample .env (copy, not move)
  • Never run git rm .env.sample

Sources : .env.sample1

Mistake 3: Accidentally Staging with git add -A

Scenario : Running git add -A or git add . without reviewing files.

Result : Might stage .env if .gitignore is misconfigured or missing.

Prevention :

  • Always run git status before git add
  • Use git add <specific-files> for granular control
  • Review git diff --cached before committing

Sources : .gitignore1


Integration with CI/CD

The version control practices integrate with the automated CI/CD pipelines:

CI Pipeline Behavior

The GitHub Actions CI workflow (.github/workflows/ci.yml) does not require the .env file because:

  1. It only tests the mosquitto service, not cloudflared
  2. The Mosquitto broker does not require the CLOUDFLARE_TUNNEL_TOKEN
  3. Secrets would be injected via GitHub Secrets for full-stack testing (not implemented in this repository)

Sources : .github/workflows/ci.yml docker-compose.yml

Documentation Pipeline

The documentation build workflow (.github/workflows/build-docs.yml) operates on the committed files only:

  • Reads .env.sample (committed) to document the template format
  • Never accesses .env (ignored) as it’s not present in the repository
  • Builds static documentation without requiring runtime secrets

Sources : .github/workflows/build-docs.yml .env.sample1


Production Environment Considerations

For production deployments, additional secret management practices should be considered:

Production Secret Management Table

EnvironmentSecret StorageInjection MethodRotation Strategy
Local Development.env filedocker-compose reads fileManual edit + restart
CI/CDGitHub SecretsEnvironment variables in workflowUpdate in GitHub UI
ProductionSecrets manager (AWS Secrets Manager, HashiCorp Vault, etc.)Environment variables via orchestrationAutomated rotation with zero-downtime

For production deployments beyond local development, consider:

  1. Secrets Manager Integration - Replace .env file with calls to a secrets management service
  2. Automated Rotation - Implement automatic token rotation with zero-downtime restarts
  3. Audit Logging - Track when secrets are accessed and by whom
  4. Encryption at Rest - Ensure secrets are encrypted when stored on disk

Sources : .env.sample1


Summary of Key Practices

  1. Never commit.env - The .gitignore configuration prevents this
  2. Always use.env.sample as template - Copy, don’t move
  3. Verify before each commit - Run git status and check for excluded files
  4. Rotate tokens out-of-band - Token updates never touch Git
  5. Audit Git history - Ensure secrets never entered version control

These practices implement the principle of “separation of code and configuration,” where sensitive credentials are injected at runtime rather than embedded in version-controlled artifacts.

Sources : .gitignore:1-2 .env.sample1

Dismiss

Refresh this wiki

Enter email to refresh