568 lines
15 KiB
Markdown
568 lines
15 KiB
Markdown
# Secure Credential Management for WinRM HTTPS
|
|
|
|
This guide covers secure methods for handling passwords and credentials in PowerShell, avoiding plaintext passwords in scripts and command history.
|
|
|
|
## Never Do This
|
|
|
|
```powershell
|
|
# BAD - Password visible in script and command history
|
|
$password = "MyPassword123!"
|
|
$certPass = ConvertTo-SecureString "MyPassword123!" -AsPlainText -Force
|
|
```
|
|
|
|
**Problems:**
|
|
- Password stored in plaintext in script files
|
|
- Visible in PowerShell command history
|
|
- Can be read by anyone with file access
|
|
- Logged in transcripts and logs
|
|
|
|
---
|
|
|
|
## Secure Methods
|
|
|
|
### Method 1: Interactive Prompt (Most Secure for Manual Use)
|
|
|
|
**For certificate password:**
|
|
```powershell
|
|
# PowerShell will prompt securely (characters are hidden)
|
|
$certPass = Read-Host "Enter certificate password" -AsSecureString
|
|
|
|
# Confirm password
|
|
$certPassConfirm = Read-Host "Confirm certificate password" -AsSecureString
|
|
|
|
# Verify they match (optional but recommended)
|
|
$pwd1 = [Runtime.InteropServices.Marshal]::PtrToStringAuto(
|
|
[Runtime.InteropServices.Marshal]::SecureStringToBSTR($certPass)
|
|
)
|
|
$pwd2 = [Runtime.InteropServices.Marshal]::PtrToStringAuto(
|
|
[Runtime.InteropServices.Marshal]::SecureStringToBSTR($certPassConfirm)
|
|
)
|
|
|
|
if ($pwd1 -ne $pwd2) {
|
|
Write-Error "Passwords do not match!"
|
|
exit 1
|
|
}
|
|
|
|
# Use the secure password
|
|
.\Setup-WinRM-HTTPS.ps1 -CertificatePath ".\wildcard.pfx" `
|
|
-CertificatePassword $certPass -Domain "logon.ds.ge.com"
|
|
```
|
|
|
|
**For domain credentials:**
|
|
```powershell
|
|
# Get-Credential shows a secure dialog
|
|
$cred = Get-Credential -Message "Enter domain admin credentials"
|
|
|
|
# Use the credentials
|
|
.\Invoke-RemoteAssetCollection-HTTPS.ps1 `
|
|
-HostnameListFile ".\shopfloor-hostnames.txt" `
|
|
-Domain "logon.ds.ge.com" `
|
|
-Credential $cred
|
|
```
|
|
|
|
---
|
|
|
|
### Method 2: Encrypted File Storage (For Automation)
|
|
|
|
**Store credentials securely:**
|
|
```powershell
|
|
# One-time setup: Save encrypted credentials
|
|
# This creates an encrypted file that only YOUR USER ACCOUNT on THIS COMPUTER can decrypt
|
|
|
|
# For domain credentials
|
|
$cred = Get-Credential -Message "Enter domain admin credentials"
|
|
$cred | Export-Clixml -Path "C:\Secure\shopfloor-admin-cred.xml"
|
|
|
|
# For certificate password
|
|
$certPass = Read-Host "Enter certificate password" -AsSecureString
|
|
$certPass | Export-Clixml -Path "C:\Secure\cert-password.xml"
|
|
```
|
|
|
|
**Use stored credentials:**
|
|
```powershell
|
|
# Import encrypted credentials
|
|
$cred = Import-Clixml -Path "C:\Secure\shopfloor-admin-cred.xml"
|
|
$certPass = Import-Clixml -Path "C:\Secure\cert-password.xml"
|
|
|
|
# Use in scripts
|
|
.\Invoke-RemoteAssetCollection-HTTPS.ps1 `
|
|
-HostnameListFile ".\shopfloor-hostnames.txt" `
|
|
-Domain "logon.ds.ge.com" `
|
|
-Credential $cred
|
|
```
|
|
|
|
**Important notes:**
|
|
- Encrypted files can ONLY be decrypted by the same user on the same computer
|
|
- Safe to store in version control (but not recommended)
|
|
- Won't work if script runs as different user (e.g., scheduled task with service account)
|
|
- Won't work on different computer
|
|
|
|
---
|
|
|
|
### Method 3: Windows Credential Manager (Best for Scheduled Tasks)
|
|
|
|
**Store credentials in Windows Credential Manager:**
|
|
|
|
```powershell
|
|
# Install CredentialManager module (one time)
|
|
Install-Module -Name CredentialManager -Force -Scope CurrentUser
|
|
|
|
# Store credentials
|
|
New-StoredCredential -Target "ShopfloorAdmin" `
|
|
-Username "DOMAIN\serviceaccount" `
|
|
-Password "password" `
|
|
-Type Generic `
|
|
-Persist LocalMachine
|
|
```
|
|
|
|
**Retrieve and use:**
|
|
```powershell
|
|
# Import module
|
|
Import-Module CredentialManager
|
|
|
|
# Get stored credential
|
|
$cred = Get-StoredCredential -Target "ShopfloorAdmin"
|
|
|
|
# Use in script
|
|
.\Invoke-RemoteAssetCollection-HTTPS.ps1 `
|
|
-HostnameListFile ".\shopfloor-hostnames.txt" `
|
|
-Domain "logon.ds.ge.com" `
|
|
-Credential $cred
|
|
```
|
|
|
|
**Advantages:**
|
|
- Works with scheduled tasks
|
|
- Can be used by service accounts
|
|
- Centralized management
|
|
- Encrypted by Windows
|
|
|
|
---
|
|
|
|
### Method 4: Environment Variables (For Automation Contexts)
|
|
|
|
**Set environment variable (for current session):**
|
|
```powershell
|
|
# Not recommended for passwords, but okay for non-sensitive config
|
|
$env:WINRM_DOMAIN = "logon.ds.ge.com"
|
|
$env:WINRM_CERT_PATH = "C:\Certs\wildcard.pfx"
|
|
|
|
# Use in scripts
|
|
.\Setup-WinRM-HTTPS.ps1 `
|
|
-CertificatePath $env:WINRM_CERT_PATH `
|
|
-Domain $env:WINRM_DOMAIN
|
|
```
|
|
|
|
** Do NOT use for passwords:**
|
|
```powershell
|
|
# BAD - Environment variables are not secure for passwords
|
|
$env:CERT_PASSWORD = "MyPassword" # DON'T DO THIS
|
|
```
|
|
|
|
---
|
|
|
|
### Method 5: Azure Key Vault (Enterprise)
|
|
|
|
**For production environments with Azure:**
|
|
|
|
```powershell
|
|
# Install Az modules
|
|
Install-Module -Name Az.KeyVault -Force
|
|
|
|
# Connect to Azure
|
|
Connect-AzAccount
|
|
|
|
# Store secret
|
|
$secretPassword = ConvertTo-SecureString "YourPassword" -AsPlainText -Force
|
|
Set-AzKeyVaultSecret -VaultName "YourKeyVault" `
|
|
-Name "ShopfloorCertPassword" `
|
|
-SecretValue $secretPassword
|
|
|
|
# Retrieve secret
|
|
$secret = Get-AzKeyVaultSecret -VaultName "YourKeyVault" `
|
|
-Name "ShopfloorCertPassword" -AsPlainText
|
|
|
|
$certPass = ConvertTo-SecureString $secret -AsPlainText -Force
|
|
|
|
# Use in script
|
|
.\Setup-WinRM-HTTPS.ps1 -CertificatePath ".\wildcard.pfx" `
|
|
-CertificatePassword $certPass -Domain "logon.ds.ge.com"
|
|
```
|
|
|
|
---
|
|
|
|
## 🔐 Recommended Approaches by Scenario
|
|
|
|
### Scenario 1: Manual Testing (You Running Scripts)
|
|
|
|
**Use: Interactive Prompts**
|
|
|
|
```powershell
|
|
# Let scripts prompt you
|
|
.\Generate-WildcardCert.ps1
|
|
# (Will prompt for password)
|
|
|
|
$cred = Get-Credential
|
|
.\Invoke-RemoteAssetCollection-HTTPS.ps1 `
|
|
-HostnameListFile ".\shopfloor-hostnames.txt" `
|
|
-Domain "logon.ds.ge.com" `
|
|
-Credential $cred
|
|
```
|
|
|
|
**Why:** Simple, secure, no storage needed
|
|
|
|
---
|
|
|
|
### Scenario 2: Scheduled Task (Same User)
|
|
|
|
**Use: Encrypted File Storage**
|
|
|
|
```powershell
|
|
# One-time setup (run as the user who will run scheduled task)
|
|
$cred = Get-Credential
|
|
$cred | Export-Clixml -Path "C:\Secure\shopfloor-cred.xml"
|
|
|
|
# In scheduled task script
|
|
$cred = Import-Clixml -Path "C:\Secure\shopfloor-cred.xml"
|
|
.\Invoke-RemoteAssetCollection-HTTPS.ps1 `
|
|
-HostnameListFile "C:\Scripts\winrm-https\shopfloor-hostnames.txt" `
|
|
-Domain "logon.ds.ge.com" `
|
|
-Credential $cred
|
|
```
|
|
|
|
**Why:** Secure, works automatically, tied to specific user
|
|
|
|
---
|
|
|
|
### Scenario 3: Scheduled Task (Service Account)
|
|
|
|
**Use: Windows Credential Manager**
|
|
|
|
```powershell
|
|
# Setup (run as service account or with appropriate permissions)
|
|
Install-Module CredentialManager -Force
|
|
New-StoredCredential -Target "ShopfloorAdmin" `
|
|
-Username "DOMAIN\svc-shopfloor" `
|
|
-Password "ServiceAccountPassword" `
|
|
-Type Generic `
|
|
-Persist LocalMachine
|
|
|
|
# In scheduled task script
|
|
Import-Module CredentialManager
|
|
$cred = Get-StoredCredential -Target "ShopfloorAdmin"
|
|
.\Invoke-RemoteAssetCollection-HTTPS.ps1 `
|
|
-HostnameListFile "C:\Scripts\winrm-https\shopfloor-hostnames.txt" `
|
|
-Domain "logon.ds.ge.com" `
|
|
-Credential $cred
|
|
```
|
|
|
|
**Why:** Works across users, secure, manageable
|
|
|
|
---
|
|
|
|
### Scenario 4: Enterprise Production
|
|
|
|
**Use: Azure Key Vault or HashiCorp Vault**
|
|
|
|
```powershell
|
|
# Retrieve from Key Vault
|
|
$certPass = Get-AzKeyVaultSecret -VaultName "ProdVault" `
|
|
-Name "WinRMCertPassword" -AsPlainText |
|
|
ConvertTo-SecureString -AsPlainText -Force
|
|
|
|
$cred = Get-AzKeyVaultSecret -VaultName "ProdVault" `
|
|
-Name "ShopfloorAdminCred" -AsPlainText |
|
|
ConvertTo-SecureString -AsPlainText -Force
|
|
|
|
# Use in deployment
|
|
.\Setup-WinRM-HTTPS.ps1 -CertificatePath ".\wildcard.pfx" `
|
|
-CertificatePassword $certPass -Domain "logon.ds.ge.com"
|
|
```
|
|
|
|
**Why:** Centralized, audited, compliant, scalable
|
|
|
|
---
|
|
|
|
## 📝 Updated Script Examples
|
|
|
|
### Generate Certificate (Secure)
|
|
|
|
```powershell
|
|
# Interactive (recommended for manual use)
|
|
.\Generate-WildcardCert.ps1
|
|
# Will prompt: "Enter password for PFX file:"
|
|
|
|
# With pre-stored password (for automation)
|
|
$certPass = Import-Clixml -Path "C:\Secure\cert-password.xml"
|
|
.\Generate-WildcardCert.ps1 -Password $certPass
|
|
```
|
|
|
|
---
|
|
|
|
### Setup WinRM HTTPS (Secure)
|
|
|
|
```powershell
|
|
# Interactive (recommended for testing)
|
|
.\Setup-WinRM-HTTPS.ps1 -CertificatePath ".\wildcard.pfx" `
|
|
-Domain "logon.ds.ge.com"
|
|
# Will prompt: "Enter certificate password:"
|
|
|
|
# With stored password (for automation)
|
|
$certPass = Import-Clixml -Path "C:\Secure\cert-password.xml"
|
|
.\Setup-WinRM-HTTPS.ps1 -CertificatePath ".\wildcard.pfx" `
|
|
-CertificatePassword $certPass -Domain "logon.ds.ge.com"
|
|
|
|
# With Credential Manager (for scheduled tasks)
|
|
Import-Module CredentialManager
|
|
$certPassPlain = (Get-StoredCredential -Target "CertPassword").Password
|
|
$certPass = ConvertTo-SecureString $certPassPlain -AsPlainText -Force
|
|
.\Setup-WinRM-HTTPS.ps1 -CertificatePath ".\wildcard.pfx" `
|
|
-CertificatePassword $certPass -Domain "logon.ds.ge.com"
|
|
```
|
|
|
|
---
|
|
|
|
### Remote Collection (Secure)
|
|
|
|
```powershell
|
|
# Interactive (recommended for manual use)
|
|
.\Invoke-RemoteAssetCollection-HTTPS.ps1 `
|
|
-HostnameListFile ".\shopfloor-hostnames.txt" `
|
|
-Domain "logon.ds.ge.com"
|
|
# Will prompt: "Enter credentials for remote computer access:"
|
|
|
|
# With stored credentials (for automation)
|
|
$cred = Import-Clixml -Path "C:\Secure\shopfloor-admin-cred.xml"
|
|
.\Invoke-RemoteAssetCollection-HTTPS.ps1 `
|
|
-HostnameListFile ".\shopfloor-hostnames.txt" `
|
|
-Domain "logon.ds.ge.com" `
|
|
-Credential $cred
|
|
|
|
# With Credential Manager (for scheduled tasks)
|
|
Import-Module CredentialManager
|
|
$cred = Get-StoredCredential -Target "ShopfloorAdmin"
|
|
.\Invoke-RemoteAssetCollection-HTTPS.ps1 `
|
|
-HostnameListFile ".\shopfloor-hostnames.txt" `
|
|
-Domain "logon.ds.ge.com" `
|
|
-Credential $cred
|
|
```
|
|
|
|
---
|
|
|
|
## 🛡️ Security Best Practices
|
|
|
|
### Do's
|
|
|
|
1. **Always use SecureString for passwords**
|
|
```powershell
|
|
$password = Read-Host "Password" -AsSecureString
|
|
```
|
|
|
|
2. **Use Get-Credential for domain accounts**
|
|
```powershell
|
|
$cred = Get-Credential
|
|
```
|
|
|
|
3. **Store credentials in encrypted files or credential manager**
|
|
```powershell
|
|
$cred | Export-Clixml -Path "C:\Secure\cred.xml"
|
|
```
|
|
|
|
4. **Set proper file permissions on credential files**
|
|
```powershell
|
|
# Only allow current user access
|
|
$acl = Get-Acl "C:\Secure\cred.xml"
|
|
$acl.SetAccessRuleProtection($true, $false)
|
|
$rule = New-Object System.Security.AccessControl.FileSystemAccessRule(
|
|
$env:USERNAME, "FullControl", "Allow"
|
|
)
|
|
$acl.AddAccessRule($rule)
|
|
Set-Acl "C:\Secure\cred.xml" $acl
|
|
```
|
|
|
|
5. **Clear sensitive variables after use**
|
|
```powershell
|
|
$certPass = $null
|
|
$cred = $null
|
|
[System.GC]::Collect()
|
|
```
|
|
|
|
### Don'ts
|
|
|
|
1. **Never hardcode passwords**
|
|
```powershell
|
|
# BAD
|
|
$password = "MyPassword123!"
|
|
```
|
|
|
|
2. **Never pass passwords as plaintext parameters**
|
|
```powershell
|
|
# BAD
|
|
.\Script.ps1 -Password "MyPassword"
|
|
```
|
|
|
|
3. **Never store passwords in environment variables**
|
|
```powershell
|
|
# BAD
|
|
$env:PASSWORD = "MyPassword"
|
|
```
|
|
|
|
4. **Never commit credentials to version control**
|
|
```powershell
|
|
# BAD - Adding cred.xml to git
|
|
```
|
|
|
|
5. **Never log passwords**
|
|
```powershell
|
|
# BAD
|
|
Write-Host "Password: $password"
|
|
Add-Content "log.txt" "Password: $password"
|
|
```
|
|
|
|
---
|
|
|
|
## Setting Up Secure Credential Storage
|
|
|
|
### Step 1: Create Secure Directory
|
|
|
|
```powershell
|
|
# Create directory for secure files
|
|
$securePath = "C:\Secure"
|
|
New-Item -Path $securePath -ItemType Directory -Force
|
|
|
|
# Set permissions (only current user)
|
|
$acl = Get-Acl $securePath
|
|
$acl.SetAccessRuleProtection($true, $false)
|
|
$rule = New-Object System.Security.AccessControl.FileSystemAccessRule(
|
|
$env:USERNAME, "FullControl", "Allow"
|
|
)
|
|
$acl.AddAccessRule($rule)
|
|
Set-Acl $securePath $acl
|
|
|
|
Write-Host "Secure directory created: $securePath"
|
|
```
|
|
|
|
---
|
|
|
|
### Step 2: Store Certificate Password
|
|
|
|
```powershell
|
|
# Prompt for certificate password
|
|
Write-Host "Setting up certificate password storage..." -ForegroundColor Cyan
|
|
$certPass = Read-Host "Enter certificate password" -AsSecureString
|
|
$certPassConfirm = Read-Host "Confirm certificate password" -AsSecureString
|
|
|
|
# Verify match
|
|
$pwd1 = [Runtime.InteropServices.Marshal]::PtrToStringAuto(
|
|
[Runtime.InteropServices.Marshal]::SecureStringToBSTR($certPass)
|
|
)
|
|
$pwd2 = [Runtime.InteropServices.Marshal]::PtrToStringAuto(
|
|
[Runtime.InteropServices.Marshal]::SecureStringToBSTR($certPassConfirm)
|
|
)
|
|
|
|
if ($pwd1 -ne $pwd2) {
|
|
Write-Error "Passwords do not match!"
|
|
exit 1
|
|
}
|
|
|
|
# Save encrypted
|
|
$certPass | Export-Clixml -Path "C:\Secure\cert-password.xml"
|
|
Write-Host "Certificate password saved securely to C:\Secure\cert-password.xml" -ForegroundColor Green
|
|
```
|
|
|
|
---
|
|
|
|
### Step 3: Store Domain Credentials
|
|
|
|
```powershell
|
|
# Prompt for domain credentials
|
|
Write-Host "Setting up domain admin credentials..." -ForegroundColor Cyan
|
|
$cred = Get-Credential -Message "Enter domain admin credentials for shopfloor PCs"
|
|
|
|
# Save encrypted
|
|
$cred | Export-Clixml -Path "C:\Secure\shopfloor-admin-cred.xml"
|
|
Write-Host "Domain credentials saved securely to C:\Secure\shopfloor-admin-cred.xml" -ForegroundColor Green
|
|
```
|
|
|
|
---
|
|
|
|
### Step 4: Create Helper Script
|
|
|
|
```powershell
|
|
# Create script to load credentials
|
|
@'
|
|
# Load-SecureCredentials.ps1
|
|
# Helper script to load stored credentials
|
|
|
|
function Get-CertificatePassword {
|
|
if (Test-Path "C:\Secure\cert-password.xml") {
|
|
return Import-Clixml -Path "C:\Secure\cert-password.xml"
|
|
}
|
|
else {
|
|
Write-Warning "Certificate password not found. Please run setup."
|
|
return Read-Host "Enter certificate password" -AsSecureString
|
|
}
|
|
}
|
|
|
|
function Get-DomainCredential {
|
|
if (Test-Path "C:\Secure\shopfloor-admin-cred.xml") {
|
|
return Import-Clixml -Path "C:\Secure\shopfloor-admin-cred.xml"
|
|
}
|
|
else {
|
|
Write-Warning "Domain credentials not found. Please run setup."
|
|
return Get-Credential -Message "Enter domain admin credentials"
|
|
}
|
|
}
|
|
|
|
Export-ModuleMember -Function Get-CertificatePassword, Get-DomainCredential
|
|
'@ | Out-File -FilePath ".\Load-SecureCredentials.ps1" -Encoding UTF8
|
|
|
|
Write-Host "Helper script created: .\Load-SecureCredentials.ps1" -ForegroundColor Green
|
|
```
|
|
|
|
---
|
|
|
|
### Step 5: Use in Scripts
|
|
|
|
```powershell
|
|
# Import helper
|
|
. .\Load-SecureCredentials.ps1
|
|
|
|
# Get stored credentials
|
|
$certPass = Get-CertificatePassword
|
|
$cred = Get-DomainCredential
|
|
|
|
# Use in operations
|
|
.\Setup-WinRM-HTTPS.ps1 -CertificatePath ".\wildcard.pfx" `
|
|
-CertificatePassword $certPass -Domain "logon.ds.ge.com"
|
|
|
|
.\Invoke-RemoteAssetCollection-HTTPS.ps1 `
|
|
-HostnameListFile ".\shopfloor-hostnames.txt" `
|
|
-Domain "logon.ds.ge.com" `
|
|
-Credential $cred
|
|
```
|
|
|
|
---
|
|
|
|
## Summary Comparison
|
|
|
|
| Method | Security | Ease of Use | Automation | Cross-User | Enterprise |
|
|
|--------|----------|-------------|------------|------------|------------|
|
|
| Interactive Prompt | | | | | |
|
|
| Encrypted File | | | | | |
|
|
| Credential Manager | | | | | |
|
|
| Azure Key Vault | | | | | |
|
|
| Plaintext (DON'T) | | | | | |
|
|
|
|
---
|
|
|
|
## 🎯 Recommendation
|
|
|
|
**For your shopfloor PC scenario:**
|
|
|
|
1. **Testing/Development:** Use interactive prompts
|
|
2. **Production Manual:** Use encrypted files
|
|
3. **Scheduled Tasks:** Use Windows Credential Manager
|
|
4. **Enterprise Scale:** Consider Azure Key Vault
|
|
|
|
**Start with Method 1 (Interactive Prompts) for testing, then move to Method 2 (Encrypted Files) or Method 3 (Credential Manager) for production automation.**
|