CloudMask helps you safely share AWS infrastructure data with Large Language Models by anonymizing sensitive identifiers while maintaining structure and reversibility.
Features
- 🔒 Secure Anonymization: Hash-based deterministic anonymization
- 🔄 Reversible: Complete mapping for unanonymization
- 🏗️ Structure-Preserving: Maintains AWS resource ID prefixes (vpc-, i-, etc.)
- ⚙️ Configurable: YAML-based configuration for company names and custom patterns
- 🐍 Dual Interface: Use as CLI tool or Python library
- 📋 Clipboard Support: Direct clipboard anonymization for quick workflows
- 📦 Modern Python: Built with Python 3.10+ features (pattern matching, union types)
- 🚀 Minimal Dependencies: Only requires PyYAML and pyperclip
Requirements
- Python 3.10 or higher
- PyYAML 6.0+
CloudMask leverages modern Python features including structural pattern matching, union type operators, and built-in generic types.
Installation
pip install cloudmask-aws
Quick Start
CLI Usage
# Generate config file
cloudmask init-config
# Anonymize (mapping stored in ~/.cloudmask/mapping.json by default)
cloudmask anonymize -i infrastructure.txt -o anonymized.txt
# Anonymize clipboard content
cloudmask anonymize --clipboard
# Restore original values
cloudmask unanonymize -i llm-response.txt -o restored.txt
# Restore clipboard content
cloudmask unanonymize --clipboard
# Use custom mapping location if needed
cloudmask anonymize --clipboard -m /path/to/custom-mapping.json
Python Library Usage
from cloudmask import CloudMask, CloudUnmask, Storage
# Anonymize text
mask = CloudMask(seed="my-secret-seed")
anonymized = mask.anonymize("""
Instance i-1234567890abcdef0 is running in vpc-abcdef123456
Account: 123456789012
Company: Acme Corp
""")
# Save mapping to secure central location (~/.cloudmask/mapping.json)
mask.save_mapping(Storage.DefaultMappingPath)
# Unanonymize later
unmask = CloudUnmask(mapping_file=Storage.DefaultMappingPath)
original = unmask.unanonymize(anonymized)
Quick Function Usage
from cloudmask import anonymize, unanonymize
# One-liner anonymization
text, mapping = anonymize(
"Instance i-123 in account 123456789012",
seed="my-seed",
company_names=["Acme Corp"]
)
# Restore original
original = unanonymize(text, mapping)
Configuration
Create a ~/.cloudmask/config.yml file (or use cloudmask init-config):
company_names:
- Acme Corp
- Example Inc
- MyCompany LLC
custom_patterns:
- pattern: '\bTICKET-\d{4,6}\b'
name: ticket
- pattern: '\bPROJ-[A-Z0-9]+'
name: project
# Example: Match company domains
- pattern: '[a-zA-Z0-9][-a-zA-Z0-9.]*\.acme\.local\b'
name: domain
# Example: Match company emails
- pattern: '[a-zA-Z0-9._%+-]+@acme-corp\.local\b'
name: email
preserve_prefixes: true
anonymize_ips: true
anonymize_domains: false
seed: my-secret-seed
Custom Pattern Edge Cases
When creating custom patterns, be aware of:
- Case sensitivity: Patterns are case-insensitive by default
- Multi-level subdomains: Use
[-a-zA-Z0-9.]*to matchapp.corp.acme.local - HTML entities:
"in data is handled automatically - Email variations: Pattern supports
user+tag@domainanduser.name@domain - URLs and paths: Patterns work in
https://server.acme.local/path - Trailing punctuation: Word boundaries (
\b) handleserver.acme.local.
Test your patterns:
cloudmask anonymize -i test.txt -o output.txt
grep -i "sensitive-term" output.txt # Should return nothing
What Gets Anonymized?
- ✅ AWS Resource IDs (vpc-, i-, sg-, ami-, etc.)
- ✅ AWS Account IDs (12-digit numbers)
- ✅ AWS ARNs
- ✅ IP Addresses
- ✅ Company names (from config)
- ✅ Custom patterns (via regex)
- ✅ Domain names (optional)
Use Cases
- 🤖 LLM Assistance: Get help with infrastructure without exposing sensitive IDs
- 📊 Data Sharing: Share infrastructure diagrams and configs safely
- 🔍 Security Analysis: Analyze configs with external tools
- 📝 Documentation: Create shareable examples from real infrastructure
Advanced Usage
Using in Scripts
from cloudmask import CloudMask, Config
from pathlib import Path
# Load custom config
config = Config.from_yaml(Path("custom-config.yaml"))
# Create anonymizer
mask = CloudMask(config=config, seed="production-seed")
# Process multiple files
for file in Path("configs").glob("*.yaml"):
output = Path("anonymized") / file.name
mask.anonymize_file(file, output)
# Save single mapping for all files
mask.save_mapping("master-mapping.json")
Context Manager
from cloudmask import TemporaryMask
with TemporaryMask(seed="temp-seed") as mask:
anonymized = mask.anonymize("vpc-123 i-456")
# Process anonymized data
# Mapping is discarded after context exits
Batch Processing
from cloudmask import create_batch_anonymizer
# Create reusable anonymizer
anon = create_batch_anonymizer(seed="batch-seed")
# Use it multiple times
result1 = anon("vpc-123")
result2 = anon("i-456")
Central Storage
CloudMask stores mapping files in a secure central location by default:
~/.cloudmask/mapping.json
Benefits
- 🔐 Automatic Security: Files created with
600permissions (owner read/write only) - 📍 Centralized: All mappings in one location
- 🔄 Auto-Merge: New mappings are merged with existing ones (same seed required)
- ⚡ Convenient: No need to specify
-mflag for common workflows
Seed Verification
CloudMask tracks which seed created each mapping file. Mappings can only be merged if they use the same seed:
# First use
mask1 = CloudMask(seed="prod-seed")
mask1.anonymize("vpc-11111")
mask1.save_mapping(Storage.DefaultMappingPath) # Creates mapping
# Later use with SAME seed - mappings merge automatically
mask2 = CloudMask(seed="prod-seed")
mask2.anonymize("vpc-22222")
mask2.save_mapping(Storage.DefaultMappingPath) # Merges with existing
# Different seed - prevents accidental corruption
mask3 = CloudMask(seed="different-seed")
mask3.save_mapping(Storage.DefaultMappingPath) # ERROR: Seed mismatch
Custom Locations
You can still use custom mapping locations:
cloudmask anonymize -i file.txt -m /custom/path/mapping.json
mask.save_mapping("/custom/path/mapping.json")
Security Notes
⚠️ Keep your mapping files secure! They contain the reversible mappings.
- The
~/.cloudmask/directory is automatically secured with700permissions - Mapping files are created with
600permissions (owner only) - Store mapping files separately from anonymized data
- Use strong, unique seeds for different projects
- Don't commit mapping files to version control
- Consider encrypting mapping files for sensitive data
- Always use the same seed for a project to enable mapping merges
Contributing
Contributions welcome! Please check out the GitHub repository.
License
MIT License - see LICENSE file for details
Troubleshooting
Pattern Not Matching?
- Test the regex:
python -c "import re; print(re.search(r'your-pattern', 'test-string'))" - Check case sensitivity: Patterns are case-insensitive
- Verify word boundaries:
\bmay not work with special characters - Test with real data: Use small samples first
Data Still Leaking?
- Check pattern order: Custom patterns run before company names
- Verify pattern name: Use generic names like
domain, notacme-domain - Test thoroughly:
grep -i "sensitive" output.txt
Support
Metadata
Release files for cloudmask-aws 0.4.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| cloudmask_aws-0.4.1.tar.gz | 448.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| cloudmask_aws-0.4.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 479.6 kB
Release files / cloudmask_aws-0.4.1.tar.gz
| Download URL | cloudmask_aws-0.4.1.tar.gz |
|---|---|
| Size | 448.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
4557e509b9710784c78cf236e95a404c25ff87d3ccd7545ae1eab5177b929706
|
|
BLAKE2b-256 checksum How to use checksums |
749731d9a29a44e09f475c2157f3a13f862cca7a3df952f3541e9324edbae5b7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.0
|
Release files / cloudmask_aws-0.4.1-py3-none-any.whl
| Download URL | cloudmask_aws-0.4.1-py3-none-any.whl |
|---|---|
| Size | 31.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2ba455ed8836cf1ed264aaeebc80adf3a8ade1bbd3fb20e5932b472ff243ea70
|
|
BLAKE2b-256 checksum How to use checksums |
edd8d5a7343f086d664f0df7f3679066a31abe45b9df8c9d3fac85df435965ed
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.0
|