Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*.dll
*.so
*.dylib

bin/*
# Test binary
matlas
matlas-test
Expand Down
24 changes: 14 additions & 10 deletions cmd/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"gopkg.in/yaml.v3"

"github.com/teabranch/matlas-cli/internal/config"
"github.com/teabranch/matlas-cli/internal/fileutil"
"github.com/teabranch/matlas-cli/internal/output"
"github.com/teabranch/matlas-cli/internal/validation"
)
Expand Down Expand Up @@ -583,18 +584,15 @@ func runImportConfig(cmd *cobra.Command, sourceFile, targetFile, format string,
finalConfig = normalizedConfig
}

// Ensure target directory exists
if err := os.MkdirAll(filepath.Dir(targetFile), 0o750); err != nil {
return fmt.Errorf("failed to create target directory: %w", err)
}

// Convert to YAML and write to target file
// Convert to YAML
outputData, err := yaml.Marshal(finalConfig)
if err != nil {
return fmt.Errorf("failed to marshal configuration: %w", err)
}

if err := os.WriteFile(targetFile, outputData, 0o600); err != nil {
// SECURITY: Write file with secure permissions
writer := fileutil.NewSecureFileWriter()
if err := writer.WriteFile(targetFile, outputData); err != nil {
return fmt.Errorf("failed to write target file: %w", err)
}

Expand Down Expand Up @@ -642,7 +640,9 @@ func runExportConfig(cmd *cobra.Command, outputFile, format string, includeSecre

// Write to file or stdout
if outputFile != "" {
if err := os.WriteFile(outputFile, output, 0o600); err != nil {
// SECURITY: Write file with secure permissions
writer := fileutil.NewSecureFileWriter()
if err := writer.WriteFile(outputFile, output); err != nil {
return fmt.Errorf("failed to write to output file: %w", err)
}
fmt.Printf("✅ Configuration exported successfully to: %s\n", outputFile)
Expand Down Expand Up @@ -703,7 +703,9 @@ func runMigrateConfig(cmd *cobra.Command, fromVersion, toVersion string, backup
// Create backup if requested
if backup {
backupFile := configFile + ".backup." + strings.ReplaceAll(fromVersion, ".", "_")
if err := os.WriteFile(backupFile, configData, 0o600); err != nil {
// SECURITY: Write backup with secure permissions
writer := fileutil.NewSecureFileWriter()
if err := writer.WriteFile(backupFile, configData); err != nil {
return fmt.Errorf("failed to create backup: %w", err)
}
fmt.Printf("📁 Backup created: %s\n", backupFile)
Expand All @@ -721,7 +723,9 @@ func runMigrateConfig(cmd *cobra.Command, fromVersion, toVersion string, backup
return fmt.Errorf("failed to marshal migrated configuration: %w", err)
}

if err := os.WriteFile(configFile, migratedData, 0o600); err != nil {
// SECURITY: Write file with secure permissions
writer := fileutil.NewSecureFileWriter()
if err := writer.WriteFile(configFile, migratedData); err != nil {
return fmt.Errorf("failed to write migrated configuration: %w", err)
}

Expand Down
11 changes: 11 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,17 @@ var (
Long: "matlas-cli enables unified management of MongoDB Atlas resources and standalone MongoDB databases.",
SilenceUsage: true,
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
// SECURITY: Check if credentials provided via insecure flags
if cmd.Flags().Changed("api-key") || cmd.Flags().Changed("pub-key") {
return fmt.Errorf(
"ERROR: Passing credentials via command-line flags is insecure.\n" +
"Command-line arguments are visible in process listings and shell history.\n\n" +
"Please use one of these secure methods instead:\n" +
" 1. Environment variables: ATLAS_API_KEY and ATLAS_PUB_KEY\n" +
" 2. Config file: ~/.matlas/config.yaml\n" +
" 3. Platform keychain (see documentation)")
}

// 1. Initialize enhanced logging
logConfig := &logging.Config{
Level: logging.LevelInfo,
Expand Down
2 changes: 1 addition & 1 deletion docs/alerts.md
Original file line number Diff line number Diff line change
Expand Up @@ -536,7 +536,7 @@ See the [Alert Examples]({{ '/examples/alerts/' | relative_url }}) for comprehen

- [Alert Examples]({{ '/examples/alerts/' | relative_url }}) - Working YAML examples
- [Atlas Commands]({{ '/atlas/#alerts' | relative_url }}) - CLI command reference
- [YAML Kinds Reference]({{ '/yaml-kinds/#alertconfiguration' | relative_url }}) - Complete AlertConfiguration reference
- [YAML Kinds Reference]({{ '/reference/#alertconfiguration' | relative_url }}) - Complete AlertConfiguration reference
- [Infrastructure Commands]({{ '/infra/' | relative_url }}) - Apply and manage configurations

---
Expand Down
2 changes: 1 addition & 1 deletion docs/atlas.md
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ matlas atlas clusters update my-cluster --project-id <id> --pit
- **Point-in-Time Recovery** (`--pit`): Recovery to any specific moment in time (requires backup)
- **Cross-Region Backup**: Use multi-region cluster configurations (see YAML examples)

**Note:** For complex cluster configurations with multi-region setups, use [infrastructure workflows](/infra/) with YAML configurations.
**Note:** For complex cluster configurations with multi-region setups, use [infrastructure workflows]({{ '/infra/' | relative_url }}) with YAML configurations.

## Atlas Search

Expand Down
6 changes: 3 additions & 3 deletions docs/dag-engine.md
Original file line number Diff line number Diff line change
Expand Up @@ -794,6 +794,6 @@ For very large configurations (1000+ operations):

## Further Reading

- [Infrastructure Workflows](/infra/) - General infrastructure management
- [Discovery Documentation](/discovery/) - Enumerating Atlas resources
- [YAML Kinds Reference](/yaml-kinds-reference/) - Configuration format details
- [Infrastructure Workflows]({{ '/infra/' | relative_url }}) - General infrastructure management
- [Discovery Documentation]({{ '/discovery/' | relative_url }}) - Enumerating Atlas resources
- [YAML Kinds Reference]({{ '/reference/yaml-kinds/' | relative_url }}) - Configuration format details
2 changes: 1 addition & 1 deletion docs/database.md
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ All database user management is handled via `matlas atlas users` commands. Users

### User Management via Atlas API

For complete user management documentation, see the [Atlas Commands](/atlas/) documentation. Here are the essential commands:
For complete user management documentation, see the [Atlas Commands]({{ '/atlas/' | relative_url }}) documentation. Here are the essential commands:

```bash
# Create Atlas database user (propagates to MongoDB databases)
Expand Down
12 changes: 6 additions & 6 deletions docs/examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,11 @@ Complete infrastructure management workflows
- Safe operations with preserve-existing
- Dependency management

### [Search & VPC]({{ '/examples/advanced/' | relative_url }})
Advanced Atlas features
- Atlas Search index configurations
- VPC endpoint setups
- Vector search for AI applications
### [DAG Analysis]({{ '/examples/dag-analysis/' | relative_url }})
Infrastructure optimization and analysis
- Dependency graph visualization
- Critical path analysis
- Bottleneck detection and optimization suggestions

### [Alerts & Monitoring]({{ '/examples/alerts/' | relative_url }})
Atlas alert configurations for monitoring and notifications
Expand Down Expand Up @@ -145,7 +145,7 @@ matlas infra apply -f current.yaml --preserve-existing

## Related Documentation

- [YAML Kinds Reference]({{ '/yaml-kinds/' | relative_url }}) - Complete reference for all resource types
- [YAML Kinds Reference]({{ '/reference/' | relative_url }}) - Complete reference for all resource types
- [Infrastructure Commands]({{ '/infra/' | relative_url }}) - `plan`, `apply`, `diff`, and `destroy` operations
- [Atlas Commands]({{ '/atlas/' | relative_url }}) - Direct Atlas resource management
- [Database Commands]({{ '/database/' | relative_url }}) - MongoDB database operations
Expand Down
2 changes: 1 addition & 1 deletion docs/examples/alerts.md
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,7 @@ notifications:
## Related Documentation

- [Alert CLI Commands]({{ '/atlas/#alerts' | relative_url }}) - Command-line alert management
- [YAML Kinds Reference]({{ '/yaml-kinds/#alertconfiguration' | relative_url }}) - Complete AlertConfiguration reference
- [YAML Kinds Reference]({{ '/reference/#alertconfiguration' | relative_url }}) - Complete AlertConfiguration reference
- [Infrastructure Commands]({{ '/infra/' | relative_url }}) - Apply and manage alert configurations
- [Atlas Documentation]({{ '/atlas/' | relative_url }}) - Atlas resource management

Expand Down
1 change: 1 addition & 0 deletions docs/examples/clusters.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ title: Cluster Examples
parent: Examples
nav_order: 2
description: MongoDB cluster configurations for different environments
permalink: /examples/clusters/
---

# Cluster Examples
Expand Down
8 changes: 5 additions & 3 deletions docs/examples/dag-analysis.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ layout: default
title: DAG Analysis Examples
parent: Examples
nav_order: 8
description: DAG engine examples for analyzing and visualizing infrastructure deployments
permalink: /examples/dag-analysis/
---

# DAG Analysis Examples
Expand Down Expand Up @@ -707,6 +709,6 @@ jq -r '.criticalPathDuration' metrics/analysis-*.json

## Further Reading

- [DAG Engine Documentation](/dag-engine/) - Complete feature guide
- [Infrastructure Workflows](/infra/) - Plan, diff, apply workflows
- [Discovery Documentation](/discovery/) - Enumerating Atlas resources
- [DAG Engine Documentation]({{ '/dag-engine/' | relative_url }}) - Complete feature guide
- [Infrastructure Workflows]({{ '/infra/' | relative_url }}) - Plan, diff, apply workflows
- [Discovery Documentation]({{ '/discovery/' | relative_url }}) - Enumerating Atlas resources
1 change: 1 addition & 0 deletions docs/examples/discovery.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ title: Discovery Examples
parent: Examples
nav_order: 1
description: Convert existing Atlas resources to infrastructure-as-code format
permalink: /examples/discovery/
---

# Discovery Examples
Expand Down
1 change: 1 addition & 0 deletions docs/examples/infrastructure.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ title: Infrastructure Patterns
parent: Examples
nav_order: 6
description: Complete infrastructure management workflows and patterns
permalink: /examples/infrastructure/
---

# Infrastructure Patterns
Expand Down
3 changes: 2 additions & 1 deletion docs/examples/network.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ title: Network Access
parent: Examples
nav_order: 5
description: IP allowlisting and network security configurations
permalink: /examples/network/
---

# Network Access Examples
Expand Down Expand Up @@ -288,4 +289,4 @@ ipAddress: "54.123.45.67"

- [Clusters]({{ '/examples/clusters/' | relative_url }}) - Cluster configurations needing network access
- [Infrastructure Patterns]({{ '/examples/infrastructure/' | relative_url }}) - Complete infrastructure with network security
- [VPC Endpoints]({{ '/examples/advanced/' | relative_url }}) - Private network connectivity
- [YAML Kinds Reference]({{ '/reference/yaml-kinds/' | relative_url }}) - VPC endpoint configuration reference
1 change: 1 addition & 0 deletions docs/examples/roles.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ title: Custom Roles
parent: Examples
nav_order: 4
description: Granular permission management with custom database roles
permalink: /examples/roles/
---

# Custom Roles Examples
Expand Down
1 change: 1 addition & 0 deletions docs/examples/users.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ title: User Management
parent: Examples
nav_order: 3
description: Database user and authentication examples
permalink: /examples/users/
---

# User Management Examples
Expand Down
8 changes: 4 additions & 4 deletions docs/infra.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Terraform-inspired workflows for managing MongoDB Atlas infrastructure as code.

Matlas provides infrastructure-as-code workflows inspired by Terraform and kubectl:

1. **[Discover](/discovery/)** → Enumerate current Atlas resources and optionally include database-level resources
1. **[Discover]({{ '/discovery/' | relative_url }})** → Enumerate current Atlas resources and optionally include database-level resources
2. **Plan/Diff** → Preview changes before applying
3. **Apply** → Reconcile desired state
4. **Show** → Display current state
Expand All @@ -40,7 +40,7 @@ The discovery feature supports comprehensive resource enumeration including clus

Enumerate Atlas resources for a project and optionally convert to ApplyDocument format.

**For comprehensive discovery documentation, see [Discovery](/discovery/)**
**For comprehensive discovery documentation, see [Discovery]({{ '/discovery/' | relative_url }})**

### Basic discovery
```bash
Expand Down Expand Up @@ -76,7 +76,7 @@ matlas discover \
- **Database discovery**: Include databases, collections, indexes, and custom roles
- **Authentication flexibility**: Temporary users, manual credentials, or direct connection strings

See [Discovery documentation](/discovery/) for complete usage guide and examples.
See [Discovery documentation]({{ '/discovery/' | relative_url }}) for complete usage guide and examples.

---

Expand Down Expand Up @@ -105,7 +105,7 @@ matlas infra optimize -f config.yaml --project-id <project-id>
- Generate visual dependency graphs
- Get actionable optimization recommendations

**For comprehensive documentation, see [DAG Engine](/dag-engine/)**
**For comprehensive documentation, see [DAG Engine]({{ '/dag-engine/' | relative_url }})**

---

Expand Down
8 changes: 4 additions & 4 deletions docs/yaml-kinds.md
Original file line number Diff line number Diff line change
Expand Up @@ -595,10 +595,10 @@ spec:

## Related Documentation

- {{ '/infra/' | relative_url }} - Infrastructure commands (`apply`, `plan`, `diff`)
- {{ '/atlas/' | relative_url }} - Atlas resource management
- {{ '/database/' | relative_url }} - Database operations
- {{ '/auth/' | relative_url }} - Authentication and configuration
- [Infrastructure Commands]({{ '/infra/' | relative_url }}) - Infrastructure commands (`apply`, `plan`, `diff`)
- [Atlas Commands]({{ '/atlas/' | relative_url }}) - Atlas resource management
- [Database Commands]({{ '/database/' | relative_url }}) - Database operations
- [Authentication]({{ '/auth/' | relative_url }}) - Authentication and configuration
- [Examples]({{ '/examples/' | relative_url }}) - Working examples for all kinds

---
Expand Down
56 changes: 46 additions & 10 deletions internal/clients/mongodb/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ package mongodb
import (
"context"
"crypto/tls"
"flag"
"fmt"
"os"
"strings"
"time"

"go.mongodb.org/mongo-driver/bson"
Expand All @@ -13,6 +16,7 @@ import (
"go.mongodb.org/mongo-driver/mongo/readpref"

"github.com/teabranch/matlas-cli/internal/logging"
"github.com/teabranch/matlas-cli/internal/security"
"github.com/teabranch/matlas-cli/internal/types"
)

Expand Down Expand Up @@ -53,6 +57,42 @@ func DefaultClientConfig() *ClientConfig {
}
}

// ValidateTLSConfig validates TLS configuration and enforces security requirements
func (c *ClientConfig) ValidateTLSConfig() error {
if c.TLSInsecure && c.TLSEnabled {
// Check for explicit override environment variable
if os.Getenv("MATLAS_ALLOW_INSECURE_TLS") != "true" {
// Check if we're in a test environment
if !isTestEnvironment() {
return fmt.Errorf(
"TLS certificate verification is disabled. " +
"This is INSECURE and only allowed for testing. " +
"Set MATLAS_ALLOW_INSECURE_TLS=true to override (NOT recommended for production)")
}
}

// Log warning even if allowed
logging.Default().Warn("TLS certificate verification is DISABLED - this is insecure!")
}
return nil
}

// isTestEnvironment checks if we're running in a test context
func isTestEnvironment() bool {
// Check if running under go test
if flag.Lookup("test.v") != nil {
return true
}
// Check if binary name suggests test execution
if len(os.Args) > 0 {
arg0 := os.Args[0]
if strings.Contains(arg0, ".test") || strings.Contains(arg0, "/_test/") {
return true
}
}
return false
}

// NewClient creates a new MongoDB client with Atlas-optimized settings
func NewClient(ctx context.Context, config *ClientConfig, logger *logging.Logger) (*Client, error) {
if config == nil {
Expand All @@ -63,6 +103,11 @@ func NewClient(ctx context.Context, config *ClientConfig, logger *logging.Logger
logger = logging.Default()
}

// SECURITY: Validate TLS configuration before proceeding
if err := config.ValidateTLSConfig(); err != nil {
return nil, err
}

clientOptions := options.Client().
ApplyURI(config.ConnectionString).
SetConnectTimeout(config.ConnectTimeout).
Expand Down Expand Up @@ -106,7 +151,7 @@ func NewClient(ctx context.Context, config *ClientConfig, logger *logging.Logger
}

logger.Info("Successfully connected to MongoDB",
"connection_string", maskConnectionString(config.ConnectionString))
"connection_string", security.MaskConnectionString(config.ConnectionString))

return &Client{
client: client,
Expand Down Expand Up @@ -426,12 +471,3 @@ func (c *Client) ListIndexes(ctx context.Context, dbName, collectionName string)

return indexes, nil
}

// maskConnectionString masks sensitive information in connection strings for logging
func maskConnectionString(connectionString string) string {
// Simple masking - in production, use more sophisticated parsing
if len(connectionString) > 50 {
return connectionString[:20] + "***MASKED***" + connectionString[len(connectionString)-10:]
}
return "***MASKED***"
}
Loading