Public/New-SSHCredentials.ps1
<#
.SYNOPSIS This function creates a new SSH User/Client key pair and has the Vault Server sign the Public Key, returning a '-cert.pub' file that can be used for Public Key Certificate SSH Authentication. .DESCRIPTION See .SYNOPSIS .NOTES .PARAMETER VaultServerBaseUri This parameter is MANDATORY. This parameter takes a string that represents a Uri referencing the location of the Vault Server on your network. Example: "https://vaultserver.zero.lab:8200/v1" .PARAMETER DomainCredentialsWithAccessToVault This parameter is OPTIONAL, however, either -DomainCredentialsWithAccessToVault or -VaultAuthToken are REQUIRED. This parameter takes a PSCredential. Example: $Creds = [pscredential]::new("zero\zeroadmin",$(Read-Host "Please enter the password for 'zero\zeroadmin'" -AsSecureString)) .PARAMETER AuthorizedPrincipalString This parameter is OPTIONAL. This parameter takes a a string that represents the "Authorized Principal" that will be addedd to the user ssh certificate. This user account should be listed in the 'authorized_principals' file on the Remote Host(s) you would like to ssh to. The value for thhis parameter should be in format '<DomainUser>@<FullDomain>' or '<LocalUser>@<RemoteHostName>' If you do NOT use this parameter, then the user account provided with the -DomainCredentialsWithAccessToVault parameter will be used. .PARAMETER VaultAuthToken This parameter is OPTIONAL, however, either -DomainCredentialsWithAccessToVault or -VaultAuthToken are REQUIRED. This parameter takes a string that represents a Token for a Vault User that has (root) permission to lookup Tokens using the Vault Server REST API. .PARAMETER NewSSHKeyName This parameter is MANDATORY. This parameter takes a string that represents the file name that you would like to give to the new SSH User/Client Keys. .PARAMETER NewSSHKeyPurpose This parameter is OPTIONAL. This parameter takes a string that represents a very brief description of what the new SSH Keys will be used for. This description will be added to the Comment section when the new keys are created. .PARAMETER NewSSHKeyPwd This parameter is OPTIONAL. This parameter takes a SecureString that represents the password used to protect the new Private Key file that is created. .PARAMETER BlankSSHPrivateKeyPwd This parameter is OPTIONAL. This parameter is a switch. Use it to ensure that the newly created Private Key is NOT password protected. .PARAMETER AddToSSHAgent This parameter is OPTIONAL, but recommended. This parameter is a switch. If used, the new SSH Key Pair will be added to the ssh-agent service. .PARAMETER RemovePrivateKey This parameter is OPTIONAL. This parameter should only be used in conjunction with the -AddtoSSHAgent switch. This parameter is a switch. If used, the newly created Private Key will be added to the ssh-agent and deleted from the filesystem. .PARAMETER SSHAgentExpiry This parameter is OPTIONAL. This parameter should only be used in conjunction with the -AddtoSSHAgent switch. This parameter takes an integer that specifies the number of seconds that the ssh key identity will remain in the ssh-agent - at which point it will expire and be removed from the ssh-agent. .EXAMPLE # Open an elevated PowerShell Session, import the module, and - PS C:\Users\zeroadmin> $NewSSHCredentialsSplatParams = @{ VaultServerBaseUri = $VaultServerBaseUri VaultAuthToken = $VaultAuthToken NewSSHKeyName = $NewSSHKeyName AddToSSHAgent = $True } PS C:\Users\zeroadmin> $NewSSHCredsResult = New-SSHCredentials @NewSSHCredentialsSplatParams #> function New-SSHCredentials { [CmdletBinding()] Param ( [Parameter(Mandatory=$True)] [ValidatePattern("\/v1$")] [string]$VaultServerBaseUri, [Parameter(Mandatory=$False)] [pscredential]$DomainCredentialsWithAccessToVault, [Parameter(Mandatory=$False)] [ValidatePattern("[a-zA-Z0-9]+@[a-zA-Z0-9]+")] [string[]]$AuthorizedPrincipalString, [Parameter(Mandatory=$False)] [string]$VaultAuthToken, [Parameter(Mandatory=$True)] [string]$NewSSHKeyName, [Parameter(Mandatory=$False)] [ValidatePattern("^\w*$")] # No spaces allowed [string]$NewSSHKeyPurpose, [Parameter(Mandatory=$False)] [System.Security.SecureString]$NewSSHKeyPwd, [Parameter(Mandatory=$False)] [switch]$BlankSSHPrivateKeyPwd, [Parameter(Mandatory=$False)] [switch]$AddToSSHAgent, [Parameter(Mandatory=$False)] [switch]$RemovePrivateKey, [Parameter(Mandatory=$False)] [int]$SSHAgentExpiry ) if ($PSVersionTable.Platform -eq "Unix" -or $PSVersionTable.OS -match "Darwin" -and $env:SudoPwdPrompt) { if (GetElevation) { Write-Error "You should not be running the $($MyInvocation.MyCommand.Name) function as root! Halting!" $global:FunctionResult = "1" return } RemoveMySudoPwd NewCronToAddSudoPwd $env:SudoPwdPrompt = $False } if (!$PSVersionTable.Platform -or $PSVersionTable.Platform -eq "Win32NT") { [Net.ServicePointManager]::SecurityProtocol = "tls12, tls11, tls" if (!$(GetElevation)) { Write-Error "The $($MyInvocation.MyCommand.Name) function must be run from an elevated PowerShell session! Halting!" $global:FunctionResult = "1" return } } if ($(!$VaultAuthToken -and !$DomainCredentialsWithAccessToVault) -or $($VaultAuthToken -and $DomainCredentialsWithAccessToVault)) { Write-Error "The $($MyInvocation.MyCommand.Name) function requires one (no more, no less) of the following parameters: [-DomainCredentialsWithAccessToVault, -VaultAuthToken] Halting!" $global:FunctionResult = "1" return } if ($DomainCredentialsWithAccessToVault) { $GetVaultLoginSplatParams = @{ VaultServerBaseUri = $VaultServerBaseUri DomainCredentialsWithAccessToVault = $DomainCredentialsWithAccessToVault ErrorAction = "Stop" } try { $VaultAuthToken = Get-VaultLogin @GetVaultLoginSplatParams if (!$VaultAuthToken) {throw "The Get-VaultLogin function failed! Halting!"} } catch { Write-Error $_ $global:FunctionResult = "1" return } } if (!$DomainCredentialsWithAccessToVault -and !$AuthorizedPrincipalString) { $ErrMsg = "Either the -DomainCredentialsWithAccessToVault parameter or -AuthorizedPrincipalString parameter is required!`n" + "The value for -DomainCredentialsWithAccessToVault should be in format '<DomainRoot>\<DomainUser>'`n" + "The value for -AuthorizedPrincipalString should be in format '<DomainUser>@<FullDomain>' or '<LocalUser>@<RemoteHostName>'" Write-Error $ErrMsg $global:FunctionResult = "1" return } $HeadersParameters = @{ "X-Vault-Token" = $VaultAuthToken } # Generate an SSH key pair for zeroadmin $UserSSHDir = Join-Path $HOME .ssh if (!$(Test-Path $UserSSHDir)) { New-Item -ItemType Directory -Path $UserSSHDir } Push-Location $UserSSHDir $NewSSHKeySplatParams = @{ NewSSHKeyName = $NewSSHKeyName ErrorAction = "Stop" } if ($NewSSHKeyPurpose) { $NewSSHKeySplatParams.Add("NewSSHKeyPurpose",$NewSSHKeyPurpose) } if ($NewSSHKeyPwd) { $KeyPwd = $NewSSHKeyPwd } if (!$BlankSSHPrivateKeyPwd -and !$NewSSHKeyPwd) { #$KeyPwd = Read-Host -Prompt "Please enter a password to protect the new SSH Private Key $NewSSHKeyName" -AsSecureString $BlankSSHPrivateKeyPwd = $True } if ($KeyPwd) { $NewSSHKeySplatParams.Add("NewSSHKeyPwd",$KeyPwd) } try { $NewSSHKeyResult = New-SSHKey @NewSSHKeySplatParams if (!$NewSSHKeyResult) {throw "There was a problem with the New-SSHKey function! Halting!"} } catch { Write-Error $_ $global:FunctionResult = "1" return } # Have Vault sign the User's New public key if (!$AuthorizedPrincipalString) { $AuthorizedPrincipalUserPrep = $DomainCredentialsWithAccessToVault.UserName -split "\\" $AuthorizedPrincipalString = $AuthorizedPrincipalUserPrep[-1] + "@" + $AuthorizedPrincipalUserPrep[0] } <# else { #$AuthorizedPrincipalString = $($(whoami) -split "\\")[-1] + "@" + $($(whoami) -split "\\")[0] $UserName = whoami if ($UserName -match '\\') { $DomainNameShort = $($UserName -split '\\')[0] $UserNameShort = $($UserName -split '\\')[-1] $AuthorizedPrincipalString = $UserNameShort + "@" + $DomainNameShort } else { $UserNameShort = $UserName if ($env:HOSTNAME) { $ActualHostName = if ($env:HOSTNAME -match '\.') {$($env:HOSTNAME -split '\.')[0]} else {$env:HOSTNAME} } else { $ActualHostName = if ($env:ComputerName -match '\.') {$($env:ComputerName -split '\.')[0]} else {$env:ComputerName} } $AuthorizedPrincipalString = $UserNameShort + "@" + $ActualHostName } } #> $SignSSHUserPubKeySplatParams = @{ VaultSSHClientSigningUrl = "$VaultServerBaseUri/ssh-client-signer/sign/clientrole" VaultAuthToken = $VaultAuthToken AuthorizedUserPrincipals = @($AuthorizedPrincipalString) PathToSSHUserPublicKeyFile = $NewSSHKeyResult.PublicKeyFilePath PathToSSHUserPrivateKeyFile = $NewSSHKeyResult.PrivateKeyFilePath ErrorAction = "Stop" } if ($AddToSSHAgent) { $SignSSHUserPubKeySplatParams.Add("AddToSSHAgent",$True) } if ($SSHAgentExpiry) { $SignSSHUserPubKeySplatParams.Add("SSHAgentExpiry",$SSHAgentExpiry) } try { $SignSSHUserPublicKeyResult = Sign-SSHUserPublicKey @SignSSHUserPubKeySplatParams if (!$SignSSHUserPublicKeyResult) {throw "There was a problem with the Sign-SSHUserPublicKey function! Halting!"} } catch { Write-Error $_ $global:FunctionResult = "1" return } if ($RemovePrivateKey -and $SignSSHUserPublicKeyResult.AddedToSSHAgent) { Remove-Item $NewSSHKeyResult.PrivateKeyFilePath -Force } # Next, pull the Vault Host Signing CA Public Key and Vault Client (User) Signing CA Public Key into the necessary config files # NOTE: The Add-CAPubKeyToSSHAndSSHDConfig function will NOT do anything if it doesn't need to $AddCAPubKeyToSSHAndSSHDConfigSplatParams = @{ PublicKeyOfCAUsedToSignUserKeysVaultUrl = "$VaultServerBaseUri/ssh-client-signer/public_key" PublicKeyOfCAUsedToSignHostKeysVaultUrl = "$VaultServerBaseUri/ssh-host-signer/public_key" AuthorizedUserPrincipals = @($AuthorizedPrincipalString) ErrorAction = "Stop" } try { $AddCAPubKeyResult = Add-CAPubKeyToSSHAndSSHDConfig @AddCAPubKeyToSSHAndSSHDConfigSplatParams } catch { Write-Warning "There was a problem with the Add-CAPubKeyToSSHAndSSHDConfig function! The problem is as follows:" Write-Warning "$($_ | Out-String)" Write-Warning "SSH Cert Authentication may still work..." } # Finally, figure out the most efficient ssh command to use to remote into the remote host. Write-Host "Determining the most efficient ssh command to use with your new credentials..." if ($PSVersionTable.Platform -eq "Unix" -or $PSVersionTable.OS -match "Darwin") { Write-Warning "Please IGNORE any password prompts that may appear in STDOUT." } $Output = Get-SSHClientAuthSanity -SSHKeyFilePath $NewSSHKeyResult.PublicKeyFilePath -AuthMethod PublicKeyCertificate if (Test-Path $NewSSHKeyResult.PrivateKeyFilePath) { $Output | Add-Member -Type NoteProperty -Name PrivateKeyPath -Value $NewSSHKeyResult.PrivateKeyFilePath } if (Test-Path $NewSSHKeyResult.PublicKeyFilePath) { $Output | Add-Member -Type NoteProperty -Name PublicKeyPath -Value $NewSSHKeyResult.PublicKeyFilePath } if (Test-Path $SignSSHUserPublicKeyResult.SignedCertFile.FullName) { $Output | Add-Member -Type NoteProperty -Name PublicCertPath -Value $SignSSHUserPublicKeyResult.SignedCertFile.FullName } $Output Pop-Location } |