mirror of
https://github.com/trailofbits/algo.git
synced 2025-06-06 15:13:56 +02:00
Embed certs into Windows deployment scripts (#840)
- Obviate need to copy separate script and certificate files - Allow execution from any directory, not just the script's parent directory (no assumption of any particular working directory) - Fix docs that neglected to mention copying cacert.pem - Fix docs that incorrectly referred to the user cert store As part of this work, rewrite the windows_client.ps1.j2 deployment script template - Add comment-based help - Require admin privileges - Use a Param() block - Use parameter sets with -Add and -Remove switches - Add the -GetInstalledCerts switch, to list any Algo certificates installed the machine's cert store - Add the -SaveCerts switch, to save the embedded certificates to files - Put Jinja2 variables inside Powershell variables, - Use native Powershell cmdlets rather than shell out to certutil.exe - Add a playbook to regenerate the windows_USER.ps1 scripts
This commit is contained in:
parent
a8784bc0f4
commit
e944ee993a
5 changed files with 320 additions and 30 deletions
|
@ -101,9 +101,9 @@ No version of Android supports IKEv2. Install the [strongSwan VPN Client for And
|
|||
|
||||
### Windows 10
|
||||
|
||||
Copy your PowerShell script `windows_{username}.ps1` and p12 certificate `{username}.p12` to the Windows client and run the following command as Administrator to configure the VPN connection.
|
||||
Copy your PowerShell script `windows_{username}.ps1` to the Windows client and run the following command as Administrator to configure the VPN connection.
|
||||
```
|
||||
powershell -ExecutionPolicy ByPass -File windows_{username}.ps1 Add
|
||||
powershell -ExecutionPolicy ByPass -File windows_{username}.ps1 -Add
|
||||
```
|
||||
|
||||
For a manual installation, see the [Windows setup instructions](/docs/client-windows.md).
|
||||
|
|
|
@ -1,27 +1,69 @@
|
|||
# Windows client manual setup
|
||||
|
||||
Windows clients have a more complicated setup than most others. Follow the steps below to set one up:
|
||||
## Automatic installtion
|
||||
|
||||
1. Copy the CA certificate (`cacert.pem`), user certificate (`$user.p12`), and the user PowerShell script (`windows_$user.ps1`) to the client computer.
|
||||
2. Import the CA certificate to the local machine Trusted Root certificate store.
|
||||
3. Open PowerShell as Administrator. Navigate to your copied files.
|
||||
4. If you haven't already, you will need to change the Execution Policy to allow unsigned scripts to run.
|
||||
To install automatically, use the generated user Powershell script.
|
||||
|
||||
1. Copy the user PowerShell script (`windows_USER.ps1`) to the client computer.
|
||||
2. Open Powershell as Administrator.
|
||||
3. Run the following command:
|
||||
```powershell
|
||||
powershell -ExecutionPolicy ByPass -File C:\path\to\windows_USER.ps1 -Add
|
||||
```
|
||||
4. The command has help information available. To view its full help, run this from Powershell:
|
||||
```powershell
|
||||
Get-Help -Name .\windows_USER.ps1 -Full | more
|
||||
```
|
||||
|
||||
## Manual installation
|
||||
|
||||
1. Copy the CA certificate (`cacert.pem`) and user certificate (`USER.p12`) to the client computer
|
||||
2. Open PowerShell as Administrator. Navigate to your copied files.
|
||||
3. If you haven't already, you will need to change the Execution Policy to allow unsigned scripts to run.
|
||||
|
||||
```powershell
|
||||
Set-ExecutionPolicy Unrestricted -Scope CurrentUser
|
||||
```
|
||||
|
||||
5. In the same PowerShell window, run the included PowerShell script to import the user certificate, set up a VPN connection, and activate stronger ciphers on it.
|
||||
6. After you execute the user script, set the Execution Policy back before you close the PowerShell window.
|
||||
4. In the same window, run the necessary commands to install the certificates and create the VPN configuration. Note the lines at the top defining the VPN address, USER.p12 file location, and CA certificate location - change those lines to the IP address of your Algo server and the location you saved those two files. Also note that it will prompt for the "User p12 password", which is printed at the end of a successful Algo deployment.
|
||||
|
||||
```powershell
|
||||
$VpnServerAddress = "1.2.3.4"
|
||||
$UserP12Path = "$Home\Downloads\USER.p12"
|
||||
$CaCertPath = "$Home\Downloads\cacert.pem"
|
||||
$VpnName = "Algo VPN $VpnServerAddress IKEv2"
|
||||
$p12Pass = Read-Host -AsSecureString -Prompt "User p12 password"
|
||||
|
||||
Import-PfxCertificate -FilePath $UserP12Path -CertStoreLocation Cert:\LocalMachine\My -Password $p12Pass
|
||||
Import-Certificate -FilePath $CaCertPath -CertStoreLocation Cert:\LocalMachine\Root
|
||||
|
||||
$addVpnParams = @{
|
||||
Name = $VpnName
|
||||
ServerAddress = $VpnServerAddress
|
||||
TunnelType = "IKEv2"
|
||||
AuthenticationMethod = "MachineCertificate"
|
||||
EncryptionLevel = "Required"
|
||||
}
|
||||
Add-VpnConnection @addVpnParams
|
||||
|
||||
$setVpnParams = @{
|
||||
ConnectionName = $VpnName
|
||||
AuthenticationTransformConstants = "GCMAES128"
|
||||
CipherTransformConstants = "GCMAES128"
|
||||
EncryptionMethod = "AES128"
|
||||
IntegrityCheckMethod = "SHA384"
|
||||
DHGroup = "ECP256"
|
||||
PfsGroup = "ECP256"
|
||||
Force = $true
|
||||
}
|
||||
Set-VpnConnectionIPsecConfiguration @setVpnParams
|
||||
|
||||
```
|
||||
|
||||
5. After you execute the user script, set the Execution Policy back before you close the PowerShell window.
|
||||
|
||||
```powershell
|
||||
Set-ExecutionPolicy Restricted -Scope CurrentUser
|
||||
```
|
||||
|
||||
Your VPN is now installed and ready to use.
|
||||
|
||||
If you want to perform these steps by hand, you will need to import the user certificate to the Personal certificate store, add an IKEv2 connection in the network settings, then activate stronger ciphers on it via the following PowerShell script:
|
||||
|
||||
```powershell
|
||||
Set-VpnConnectionIPsecConfiguration -ConnectionName "Algo" -AuthenticationTransformConstants GCMAES128 -CipherTransformConstants GCMAES128 -EncryptionMethod AES128 -IntegrityCheckMethod SHA384 -DHGroup ECP256 -PfsGroup ECP256
|
||||
```
|
||||
|
|
67
playbooks/win_script_rebuild.yml
Normal file
67
playbooks/win_script_rebuild.yml
Normal file
|
@ -0,0 +1,67 @@
|
|||
---
|
||||
|
||||
# This playbook is designed to help when modifying the Windows script template
|
||||
# in roles/vpn/templates/client_windows.ps1.j2
|
||||
# It rebuilds the client_USER.ps1 scripts for each user defined in config.cfg,
|
||||
# without redeploying users or opening an SSH connection to the Algo server at
|
||||
# all.
|
||||
#
|
||||
# This playbook is _not_ part of a normal Algo deployment.
|
||||
# It is only intended to speed up development of the client_USER.ps1 Windows
|
||||
# Algo install scripts.
|
||||
#
|
||||
# REQUIREMENTS
|
||||
# - Algo must have been deployed once
|
||||
# - Windows users must have been enabled at deployment time
|
||||
# - All users defined in config.cfg must not have changed
|
||||
# - Only one Algo deployment exists in the configs/ directory
|
||||
# - There must be exactly one subfolder in the configs/ directory:
|
||||
# the folder named after the IP of the algo server
|
||||
|
||||
- hosts: localhost
|
||||
gather_facts: False
|
||||
tags: always
|
||||
vars_files:
|
||||
- ../config.cfg
|
||||
|
||||
tasks:
|
||||
|
||||
- name: Get config subdir
|
||||
shell: find ../configs/* -maxdepth 0 -type d | sed 's/.*\///'
|
||||
register: config_subdir_result
|
||||
- fail:
|
||||
msg:
|
||||
- "Found wrong number of config subdirs... stdout:"
|
||||
- "{{ config_subdir_result.split('\n') }}"
|
||||
when: config_subdir_result.stdout.split('\n') | length != 1
|
||||
- set_fact:
|
||||
IP_subject_alt_name: "{{ config_subdir_result.stdout }}"
|
||||
- debug:
|
||||
var: IP_subject_alt_name
|
||||
|
||||
- name: Register p12 PayloadContent
|
||||
shell: cat private/{{ item }}.p12 | base64
|
||||
register: PayloadContent
|
||||
args:
|
||||
chdir: "../configs/{{ IP_subject_alt_name }}/pki/"
|
||||
with_items: "{{ users }}"
|
||||
|
||||
- name: Set facts for mobileconfigs
|
||||
set_fact:
|
||||
proxy_enabled: false
|
||||
PayloadContentCA: "{{ lookup('file' , '../configs/{{ IP_subject_alt_name }}/pki/cacert.pem')|b64encode }}"
|
||||
|
||||
- name: Build the windows client powershell script
|
||||
template:
|
||||
src: ../roles/vpn/templates/client_windows.ps1.j2
|
||||
dest: ../configs/{{ IP_subject_alt_name }}/windows_{{ item.0 }}.ps1
|
||||
mode: 0600
|
||||
with_together:
|
||||
- "{{ users }}"
|
||||
- "{{ PayloadContent.results }}"
|
||||
|
||||
- name: List windows client powershell scripts
|
||||
debug:
|
||||
msg: "configs/{{ IP_subject_alt_name }}/windows_{{ item }}.ps1"
|
||||
with_items:
|
||||
- "{{ users }}"
|
|
@ -70,10 +70,12 @@
|
|||
- name: Build the windows client powershell script
|
||||
template:
|
||||
src: client_windows.ps1.j2
|
||||
dest: configs/{{ IP_subject_alt_name }}/windows_{{ item }}.ps1
|
||||
dest: configs/{{ IP_subject_alt_name }}/windows_{{ item.0 }}.ps1
|
||||
mode: 0600
|
||||
when: Win10_Enabled is defined and Win10_Enabled == "Y" or supports_windows.stat.exists == true
|
||||
with_items: "{{ users }}"
|
||||
with_together:
|
||||
- "{{ users }}"
|
||||
- "{{ PayloadContent.results }}"
|
||||
|
||||
- name: Restrict permissions for the local private directories
|
||||
file:
|
||||
|
|
|
@ -1,19 +1,198 @@
|
|||
#Requires -RunAsAdministrator
|
||||
|
||||
function AddAlgoVPN {
|
||||
certutil -f -importpfx .\{{ item }}.p12
|
||||
certutil -addstore root .\cacert.pem
|
||||
Add-VpnConnection -name "Algo VPN {{ IP_subject_alt_name }} IKEv2" -ServerAddress "{{ IP_subject_alt_name }}" -TunnelType IKEv2 -AuthenticationMethod MachineCertificate -EncryptionLevel Required
|
||||
Set-VpnConnectionIPsecConfiguration -ConnectionName "Algo VPN {{ IP_subject_alt_name }} IKEv2" -AuthenticationTransformConstants GCMAES128 -CipherTransformConstants GCMAES128 -EncryptionMethod AES128 -IntegrityCheckMethod SHA384 -DHGroup ECP256 -PfsGroup ECP256 -Force
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Add or remove the Algo VPN
|
||||
|
||||
.DESCRIPTION
|
||||
Add or remove the Algo VPN
|
||||
See the examples for more information
|
||||
|
||||
.PARAMETER Add
|
||||
Add the VPN to the local system
|
||||
|
||||
.PARAMETER Remove
|
||||
Remove the VPN from the local system
|
||||
|
||||
.PARAMETER GetInstalledCerts
|
||||
Retrieve Algo certs, if any, from the system certificate store
|
||||
|
||||
.PARAMETER SaveCerts
|
||||
Save the Algo certs embedded in this file
|
||||
|
||||
.PARAMETER OutputDirectory
|
||||
When saving the Algo certs, save to this directory
|
||||
|
||||
.PARAMETER Pkcs12DecryptionPassword
|
||||
The decryption password for the user's PKCS12 certificate, sometimes called the "p12 password".
|
||||
Note that this must be passed in as a SecureString, not a regular string.
|
||||
You can create a secure string with the `Read-Host -AsSecureString` cmdlet.
|
||||
See the examples for more information.
|
||||
|
||||
.EXAMPLE
|
||||
client_USER.ps1 -Add
|
||||
|
||||
Adds the Algo VPN
|
||||
|
||||
.EXAMPLE
|
||||
$p12pass = Read-Host -AsSecureString; client_USER.ps1 -Add -Pkcs12DecryptionPassword $p12pass
|
||||
|
||||
Create a variable containing the PKCS12 decryption password, then use it when adding the VPN.
|
||||
This can be especially useful when troubleshooting, because you can use the same variable with
|
||||
multiple calls to client_USER.ps1, rather than having to type the PKCS12 password each time.
|
||||
|
||||
.EXAMPLE
|
||||
client_USER.ps1 -Remove
|
||||
|
||||
Removes the Algo VPN if installed.
|
||||
|
||||
.EXAMPLE
|
||||
client_USER.ps1 -GetIntalledCerts
|
||||
|
||||
Show the Algo VPN's installed certificates, if any.
|
||||
|
||||
.EXAMPLE
|
||||
client_USER.ps1 -SaveCerts -OutputDirectory $Home\Downloads
|
||||
|
||||
Save the embedded CA cert and encrypted user PKCS12 file.
|
||||
#>
|
||||
[CmdletBinding(DefaultParameterSetName="Add")] Param(
|
||||
[Parameter(ParameterSetName="Add")]
|
||||
[Switch] $Add,
|
||||
|
||||
[Parameter(ParameterSetName="Add")]
|
||||
[SecureString] $Pkcs12DecryptionPassword,
|
||||
|
||||
[Parameter(Mandatory, ParameterSetName="Remove")]
|
||||
[Switch] $Remove,
|
||||
|
||||
[Parameter(Mandatory, ParameterSetName="GetInstalledCerts")]
|
||||
[Switch] $GetInstalledCerts,
|
||||
|
||||
[Parameter(Mandatory, ParameterSetName="SaveCerts")]
|
||||
[Switch] $SaveCerts,
|
||||
|
||||
[Parameter(ParameterSetName="SaveCerts")]
|
||||
[string] $OutputDirectory = "$PWD"
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$VpnServerAddress = "{{ IP_subject_alt_name }}"
|
||||
$VpnName = "Algo VPN {{ IP_subject_alt_name }} IKEv2"
|
||||
$VpnUser = "{{ item.0 }}"
|
||||
$CaCertificateBase64 = "{{ PayloadContentCA }}"
|
||||
$UserPkcs12Base64 = "{{ item.1.stdout }}"
|
||||
|
||||
if ($PsCmdlet.ParameterSetName -eq "Add" -and -not $Pkcs12DecryptionPassword) {
|
||||
$Pkcs12DecryptionPassword = Read-Host -AsSecureString -Prompt "Pkcs12DecryptionPassword"
|
||||
}
|
||||
|
||||
function RemoveAlgoVPN {
|
||||
Get-ChildItem cert:LocalMachine/Root | Where-Object { $_.Subject -match '^CN={{ IP_subject_alt_name }}$' -and $_.Issuer -match '^CN={{ IP_subject_alt_name }}$' } | Remove-Item
|
||||
Get-ChildItem cert:LocalMachine/My | Where-Object { $_.Subject -match '^CN={{ item }}$' -and $_.Issuer -match '^CN={{ IP_subject_alt_name }}$' } | Remove-Item
|
||||
Remove-VpnConnection -name "Algo VPN {{ IP_subject_alt_name }} IKEv2" -Force
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Create a temporary directory
|
||||
#>
|
||||
function New-TemporaryDirectory {
|
||||
[CmdletBinding()] Param()
|
||||
do {
|
||||
$guid = New-Guid | Select-Object -ExpandProperty Guid
|
||||
$newTempDirPath = Join-Path -Path $env:TEMP -ChildPath $guid
|
||||
} while (Test-Path -Path $newTempDirPath)
|
||||
New-Item -ItemType Directory -Path $newTempDirPath
|
||||
}
|
||||
|
||||
switch ($args[0]) {
|
||||
"Add" { AddAlgoVPN }
|
||||
"Remove" { RemoveAlgoVPN }
|
||||
default { Write-Host Usage: $MyInvocation.MyCommand.Name "(Add|Remove)" }
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Retrieve any installed Algo VPN certificates
|
||||
#>
|
||||
function Get-InstalledAlgoVpnCertificates {
|
||||
[CmdletBinding()] Param()
|
||||
Get-ChildItem -LiteralPath Cert:\LocalMachine\Root |
|
||||
Where-Object {
|
||||
$_.Subject -match "^CN=${VpnServerAddress}$" -and $_.Issuer -match "^CN=${VpnServerAddress}$"
|
||||
}
|
||||
Get-ChildItem -LiteralPath Cert:\LocalMachine\My |
|
||||
Where-Object {
|
||||
$_.Subject -match "^CN=${VpnUser}$" -and $_.Issuer -match "^CN=${VpnServerAddress}$"
|
||||
}
|
||||
}
|
||||
|
||||
function Save-AlgoVpnCertificates {
|
||||
[CmdletBinding()] Param(
|
||||
[String] $OutputDirectory = $PWD
|
||||
)
|
||||
$caCertPath = Join-Path -Path $OutputDirectory -ChildPath "cacert.pem"
|
||||
$userP12Path = Join-Path -Path $OutputDirectory -ChildPath "$VpnUser.p12"
|
||||
# NOTE: We cannot use ConvertFrom-Base64 here because it is not designed for binary data
|
||||
[IO.File]::WriteAllBytes(
|
||||
$caCertPath,
|
||||
[Convert]::FromBase64String($CaCertificateBase64))
|
||||
[IO.File]::WriteAllBytes(
|
||||
$userP12Path,
|
||||
[Convert]::FromBase64String($UserPkcs12Base64))
|
||||
return New-Object -TypeName PSObject -Property @{
|
||||
CaPem = $caCertPath
|
||||
UserPkcs12 = $userP12Path
|
||||
}
|
||||
}
|
||||
|
||||
function Add-AlgoVPN {
|
||||
[Cmdletbinding()] Param()
|
||||
|
||||
$workDir = New-TemporaryDirectory
|
||||
|
||||
try {
|
||||
$certs = Save-AlgoVpnCertificates -OutputDirectory $workDir
|
||||
$importPfxCertParams = @{
|
||||
Password = $Pkcs12DecryptionPassword
|
||||
FilePath = $certs.UserPkcs12
|
||||
CertStoreLocation = "Cert:\LocalMachine\My"
|
||||
}
|
||||
Import-PfxCertificate @importPfxCertParams
|
||||
$importCertParams = @{
|
||||
FilePath = $certs.CaPem
|
||||
CertStoreLocation = "Cert:\LocalMachine\Root"
|
||||
}
|
||||
Import-Certificate @importCertParams
|
||||
} finally {
|
||||
Remove-Item -Recurse -Force -LiteralPath $workDir
|
||||
}
|
||||
|
||||
$addVpnParams = @{
|
||||
Name = $VpnName
|
||||
ServerAddress = $VpnServerAddress
|
||||
TunnelType = "IKEv2"
|
||||
AuthenticationMethod = "MachineCertificate"
|
||||
EncryptionLevel = "Required"
|
||||
}
|
||||
Add-VpnConnection @addVpnParams
|
||||
|
||||
$setVpnParams = @{
|
||||
ConnectionName = $VpnName
|
||||
AuthenticationTransformConstants = "GCMAES128"
|
||||
CipherTransformConstants = "GCMAES128"
|
||||
EncryptionMethod = "AES128"
|
||||
IntegrityCheckMethod = "SHA384"
|
||||
DHGroup = "ECP256"
|
||||
PfsGroup = "ECP256"
|
||||
Force = $true
|
||||
}
|
||||
Set-VpnConnectionIPsecConfiguration @setVpnParams
|
||||
}
|
||||
|
||||
function Remove-AlgoVPN {
|
||||
[CmdletBinding()] Param()
|
||||
Get-InstalledAlgoVpnCertificates | Remove-Item -Force
|
||||
Remove-VpnConnection -Name $VpnName -Force
|
||||
}
|
||||
|
||||
switch ($PsCmdlet.ParameterSetName) {
|
||||
"Add" { Add-AlgoVPN }
|
||||
"Remove" { Remove-AlgoVPN }
|
||||
"GetInstalledCerts" { Get-InstalledAlgoVpnCertificates }
|
||||
"SaveCerts" {
|
||||
$certs = Save-AlgoVpnCertificates -OutputDirectory $OutputDirectory
|
||||
Get-Item -LiteralPath $certs.UserPkcs12, $certs.CaPem
|
||||
}
|
||||
default { throw "Unknown parameter set: '$($PsCmdlet.ParameterSetName)'" }
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue