Skip to main content

26_secure_instance_bootstrap/
26_secure_instance_bootstrap.rs

1//! Example 26: Secure Instance Bootstrap with Authentication
2//!
3//! This example demonstrates the SECURE way to initialize a WAMI instance
4//! and authenticate as root. This prevents brute force attacks on instance IDs.
5//!
6//! # Security Model
7//!
8//! 1. **Instance Bootstrap** - Generate root user with credentials
9//! 2. **Credential Storage** - Save credentials securely (shown once!)
10//! 3. **Authentication** - Required for all operations
11//! 4. **Authorization** - Policy-based access control
12//!
13//! # Critical Security Notes
14//!
15//! ⚠️  Root credentials are shown ONLY during initialization
16//! ⚠️  They are hashed with bcrypt and cannot be retrieved later
17//! ⚠️  Without credentials, no access is possible (even for root!)
18//! ⚠️  This prevents brute force attacks on instance IDs
19
20use std::sync::Arc;
21use tokio::sync::RwLock;
22use wami::store::memory::InMemoryWamiStore;
23use wami::{
24    AmiError, AuthenticationService, CreateUserRequest, InstanceBootstrap, RootCredentials,
25    UserService,
26};
27
28#[tokio::main]
29async fn main() -> Result<(), Box<dyn std::error::Error>> {
30    println!("🔐 WAMI Secure Instance Bootstrap Example\n");
31    println!("{}", "=".repeat(60));
32
33    // =========================================================================
34    // Step 1: Initialize the Store
35    // =========================================================================
36    println!("\n📦 Step 1: Initialize Store");
37    let store = Arc::new(RwLock::new(InMemoryWamiStore::default()));
38    println!("✅ In-memory store created");
39
40    // =========================================================================
41    // Step 2: Bootstrap Instance with Root User & Credentials
42    // =========================================================================
43    println!("\n🚀 Step 2: Bootstrap Instance");
44    println!("    Creating instance '999888777' with root user...");
45
46    let instance_id = "999888777";
47    let root_creds = InstanceBootstrap::initialize_instance(store.clone(), instance_id).await?;
48
49    println!("\n✅ Instance initialized successfully!");
50    println!("\n🔑 ROOT CREDENTIALS (SAVE THESE SECURELY!):");
51    println!("{}", "=".repeat(60));
52    println!("   Access Key ID:     {}", root_creds.access_key_id);
53    println!("   Secret Access Key: {}", root_creds.secret_access_key);
54    println!("   Instance ID:       {}", root_creds.instance_id);
55    println!("   User ARN:          {}", root_creds.user_arn);
56    println!("{}", "=".repeat(60));
57    println!("\n⚠️  CRITICAL: These credentials are shown ONLY ONCE!");
58    println!("⚠️  Save them in a secure location (secrets manager, vault, etc.)");
59    println!("⚠️  They CANNOT be retrieved later!");
60
61    // =========================================================================
62    // Step 3: Authenticate as Root
63    // =========================================================================
64    println!("\n🔓 Step 3: Authenticate as Root");
65    println!("    Using access key authentication...");
66
67    let auth_service = AuthenticationService::new(store.clone());
68
69    let root_context = auth_service
70        .authenticate(&root_creds.access_key_id, &root_creds.secret_access_key)
71        .await?;
72
73    println!("✅ Authentication successful!");
74    println!("   Authenticated as: {}", root_context.caller_arn());
75    println!("   Instance ID:      {}", root_context.instance_id());
76    println!("   Tenant Path:      {}", root_context.tenant_path());
77    println!("   Is Root:          {}", root_context.is_root());
78
79    // =========================================================================
80    // Step 4: Perform Operations with Authenticated Context
81    // =========================================================================
82    println!("\n👤 Step 4: Create Admin User (as root)");
83
84    // The same store handle that bootstrapped the instance and authenticated
85    // the caller also serves UserService — one lock type, one source of truth.
86    let user_service = UserService::new(store.clone());
87    let admin_user = user_service
88        .create_user(
89            &root_context,
90            CreateUserRequest {
91                user_name: "admin".to_string(),
92                path: Some("/".to_string()),
93                permissions_boundary: None,
94                tags: None,
95            },
96        )
97        .await?;
98
99    println!("✅ Admin user created: {}", admin_user.user_name);
100    println!("   ARN:              {}", admin_user.wami_arn);
101    println!("   Context is required for all operations");
102    println!("   Root context bypasses authorization checks");
103
104    // =========================================================================
105    // Step 5: Demonstrate Security - Invalid Credentials
106    // =========================================================================
107    println!("\n🛡️  Step 5: Demonstrate Security");
108    println!("    Attempting authentication with invalid secret...");
109
110    let result = auth_service
111        .authenticate(&root_creds.access_key_id, "wrong_secret_key")
112        .await;
113
114    match result {
115        Err(AmiError::AccessDenied { .. }) => {
116            println!("✅ Invalid credentials rejected (as expected)");
117            println!("   Brute force attacks are prevented!");
118        }
119        Ok(_) => {
120            println!("❌ ERROR: Invalid credentials should have been rejected!");
121        }
122        Err(e) => {
123            println!("❌ Unexpected error: {:?}", e);
124        }
125    }
126
127    // =========================================================================
128    // Step 6: Demonstrate Instance State Check
129    // =========================================================================
130    println!("\n🔍 Step 6: Check Instance State");
131
132    let is_initialized = InstanceBootstrap::is_initialized(store.clone(), instance_id).await?;
133    println!(
134        "   Instance '{}' initialized: {}",
135        instance_id, is_initialized
136    );
137
138    let other_instance_initialized =
139        InstanceBootstrap::is_initialized(store.clone(), "123456789").await?;
140    println!(
141        "   Instance '123456789' initialized: {}",
142        other_instance_initialized
143    );
144
145    // =========================================================================
146    // Summary
147    // =========================================================================
148    println!("\n📋 SECURITY SUMMARY");
149    println!("{}", "=".repeat(60));
150    println!("✅ Instance requires initialization before use");
151    println!("✅ Root user has cryptographically secure credentials");
152    println!("✅ Credentials are hashed with bcrypt (never plaintext)");
153    println!("✅ Authentication is mandatory for all operations");
154    println!("✅ Invalid credentials are rejected");
155    println!("✅ No way to brute force instance IDs without credentials");
156    println!("{}", "=".repeat(60));
157
158    println!("\n🎯 BEST PRACTICES");
159    println!("{}", "=".repeat(60));
160    println!("1. Store credentials in secrets manager (AWS/Vault/etc.)");
161    println!("2. Never commit credentials to version control");
162    println!("3. Never log plaintext secrets");
163    println!("4. Use root only for initial setup");
164    println!("5. Create admin users with specific policies");
165    println!("6. Rotate credentials regularly");
166    println!("7. Use principle of least privilege");
167    println!("{}", "=".repeat(60));
168
169    println!("\n✅ Example completed successfully!");
170
171    Ok(())
172}
173
174// =============================================================================
175// Helper Functions for Production Use
176// =============================================================================
177
178/// Example: How to securely store credentials in production
179///
180/// In production, you would:
181/// 1. Store in AWS Secrets Manager, HashiCorp Vault, etc.
182/// 2. Encrypt at rest
183/// 3. Control access with IAM/RBAC
184/// 4. Enable audit logging
185/// 5. Rotate credentials regularly
186#[allow(dead_code)]
187async fn store_credentials_securely(
188    creds: &RootCredentials,
189) -> Result<(), Box<dyn std::error::Error>> {
190    // Example: Store in AWS Secrets Manager
191    // let client = aws_sdk_secretsmanager::Client::new(&aws_config::load_from_env().await);
192    // client.create_secret()
193    //     .name(format!("wami/instance/{}/root", creds.instance_id))
194    //     .secret_string(serde_json::to_string(creds)?)
195    //     .send()
196    //     .await?;
197
198    println!("📝 Credentials stored in secrets manager");
199    println!("   Secret name: wami/instance/{}/root", creds.instance_id);
200
201    Ok(())
202}
203
204/// Example: How to retrieve credentials from secure storage
205#[allow(dead_code)]
206async fn retrieve_credentials_from_vault(
207    instance_id: &str,
208) -> Result<RootCredentials, Box<dyn std::error::Error>> {
209    // Example: Retrieve from secrets manager
210    // let client = aws_sdk_secretsmanager::Client::new(&aws_config::load_from_env().await);
211    // let response = client.get_secret_value()
212    //     .secret_id(format!("wami/instance/{}/root", instance_id))
213    //     .send()
214    //     .await?;
215    //
216    // let creds: RootCredentials = serde_json::from_str(response.secret_string().unwrap())?;
217
218    println!("🔑 Credentials retrieved from vault");
219
220    // Return mock for example purposes
221    Ok(RootCredentials {
222        access_key_id: "AKIAIOSFODNN7EXAMPLE".to_string(),
223        secret_access_key: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY".to_string(),
224        instance_id: instance_id.to_string(),
225        user_arn: format!("arn:wami:iam:root:wami:{}:user/root", instance_id),
226    })
227}
228
229/// Example: How to rotate root credentials
230#[allow(dead_code)]
231async fn rotate_root_credentials(
232    store: Arc<tokio::sync::RwLock<InMemoryWamiStore>>,
233    instance_id: &str,
234    old_creds: &RootCredentials,
235) -> Result<RootCredentials, Box<dyn std::error::Error>> {
236    println!("🔄 Rotating root credentials...");
237
238    // 1. Authenticate with old credentials
239    let auth_service = AuthenticationService::new(store.clone());
240    let _context = auth_service
241        .authenticate(&old_creds.access_key_id, &old_creds.secret_access_key)
242        .await?;
243
244    // 2. Create new access key for root user
245    // (This would use AccessKeyService in a real implementation)
246
247    // 3. Test new credentials
248    // (Authenticate with new credentials to verify)
249
250    // 4. Delete old access key
251    // (Once new credentials are confirmed working)
252
253    // 5. Update secrets manager with new credentials
254
255    println!("✅ Credentials rotated successfully");
256    println!("⚠️  Save the new credentials and delete the old ones!");
257
258    // Return new credentials (mock for example)
259    Ok(RootCredentials {
260        access_key_id: "AKIANEWKEY123456789".to_string(),
261        secret_access_key: "newSecretKey0123456789abcdefghijklmnop".to_string(),
262        instance_id: instance_id.to_string(),
263        user_arn: old_creds.user_arn.clone(),
264    })
265}