Некоторые картинки не загружаются из РФ и РК, используйте VPN.

суббота, 16 сентября 2023 г.

Заметка Powershell и скрипт добавления IKEv2

Простой скрипт добавления интерфейса IKEv2. Данное решение выполняет следующие задачи:

  • Экспортирует сертификат пользователя и CA
  • Добавляет интерфейс IKEv2
  • Создает IPsec политику
  • Связывает интерфейс с CA
  • Отключает маршрут по умолчанию
  • Отключает IPv6
  • Отключает автометрику и выставляет метрику равной 1. Данная метрика не влияет на трафик, нужна только для смены порядка выбора DNS сервера (подробнее)
Блок кодирования добавлен только для моих задач, без специального параметра не вызывается.
# powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0\add_ikev2.ps1"
# [Convert]::ToBase64String( [System.Text.Encoding]::Unicode.GetBytes( $(Get-Content "add_ikev2.ps1")))
# _________________________________________

param ($CreateBatch=$False)

$CurrentDir = Split-Path -Parent $MyInvocation.MyCommand.Path
Set-Location -Path $CurrentDir

if ($CreateBatch -eq $true) {
	$ps_file_name=(Get-Item $PSCommandPath).Name
	$cmd_file_name=((Get-Item $PSCommandPath).Basename+".bat")
	$encoded_script=[Convert]::ToBase64String( [System.Text.Encoding]::Unicode.GetBytes( $(Get-Content $ps_file_name -Raw)))
	$content=("@echo off`r`n"+
			"cd %~dp0`r`n"+
			"powershell -Command [System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String('$encoded_script'))>$ps_file_name`r`n"+
			"powershell -NoProfile -ExecutionPolicy Bypass -File $ps_file_name`r`n"+
			"pause")
			
	out-file -FilePath $CurrentDir\$cmd_file_name -InputObject $content -Encoding ASCII
	exit
}

$currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())

if ( -Not $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
	Write-Host "Error! Run this script as administrtor" -ForegroundColor Red
	exit
}

#############################
#
#############################
function TestPath ($Path) {
	if (-not(Test-Path -Path $Path -PathType Leaf)) {
	    Write-Host "Error! $Path not found" -ForegroundColor Red
	    exit
	}
	
}
function CheckFailExit ([bool]$State,[string]$TextMessage,[string]$CommandForError){ 
	if ($State){
		Write-Host "Error! $TextMessage with error!"  -ForegroundColor Red
		if ($CommandForError.Length -gt 0 ){
			Invoke-Expression -Command $CommandForError
		}
		exit
	} else {
		Write-Host "$TextMessage succesfully"
	}
}
#############################
#
#############################


$IniFile="settings.ini"
TestPath -Path $IniFile
$arrSettings=$(Get-Content $IniFile | ConvertFrom-StringData)


$FileCA="$CurrentDir\"+$arrSettings.FileCA
$FileP12="$CurrentDir\"+$arrSettings.FileP12
$ConnectionName=$arrSettings.ConnectionName
$ServerAddress=$arrSettings.ServerAddress
$ErrorActionPreference = "SilentlyContinue"; #This will hide errors


TestPath -Path $FileCA
TestPath -Path $FileP12

$PSK = Get-Credential -UserName 'Enter password below' -Message 'Enter password below'

Import-PfxCertificate -FilePath $FileP12  -CertStoreLocation 'Cert:\LocalMachine\My' -Password $PSK.Password | out-null
CheckFailExit -State $(!$?) -TextMessage "Import user`s cert finish"


Import-Certificate -FilePath $FileCA -CertStoreLocation cert:\LocalMachine\Root | out-null
CheckFailExit -State $(!$?) -TextMessage "Import CA cert finish"

Add-VpnConnection -ConnectionName "$ConnectionName" -ServerAddress "$ServerAddress" -TunnelType 'Ikev2' -AuthenticationMethod MachineCertificate -EncryptionLevel "Maximum" | out-null
CheckFailExit -State $(!$?) -TextMessage "Add VPN connection finish"

Set-VpnConnectionIPsecConfiguration -ConnectionName "$ConnectionName" -AuthenticationTransformConstants SHA196 -CipherTransformConstants AES256 -EncryptionMethod AES256 -IntegrityCheckMethod SHA1 -PfsGroup None -DHGroup Group14 -PassThru -Force | out-null
CheckFailExit -State $(!$?) -TextMessage "Set IPSec param for VPN connection finish" -CommandForError "Remove-VpnConnection -ConnectionName ""$ConnectionName"""

Set-VpnConnection -ConnectionName "$ConnectionName" -MachineCertificateIssuerFilter "$FileCA" -SplitTunneling $True | out-null
CheckFailExit -State $(!$?) -TextMessage "Set CA for VPN connection finish"

# BLOCK to change extented interface settings BEGIN
# Function converts ini files to HashTable (pbk recursion)
function Get-IniContent ($filePath)
{
	$ini = [System.Collections.Generic.Dictionary[string,psobject]]::new([StringComparer]::OrdinalIgnoreCase)
	switch -regex -file $FilePath
	{
    	"^\[(.+)\]" # Section
    	{
        	$section = $matches[1]
        	$ini[$section] = [System.Collections.Generic.Dictionary[string,psobject]]::new([StringComparer]::Ordinal)
        	$CommentCount = 0
    	}
    	"^(;.*)$" # Comment
    	{
        	$value = $matches[1]
        	$CommentCount = $CommentCount + 1
        	$name = “Comment” + $CommentCount
        	$ini[$section][$name] = $value
    	}
    	"(.+?)\s*=(.*)" # Key
    	{
        	$name,$value = $matches[1..2]
        	$ini[$section][$name] = $value
    	}
	}
	return $ini
}
# The function converts the HashTable obtained by the Get-IniContent function into an ini file (pbk)
function IniContentToString ($iniContent){
    $iniString=""
    foreach ($section in $iniContent.Keys){
        $iniString=$iniString+"`r`n"+"["+$section+"]"
        foreach ($Name in $iniContent[$section].Keys){
            $StrIni=$($Name + "=" +$iniContent[$section][$Name])
            $iniString=$iniString+"`r`n"+$StrIni
        }
    }
    return $iniString
}

$rasphone="$env:USERPROFILE\AppData\Roaming\Microsoft\Network\Connections\Pbk\rasphone.pbk"
$iniContent = Get-IniContent $rasphone
$iniContent["$ConnectionName"].IpInterfaceMetric=1
$iniContent["$ConnectionName"].ExcludedProtocols=8
$iniContent["$ConnectionName"].PreferredHwFlow=1
$iniContent["$ConnectionName"].PreferredProtocol=1
$iniContent["$ConnectionName"].PreferredCompression=1
$iniContent["$ConnectionName"].PreferredSpeaker=1
$iniContent["$ConnectionName"].AutoTiggerCapable=0
$iniContent["$ConnectionName"].Ipv6PrioritizeRemote=1

$iniString=IniContentToString $iniContent

out-file -FilePath $rasphone -InputObject $iniString -Encoding ASCII
CheckFailExit -State $(!$?) -TextMessage "Change interface settings"

# BLOCK change extented settings interface END
Write-Host "Congragulation! VPN interface create successfully!" -ForegroundColor Green
$ErrorActionPreference = "Continue"; #Turning errors back on

Настройки берутся из файла settings.ini, который должен лежать в этом же каталоге. Файл имеет следующий синтаксис:

FileP12=crt\\user.p12 
FileCA=crt\\ca.crt 
ConnectionName=Roga_IKEv2 
ServerAddress=vpn.roga.com

Косая черта обязательно дожна экранироваться.

Стандартный функционал cmd/PoSh не позволяет менять два последних пункта первого списка. Данный результат достигается при помощи редактирования файла rasphone.pbk. На просторах интернета предлагается регуляркой заменить все вхождения на нужные, но нас такой вариант не устраивает, так как у конечного пользователя может быть куча других VPN интерфейсов. Я обратил внимание на синтаксис файла, и технически это тот же Ini файл, к которому я применил доработанный скрипт конвертации Ini в HashTable, где секция (она же интерфейс) выступает в качестве первого уровня. Вот тут и выяснилось, что по умолчанию словарь РегистроНезависисмый

# Регистрозависимый словарь
[System.Collections.Generic.Dictionary[string,psobject]]::new([StringComparer]::Ordinal)
# РегистроНЕзависимый словарь
[System.Collections.Generic.Dictionary[string,psobject]]::new([StringComparer]::OrdinalIgnoreCase)
# или
@{}

Еще интересный момент - как это ни странно ASCII сохраняет в UTF-8 w/o BOM

out-file -FilePath $Path -InputObject $Content -Encoding ASCII

End of string в стиле Windows при сборе контента можно достичь указанием перевода строки и каретки "`r`n"

Т.о. мы можем обратиться к параметрам именно нужного нам интерфейса. После корректировки параметров, выявленных эмпирическим путем, таблица моей функцией разматывается обратно в Ini файл и сохраняется на прежнее место. 

Комментариев нет:

Отправить комментарий