diff --git a/BouncyCastle.Crypto.dll b/BouncyCastle.Crypto.dll new file mode 100644 index 0000000..181c000 Binary files /dev/null and b/BouncyCastle.Crypto.dll differ diff --git a/EC2Access.psd1 b/EC2Access.psd1 new file mode 100644 index 0000000..aac0c67 --- /dev/null +++ b/EC2Access.psd1 @@ -0,0 +1,143 @@ +# +# Module manifest for module 'EC2Access' +# +# +# Copyright 2021 Cloudsoft Corporation Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +@{ + +# Script module or binary module file associated with this manifest. +RootModule = 'EC2Access.psm1' + +# Version number of this module. +ModuleVersion = '1.0.0' + +# Supported PSEditions +# CompatiblePSEditions = @() + +# ID used to uniquely identify this module +GUID = '676c68f1-dc0c-4c88-8435-0d06041a39eb' + +# Author of this module +Author = 'Richard Downer' + +# Company or vendor of this module +CompanyName = 'Cloudsoft' + +# Copyright statement for this module +Copyright = '© 2021 Cloudsoft Corporation Ltd.' + +# Description of the functionality provided by this module +Description = 'Provides easy Remote Desktop access to EC2 instances' + +# Minimum version of the PowerShell engine required by this module +PowerShellVersion = '5.1' +# Probably earlier is compatible but this is untested + +# Name of the PowerShell host required by this module +# PowerShellHostName = '' + +# Minimum version of the PowerShell host required by this module +# PowerShellHostVersion = '' + +# Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only. +DotNetFrameworkVersion = '4.0.0' + +# Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only. +ClrVersion = '4.0.0' + +# Processor architecture (None, X86, Amd64) required by this module +# ProcessorArchitecture = '' + +# Modules that must be imported into the global environment prior to importing this module +RequiredModules = @( 'AWS.Tools.EC2', 'AWS.Tools.SimpleSystemsManagement' ) + +# Assemblies that must be loaded prior to importing this module +RequiredAssemblies = @( 'BouncyCastle.Crypto.dll' ) + +# Script files (.ps1) that are run in the caller's environment prior to importing this module. +# ScriptsToProcess = @() + +# Type files (.ps1xml) to be loaded when importing this module +# TypesToProcess = @() + +# Format files (.ps1xml) to be loaded when importing this module +# FormatsToProcess = @() + +# Modules to import as nested modules of the module specified in RootModule/ModuleToProcess +# NestedModules = @() + +# Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. +FunctionsToExport = @( 'Get-EC2Password', 'Start-DirectEC2RemoteDesktop', 'Start-EC2RemoteDesktopViaSessionManager' ) + +# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. +CmdletsToExport = @() + +# Variables to export from this module +VariablesToExport = @() + +# Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. +AliasesToExport = @() + +# DSC resources to export from this module +# DscResourcesToExport = @() + +# List of all modules packaged with this module +# ModuleList = @() + +# List of all files packaged with this module +FileList = @( 'EC2Access.psd1', 'EC2Access.psm1', 'BouncyCastle.Crypto.dll', 'LICENSE', 'README.md', 'LICENSE.BouncyCastle', 'en-US/about_EC2Access.help.txt' ) + +# Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. +PrivateData = @{ + + PSData = @{ + + # Tags applied to this module. These help with module discovery in online galleries. + # Tags = @() + + # A URL to the license for this module. + LicenseUri = 'https://opensource.org/licenses/Apache-2.0' + + # A URL to the main website for this project. + ProjectUri = 'https://github.com/richardcloudsoft/EC2Access' + + # A URL to an icon representing this module. + # IconUri = '' + + # ReleaseNotes of this module + # ReleaseNotes = '' + + # Prerelease string of this module + # Prerelease = '' + + # Flag to indicate whether the module requires explicit user acceptance for install/update/save + # RequireLicenseAcceptance = $false + + # External dependent modules of this module + # ExternalModuleDependencies = @() + + } # End of PSData hashtable + +} # End of PrivateData hashtable + +# HelpInfo URI of this module +# HelpInfoURI = '' + +# Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. +# DefaultCommandPrefix = '' + +} + diff --git a/EC2Access.psm1 b/EC2Access.psm1 new file mode 100644 index 0000000..7a74c1f --- /dev/null +++ b/EC2Access.psm1 @@ -0,0 +1,261 @@ +# +# Main code and public interface for EC2Access module +# +# +# Copyright 2021 Cloudsoft Corporation Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +$ErrorActionPreference = 'Stop' + +function Convert-RSAEncryptedCipherTextToClearText { + Param ( + [Parameter(Mandatory=$true)][Alias('pem')][Alias('p')][string]$pemFile, + [Parameter(Mandatory=$true)][Alias('cipher')][string]$cipherText + ) + + if (-not (Test-Path -Path $pemFile)) { + # file does not exist - handle error + } + + $sr = [System.IO.StreamReader]::new($pemFile) + $pr = [Org.BouncyCastle.OpenSsl.PemReader]::new($sr) + $keyPair = $pr.ReadObject() + $rsa = [Org.BouncyCastle.Security.DotNetUtilities]::ToRSA($keyPair.Private) + $cipherBytes = [System.Convert]::FromBase64String($cipherText) + $clearBytes = $rsa.Decrypt($cipherBytes, $false) + + Return [System.Text.Encoding]::ASCII.GetString($clearBytes) +} + +<# + .SYNOPSIS + Get the Administrator password for a Windows EC2 instance. + + .PARAMETER InstanceId + The AWS EC2 instance ID. This is the string "i-" followed by a series of hexadecimal digits. + + .PARAMETER Region + The AWS region containing the instance. If omitted, the region is fetched from the environment or the AWS configuration files. + + .PARAMETER PrivateKeyFile + A file containing the private key to decrypt the password. This must be in "PEM" format, as used by SSH and as given by AWS if you use it to create a keypair. If omitted, it will default to ".ssh\id_rsa" from your home directory. + + .DESCRIPTION + This function queries the AWS APIs for the encrypted password data of an EC2 instance, and then attempts to decrypt it using a private key stored in a file on your computer. The decrypted password is returned as a SecureString. + + AWS EC2 contains a public key store. When starting an EC2 instance, you will be required to choose a public key. For Windows instances, EC2 will generate a random password, assign it to the Administrator user, and then encrypt the password using the chosen public key and stores it in the EC2 control plane. + + This function will fetch the encrypted data, then decrypt it using the private key. The private key must be in a file on disk and written in PEM format. This is the standard format using by SSH for storing private keys. If you use EC2's "Create key" function, it will store the public key and then download the private key in PEM format to you (without storing it). + + .EXAMPLE + Get-EC2Password -InstanceId i-12345678abcd -Region eu-west-2 + + .OUTPUTS + A SecureString object containing the password for the EC2 instance. +#> +function Get-EC2Password { + [CmdletBinding()] param( + [Parameter(Mandatory=$true, Position=0)] [string]$InstanceId, + [Parameter(Position=1)] [string]$Region, + [Parameter(Position=2)] [string]$PrivateKeyFile + ) + + $ErrorActionPreference = "Stop" + + # Verify the private key files exists + if($null -eq $PrivateKeyFile) { + $PrivateKeyFile = $HOME + '\.ssh\id_rsa' + } + if(-not (Test-Path $PrivateKeyFile)) { + Write-Error "$($PrivateKeyFile) does not exist. Do you need to use -PrivateKeyFile argument?" + } + + Write-Verbose "Requesting password data from AWS" + $cipherText = (Get-EC2PasswordData -Region $Region -InstanceId $InstanceId) + + Write-Verbose "Decrypting password" + $password = Convert-RSAEncryptedCipherTextToClearText -PemFile $PrivateKeyFile -CipherText $cipherText + + return (ConvertTo-SecureString -String $password -AsPlainText -Force) +} + +<# + .SYNOPSIS + Start a Remote Desktop session with a Windows EC2 instance on a public IP address. + + .PARAMETER InstanceId + The AWS EC2 instance ID. This is the string "i-" followed by a series of hexadecimal digits. + + .PARAMETER Region + The AWS region containing the instance. If omitted, the region is fetched from the environment or the AWS configuration files. + + .PARAMETER PrivateKeyFile + A file containing the private key to decrypt the password. This must be in "PEM" format, as used by SSH and as given by AWS if you use it to create a keypair. If omitted, it will default to ".ssh\id_rsa" from your home directory. + + .DESCRIPTION + Start a Remote Desktop session with an EC2 instance. The instance must have a public IP address and a security group rule that permits access from your public IP address. + + This function will retrieve the Windows Administrator password (see Get-EC2Password) and "pre-load" the credentials into the Remote Desktop client, so that you will not need to manually enter credentials into the Remote Desktop client. + + If your instance is not reachable by public IP address, but it is configured for Systems Manager Session Manager, see Start-EC2RemoteDesktopViaSessionManager for a way to start Remote Desktop sessions. + + .EXAMPLE + Start-DirectEC2RemoteDesktop -InstanceId i-12345678abcd -Region eu-west-2 +#> +function Start-DirectEC2RemoteDesktop { + [CmdletBinding(SupportsShouldProcess)] param( + [Parameter(Mandatory=$true, Position=0)] [string]$InstanceId, + [Parameter(Position=1)] [string]$Region, + [Parameter(Position=2)] [string]$PrivateKeyFile + ) + + $password = Get-EC2Password -Instance $InstanceId -Region $Region -PrivateKeyFile $PrivateKeyFile + $Credential = New-Object PSCredential "Administrator",$password + + $instance = (Get-EC2Instance -Region $Region -InstanceId $InstanceId).Instances[0] + $HostName = $instance.PublicIpAddress + Write-Verbose "Instance IP address is $HostName" + + if ($PSCmdlet.ShouldProcess($InstanceId,'Start remote desktop session')) { + Start-RemoteDesktop -HostName $HostName -Credential $Credential + } +} + +<# + .SYNOPSIS + Start a Remote Desktop session with a Windows EC2 instance using Systems Manager Session Manager. + + .PARAMETER InstanceId + The AWS EC2 instance ID. This is the string "i-" followed by a series of hexadecimal digits. + + .PARAMETER Region + The AWS region containing the instance. If omitted, the region is fetched from the environment or the AWS configuration files. + + .PARAMETER PrivateKeyFile + A file containing the private key to decrypt the password. This must be in "PEM" format, as used by SSH and as given by AWS if you use it to create a keypair. If omitted, it will default to ".ssh\id_rsa" from your home directory. + + .DESCRIPTION + Start a Remote Desktop session with an EC2 instance. The instance must be configured to support Systems Manager Session Manager. This function will configure a Session Manager port forwarding session and invoke the Remote Desktop client through the forwarded port. + + This function will retrieve the Windows Administrator password (see Get-EC2Password) and "pre-load" the credentials into the Remote Desktop client, so that you will not need to manually enter credentials into the Remote Desktop client. + + .EXAMPLE + Start-EC2RemoteDesktopViaSessionManager -InstanceId i-12345678abcd -Region eu-west-2 +#> +function Start-EC2RemoteDesktopViaSessionManager { + [CmdletBinding(SupportsShouldProcess)] param( + [Parameter(Mandatory=$true, Position=0)] [string]$InstanceId, + [Parameter(Position=1)] [string]$Region, + [Parameter(Position=2)] [string]$PrivateKeyFile + ) + + $password = Get-EC2Password -Instance $InstanceId -Region $Region -PrivateKeyFile $PrivateKeyFile + $Credential = New-Object PSCredential "Administrator",$password + + $LocalPort = 33389 + $PortForwardParams = @{ portNumber=(,"3389"); localPortNumber=(,$LocalPort.ToString()) } + $session = Start-SSMSession -Target $InstanceId -Region $Region -DocumentName AWS-StartPortForwardingSession -Parameters $PortForwardParams + + # We now need to emulate awscli - it invokes session-manager-plugin with the new session information. + # AWS Tools for PowerShell don't do this. Also some of the objects seem to look a bit different, and the + # plugin is pernickety, so we have to jump through some hoops to get all the objects matching up as close + # as we can. + + $SessionData = @{ + SessionId=$session.SessionID; + StreamUrl=$session.StreamUrl; + TokenValue=$session.TokenValue; + ResponseMetadata=@{ + RequestId=$session.ResponseMetadata.RequestId; + HTTPStatusCode=$session.HttpStatusCode; + RetryAttempts=0; + HTTPHeaders=@{ + server="server"; + "content-type"="application/x-amz-json-1.1"; + "content-length"=$session.ContentLength; + connection="keep-alive"; + "x-amzn-requestid"=$session.ResponseMetadata.RequestId; + } + } + } + + $RequestData = @{ + Target=$InstanceId; + DocumentName="AWS-StartPortForwardingSession"; + Parameters=$PortForwardParams + } + + $Arguments = ( + (ConvertTo-Json $SessionData -Compress), + $Region, + "StartSession", + "", + (ConvertTo-Json $RequestData -Compress), + "https://ssm.$($Region).amazonaws.com" + ) + + # Now we have to do some PowerShell hacking. Start-Process takes an array of arguments, which is great, + # but it doesn't actually do what we expect it to - see https://github.com/PowerShell/PowerShell/issues/5576. + # So instead we have to turn it into an escaped string ourselves... + $EscapedArguments = $Arguments | ForEach-Object { $escaped = $_ -replace "`"", "\`""; "`"$($escaped)`"" } + $ArgumentString = $EscapedArguments -join " " + + # Start the Session Manager plugin: + if ($PSCmdlet.ShouldProcess($session.SessionId,'Start Session Manager plugin')) { + try { + $Process = Start-Process -FilePath "session-manager-plugin.exe" -ArgumentList $ArgumentString -NoNewWindow -PassThru + } catch { + Write-Error "Unable to start the process session-manager-plugin.exe. Have you installed the Session Manager Plugin as described in https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html#install-plugin-windows ?" + exit + } + # Wait a moment for it to connect to the session and open up the local ports + Start-Sleep -Seconds 1 + + # The port should be open now - let's connect + if ($PSCmdlet.ShouldProcess($InstanceId,'Start remote desktop session')) { + Start-RemoteDesktop -HostName "127.0.0.1" -Credential $Credential -Port $LocalPort + } + + # Once the desktop session has finished, kill the session manager plugin + $Process.Kill() + } + +} + +function Start-RemoteDesktop { + [CmdletBinding(SupportsShouldProcess)] param( + [Parameter(Mandatory=$true, Position=0)] [String] [string]$HostName, + [Parameter(Mandatory=$true, Position=1)] [PSCredential] [string]$Credential, + [Parameter()] [Int32] [string]$Port + ) + + $nwcredential = $Credential.GetNetworkCredential() + + if ($PSCmdlet.ShouldProcess($HostName,'Adding credentials to store')) { + Start-Process -FilePath "$($env:SystemRoot)\system32\cmdkey.exe" -ArgumentList ("/generic:TERMSRV/$HostName","/user:$($nwcredential.UserName)","/pass:$($nwcredential.Password)") -WindowStyle Hidden -Wait + } + + if ($PSCmdlet.ShouldProcess($HostName,'Connecting mstsc')) { + if ($PSBoundParameters.ContainsKey('Port')) { + $target = "$($HostName):$($Port)" + } else { + $target = $HostName + } + Start-Process -FilePath "$($env:SystemRoot)\system32\mstsc.exe" -ArgumentList ("/v",$target) -NoNewWindow -Wait + } +} + +Export-ModuleMember -Function Get-EC2Password +Export-ModuleMember -Function Start-EC2RemoteDesktopViaSessionManager +Export-ModuleMember -Function Start-DirectEC2RemoteDesktop diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..be5c3d9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,85 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + +2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of this License; and +You must cause any modified files to carry prominent notices stating that You changed the files; and +You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and +If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. +You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + +5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + +6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/LICENSE-BouncyCastle b/LICENSE-BouncyCastle new file mode 100644 index 0000000..eac9753 --- /dev/null +++ b/LICENSE-BouncyCastle @@ -0,0 +1,10 @@ +The license in this file applies to the BouncyCastle.Crypto.dll file only. + + +Copyright (c) 2000 - 2017 The Legion of the Bouncy Castle Inc. (http://www.bouncycastle.org) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..c00c19f --- /dev/null +++ b/README.md @@ -0,0 +1,150 @@ +Convenient and secure access to Windows EC2 Instances with Remote Desktop (RDP) +=============================================================================== + +Getting remote access to Windows-based EC2 instances has always been trickier than the equivalent for Linux +instances. Whereas with a Linux instance it is a single command to get a fully-functional shell, to get access +to the desktop of a Windows instance you need to: + +1. Use the EC2 console to request the Administrator password +2. Start the Remote Desktop client and give it the EC2 instance address +3. Provide the Administrator credentials + +It's several more steps and each step involves several clicks or clipboard operations. + +This module aims to reduce this down to a single step, allowing a single command to start a Remote Desktop +session with no need for completing dialog boxes or manually supplying credentials. It works by automating all +of the above steps: + +1. The encrypted blob containing the Administrator password is fetched and then decrypted locally. +2. The Administrator credentials are pre-loaded into the Remote Desktop client's credential store, so there is + no need to manually enter them when making the connection. +3. The Remote Desktop client is started using the hostname for the EC2 instance. + +Furthermore, Systems Manager Session Manager's port forwarding functionality is fully integrated. If an EC2 +instance is configured for Session Manager, then this module can connect using a Session Manager forwarded +port, even if the instance is not reachable on a public IP address. + + +Prerequisites +------------- + +Firstly, you will need to install the AWS Tools for PowerShell. There are a few ways of installing +this, which are described in the AWS documentation page +[Installing the AWS Tools for PowerShell on Windows](https://docs.aws.amazon.com/powershell/latest/userguide/pstools-getting-set-up-windows.html). +We recommend installing the AWS Tools installer from the PowerShell Gallery and then installing the +EC2 and SimpleSystemsManagement module. Usually, this can be done with these commands: + +```powershell +Install-Module -Name AWS.Tools.Installer +Install-AWSToolsModule EC2,SimpleSystemsManagement +``` + +If you would also like to use the Session Manager integration (which is recommended, as you can +keep the RDP port off the public Internet), then you also need to +[Install the Session Manager plugin on Windows](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html#install-plugin-windows) +as described in the AWS documentation. + + +Installation +------------ + +The module must be installed somewhere in the `$env:PSModulePath`. Usually, the +`Documents\WindowsPowerShell\Modules` directory will do this job, but check the module path with +a command like this to make sure: + +```powershell +( Get-Item Env:\PSModulePath ).Value.Split(";") +``` + +Once you have identified a suitable modules directory, create a folder called `EC2Access`, and fill +it with the files from this repository. + +Then verify that the installation was successful with these commands: + +```powershell +Import-Module EC2Access +help Start-DirectEC2RemoteDesktop +``` + +If installation was successful, you will see the documentation for the Start-DirectEC2RemoteDesktop +command. + + +Usage +----- + +### About private keys + +To be able to decrypt the Administrator password, you will need the private key. Typically, there are two ways +to get a private key. + +1. Creating a keypair on the EC2 *Key pairs* page. EC2 will generate a private+public key pair and store the + public key. The private key is downloaded to your workstation as a file with a `.pem` extension - it is + then discarded by AWS so make sure that you keep the downloaded private key file. +2. Creating a keypair on your workstation. This is usually done using with the `ssh-keygen` tool that comes + with Git for Windows or the Windows native OpenSSH tools if you have installed them. By default this + creates the private key in `.ssh\.id_rsa` in your home directory, and corresponding public key with the + same name with a `.pub` extension. Then, using the *Import* button on the EC2 *Key pairs* page to import + the public key. + +By default, the functions in this module will assume that your private key is in the ".ssh\id_rsa" file in +your home directory, which will be the normal situation in method 2 above. If you have used method 1, or have +your key in any other location, simply pass a "-PrivateKeyFile" parameter to the functions with the path to +your private key file. + + +### Examples + +If your EC2 instance is configured for Systems Manager Session Manager, and if you have installed the AWS +Session Manager plugin tool, then you can start a Remote Desktop session with this command: + +```powershell +Start-EC2RemoteDesktopViaSessionManager i-12345678abcd +``` + +This assumes your instance is in the default region, as specified in environment variables or your AWS client +configuration files. You can specify the region explicitly: + +```powershell +Start-EC2RemoteDesktopViaSessionManager -InstanceId i-12345678abcd -Region eu-west-2 +``` + +You can specify the location of the private key file: + +```powershell +Start-EC2RemoteDesktopViaSessionManager -InstanceId i-12345678abcd ` + -PrivateKeyFile C:\Users\joe\Downloads\windows.pem +``` + +If your EC2 instance is reachable on its public IP address, then instead of +"Start-EC2RemoteDesktopViaSessionManager", you can invoke "Start-DirectEC2RemoteDesktop". This function takes +exactly the same parameters but uses the public IP address instead of Session Manager port forwarding. + +```powershell +Start-DirectEC2RemoteDesktop -InstanceId i-12345678abcd -Region eu-west-2 +``` + + +License +------- + +The original content in this project is provided under this license: + + Copyright 2021 Cloudsoft Corporation Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +See [LICENSE](LICENSE) for the full license text. + +This project incorporations portions of the Bouncy Castle project. Refer to +[LICENSE-BouncyCastle](LICENSE-BouncyCastle) for more information. diff --git a/en-US/about_EC2Access.help.txt b/en-US/about_EC2Access.help.txt new file mode 100644 index 0000000..c06f53d --- /dev/null +++ b/en-US/about_EC2Access.help.txt @@ -0,0 +1,68 @@ +TOPIC + about_EC2Access + +SHORT DESCRIPTION + EC2Access provides functions that can start a Remote Desktop session to a Windows instance in Amazon EC2. + +LONG DESCRIPTION + Getting remote access to Windows-based EC2 instances has always been trickier than the equivalent for Linux + instances. Whereas with a Linux instance it is a single command to get a fully-functional shell, to get access + to the desktop of a Windows instance you need to: + + 1. Use the EC2 console to request the Administrator password + 2. Start the Remote Desktop client and give it the EC2 instance address + 3. Provide the Administrator credentials + + It's several more steps and each step involves several clicks or clipboard operations. + + This module aims to reduce this down to a single step, allowing a single command to start a Remote Desktop + session with no need for completing dialog boxes or manually supplying credentials. It works by automating all + of the above steps: + + 1. The encrypted blob containing the Administrator password is fetched and then decrypted locally. + 2. The Administrator credentials are pre-loaded into the Remote Desktop client's credential store, so there is + no need to manually enter them when making the connection. + 3. The Remote Desktop client is started using the hostname for the EC2 instance. + + Furthermore, Systems Manager Session Manager's port forwarding functionality is fully integrated. If an EC2 + instance is configured for Session Manager, then this module can connect using a Session Manager forwarded + port, even if the instance is not reachable on a public IP address. + + To be able to decrypt the Administrator password, you will need the private key. Typically, there are two ways + to get a private key. + + 1. Creating a keypair on the EC2 "Key pairs" page. EC2 will generate a private+public key pair and store the + public key. The private key is downloaded to your workstation as a file with a ".pem" extension - it is + then discarded by AWS so make sure that you keep the downloaded private key file. + 2. Creating a keypair on your workstation. This is usually done using with the "ssh-keygen" tool that comes + with Git for Windows or the Windows native OpenSSH tools if you have installed them. By default this + creates the private key in ".ssh\.id_rsa" in your home directory, and corresponding public key with the + same name with a ".pub" extension. Then, using the "Import" button on the EC2 "Key pairs" page to import + the public key. + + By default, the functions in this module will assume that your private key is in the ".ssh\id_rsa" file in + your home directory, which will be the normal situation in method 2 above. If you have used method 1, or have + your key in any other location, simply pass a "-PrivateKeyFile" parameter to the functions with the path to + your private key file. + +EXAMPLES + If your EC2 instance is configured for Systems Manager Session Manager, and if you have installed the AWS + Session Manager plugin tool, then you can start a Remote Desktop session with this command: + + C:\> Start-EC2RemoteDesktopViaSessionManager i-12345678abcd + + This assumes your instance is in the default region, as specified in environment variables or your AWS client + configuration files. You can specify the region explicitly: + + C:\> Start-EC2RemoteDesktopViaSessionManager -InstanceId i-12345678abcd -Region eu-west-2 + + You can specify the location of the private key file: + + C:\> Start-EC2RemoteDesktopViaSessionManager -InstanceId i-12345678abcd ` + -PrivateKeyFile C:\Users\joe\Downloads\windows.pem + + If your EC2 instance is reachable on its public IP address, then instead of + "Start-EC2RemoteDesktopViaSessionManager", you can invoke "Start-DirectEC2RemoteDesktop". This function takes + exactly the same parameters but uses the public IP address instead of Session Manager port forwarding. + + C:\> Start-DirectEC2RemoteDesktop -InstanceId i-12345678abcd -Region eu-west-2