Skip to content

Add GEMINI_API_KEY_FILE for secure credential storage#3

Open
Amy-Ra-lph wants to merge 1 commit into
crunchtools:mainfrom
Amy-Ra-lph:file-based-credentials
Open

Add GEMINI_API_KEY_FILE for secure credential storage#3
Amy-Ra-lph wants to merge 1 commit into
crunchtools:mainfrom
Amy-Ra-lph:file-based-credentials

Conversation

@Amy-Ra-lph

Copy link
Copy Markdown

Summary

  • Adds GEMINI_API_KEY_FILE env var support using the standard Docker/Kubernetes _FILE suffix convention
  • API keys can be stored in files with 600 permissions instead of in plaintext env vars or config JSON
  • README updated with "Secure credential storage (recommended)" section showing file and container mount patterns
  • Containerfile comments updated with both secure (mount) and quick (env var) examples
  • Fully backward compatible — GEMINI_API_KEY env var continues to work unchanged

Closes #2

Changes

File Change
config.py Check GEMINI_API_KEY_FILE when GEMINI_API_KEY is not set; read key from file
README.md Add secure credential storage section, update security features list
Containerfile Add secure mount example to header comments

Test plan

  • GEMINI_API_KEY=key still works (backward compat)
  • GEMINI_API_KEY_FILE=/path/to/file reads key from file
  • Missing file at GEMINI_API_KEY_FILE path raises clear error
  • Neither set → existing error message (updated to mention both options)
  • Container: -v key:/run/secrets/api-key:ro,z -e GEMINI_API_KEY_FILE=/run/secrets/api-key works

Assisted-by: Claude Code

Adds the standard _FILE suffix convention (used by Docker secrets
and Kubernetes) so API keys can be stored in files with restricted
permissions instead of in environment variables or config JSON.

When GEMINI_API_KEY is not set, the server checks GEMINI_API_KEY_FILE
and reads the key from the specified file path. This prevents keys
from being stored in plaintext in Claude Code's MCP config or being
visible in process lists via container env vars.

Backward compatible — GEMINI_API_KEY env var still works.

Assisted-by: Claude Code

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces support for loading the Gemini API key from a file via the GEMINI_API_KEY_FILE environment variable, with corresponding updates to the Containerfile and README. Review feedback identifies that this implementation violates repository style guide rules prohibiting filesystem access and requiring credentials to be environment-variable only. Furthermore, the implementation lacks proper error handling for file reads and fails to ensure the loaded key is available for the existing error sanitization logic. A minor documentation improvement was also suggested for path consistency.

Comment on lines +37 to +38
api_key = key_path.read_text().strip()
logger.info(f"Loaded API key from {key_path}")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

This implementation has two issues:

  1. Security: It breaks the API key sanitization logic in GeminiApiError (defined in src/mcp_gemini_crunchtools/errors.py). That logic relies on the GEMINI_API_KEY environment variable to identify and mask the key in error messages. When the key is loaded from a file, this variable remains unset, potentially leaking the plaintext key in logs or MCP responses if the Gemini API returns it in an error message.
  2. Robustness: The read_text() call can raise PermissionError or other OSError if the file is not readable. These should be caught and wrapped in a ConfigurationError to provide a clear error message and avoid unhandled tracebacks.
Suggested change
api_key = key_path.read_text().strip()
logger.info(f"Loaded API key from {key_path}")
try:
api_key = key_path.read_text().strip()
# Set environment variable to ensure sanitization in GeminiApiError works
os.environ["GEMINI_API_KEY"] = api_key
logger.info(f"Loaded API key from {key_path}")
except OSError as e:
raise ConfigurationError(f"Failed to read GEMINI_API_KEY_FILE: {e}")
References
  1. API credentials MUST use SecretStr — never logged (Rule 10). The sanitization logic in GeminiApiError is essential to enforce this requirement when the API returns the key in error messages. (link)

Comment on lines +33 to +42
key_file = os.environ.get("GEMINI_API_KEY_FILE")
if key_file:
key_path = Path(key_file).expanduser()
if key_path.is_file():
api_key = key_path.read_text().strip()
logger.info(f"Loaded API key from {key_path}")
else:
raise ConfigurationError(
f"GEMINI_API_KEY_FILE points to missing file: {key_file}"
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

This change introduces filesystem access to load credentials, which appears to violate the Repository Style Guide's security rules. Rule 11 requires credentials to come from environment variables only, and Rule 12 prohibits filesystem access. While file-based secrets are a common best practice in containerized environments, they conflict with the current stated security policy. Please confirm if the style guide should be updated to accommodate this feature.

References
  1. Credentials MUST come from environment variables only (Rule 11) (link)
  2. No filesystem access (Rule 12) (link)

Comment thread README.md
chmod 600 ~/.config/mcp-gemini/api-key

claude mcp add mcp-gemini-crunchtools \
--env GEMINI_API_KEY_FILE=$HOME/.config/mcp-gemini/api-key \

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using the ~ shortcut instead of $HOME is more consistent with the code's implementation (which uses Path.expanduser()) and is more robust if the environment variable is stored in a configuration file without shell expansion.

Suggested change
--env GEMINI_API_KEY_FILE=$HOME/.config/mcp-gemini/api-key \
--env GEMINI_API_KEY_FILE=~/.config/mcp-gemini/api-key \

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

API keys stored in plaintext in MCP config when using --env GEMINI_API_KEY

1 participant