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

воскресенье, 25 декабря 2022 г.

Mikrotik script подготовка конфигурационного файла OVPN

У pfSense есть забавная штука - экспорт конфигурации OpenVPN, такой штуки нет на Mikrotik. Я бы мог использовать pfSense или openSense, но они мне не нравятся, а в некоторых случаях сложны для настройки. Так как в одном из проектов мне волей-неволей придется создавать новых пользователей openVPN, я решил максимально автоматизировать процесс экспорта конфигурации и описал скрипт. Скрипт:

  1. запрашивает необходимые данные
  2. создает пользователя
  3. создает и подписывает сертификат
  4. создает структуру папок
  5. создает все необходимые файлы в этой структуре
  6. экспортирует сертификат
  7. формирует и выгружает файл конфигурации
Остается только нажать на каталог правой клавишей - сохранить, скопировать конфиг и структуру каталогов в каталог openVPN (в зависимости от типа либо config, либо config-auto (требуется установка службы))
Да, сам сервер уже должен быть настроен! По-умолчанию в качестве адреса сервера openVPN берется Identity устройства. 

Запись из июля 2023 года, скрипт формирует структуру каталогов из-за ограничения переменной в 4096 байта, но я случайно нашел обходной вариант, который позволяет слить файлы в один большой - самый последний пример.
Скрипт запускается из консоли, сначала идет запрос данных, потом запрос-подтверждение и выполнение
# Pref global env
:global crtCnfpref "crtCnf"

:global crtCnfUserName
:global crtCnfPassword test
:global crtCnfAskPassword
:global crtCnfdaysValid 365
:global crtCnfFullPath ""
#config or config-auto
:global crtCnfNameConfig "config"
:global crtCnfContinue false
:global crtCnfovpnProfile "OVPN"
:global crtCnfServerAddress ([/system identity print as-value]->"name")
:global crtCnfcaName ca

:local NameOrg "DistControl"
:local RouteAddressAdd 10.20.1.254

#\\\\\\\\\\\\\\\\\\\
#	BLOCK FUNC BEGIN
#

# Block create folder BEGIN
:global crtCnfCreateFolder do={
	:global crtCnfFullPath
	if ($crtCnfFullPath="") do={
		:set crtCnfFullPath $NewPath
	} else={
		:set crtCnfFullPath ($crtCnfFullPath."/".$NewPath)
	}
	
	if ([:len [/file find where name=$crtCnfFullPath]]=0) do={
		do {
			/ip smb shares add name=crtCnfCreateFolder directory=$crtCnfFullPath
		} on-error={
			:put "Create folder failed"
		}
		/ip smb shares remove [/ip smb shares find where name=crtCnfCreateFolder]
	} else={
		:put "Path \"$crtCnfFullPath\" is exist"
	}
}

:global crtCnfCreateTree do={
	:global crtCnfCreateTree
	:global crtCnfCreateFolder
	if ([:len $1]>0) do={
		$crtCnfCreateFolder NewPath=$1
		$crtCnfCreateTree $2 $3 $4 $5 $6 $7 $8 $9
	}
}
# Block create folder END

# Block create file BEGIN
# Example: $CreateFile FileName=File.ovpn Contents="text"
:local CreateFile do={
	[/tool fetch dst-path=$FileName url="http://127.0.0.1/"  as-value]
	if ([:len [/file find where name=$FileName]]>0) do={
		/file set $FileName contents=$Contents
		:put ("Success to create file \"$FileName\"")
	} else={
		:put ("Failed to create file \"$FileName\"")
	}
}
# Block create file END

# Example:
# :global UserName ""
# $PromtInput QstUser="\r\nInsert new username" VarName="UserName" DefaultValue=365 LenValue=8
:local PromtInput do={
	:global crtCnfEndProgramm
	:local readinput do={:return}
	if ($LenValue>0) do={
		:set $QstUser ("$QstUser. Minimum length $LenValue simbols")
	}
	if ([:len $DefaultValue]>0) do={
		:set $QstUser ("$QstUser. \r\n(default - $DefaultValue)")
	}
	:put ("$QstUser:")
	:local UserPromt [$readinput]
	if ([:len $UserPromt]>0) do={
		[:parse "global $VarName;:set $VarName $UserPromt"]
		if (($LenValue>0) and [:len $UserPromt]<$LenValue) do={
			$crtCnfEndProgramm MessageText=("You entered only $[:len $UserPromt] symbols instead of $LenValue minimum required. Stop program")
		}
	} else={
		if  ([:len $DefaultValue]>0) do={
			[:parse "global $VarName;:set $VarName $DefaultValue"]
		} else={
			$crtCnfEndProgramm MessageText=("You didn't enter anything. Stop program")
		}
	}
}

# Block remove all global env BEGIN
:global  crtCnfRemoveENV do={
	:global crtCnfpref
	:foreach var in=[/system script environment print as-value] do={
		:local prefVar [:pick ($var->"name") 0 [:len $crtCnfpref]]; 
		:if ($prefVar=$crtCnfpref) do={
			/system script environment remove ($var->".id")
		}
	}
}
# Block remove all global env END

# Block call extensions BEGIN
# Example
# $crtCnfEndProgramm MessageText=("Create \"$crtCnfUserName\" failed. Stop program")
:global crtCnfEndProgramm do={
	:global  crtCnfRemoveENV
	$crtCnfRemoveENV
	:error message=$MessageText
}

# Block call extensions END
#
#	BLOCK FUNC END
#/////////////////



# Block Promt main Variable BEGIN

# Block select VPN Profiles BEGIN
:local count 0;
:local listProfiles ""
:foreach var in=[/ppp profile print as-value] do={
	:set $listProfiles ("$listProfiles \r\n	$count $($var->"name")");
	:set $count ($count+1)
}
$PromtInput QstUser=("\r\nSelect VPN profiles (enter number): $listProfiles")  VarName="crtCnfovpnProfile" DefaultValue=$crtCnfovpnProfile LenValue=0
if ([:typeof $crtCnfovpnProfile] = "num") do={
	:set $crtCnfovpnProfile ([/ppp profile get number=$crtCnfovpnProfile value-name=name])
}
:local ServerAddressLocal ([/ppp profile get [/ppp profile find where name=$crtCnfovpnProfile] value-name="local-address"])
:local nameFolderConfLocal $crtCnfovpnProfile
# Block select VPN Profiles END

$PromtInput QstUser=("\r\nInsert type installation (1=config or 2=config-auto)")  VarName="crtCnfNameConfig" DefaultValue="1" LenValue=1
if ($crtCnfNameConfig="2") do={
	:set $crtCnfNameConfig "config-auto"
} else={
	:set $crtCnfNameConfig "config"
}
$PromtInput QstUser=("\r\nInsert external server address")  VarName="crtCnfServerAddress" DefaultValue=$crtCnfServerAddress LenValue=0
$PromtInput QstUser=("\r\nInsert new username")  VarName="crtCnfUserName" DefaultValue="" LenValue=0
$PromtInput QstUser=("\r\nInsert new password")  VarName="crtCnfPassword" DefaultValue="" LenValue=0

# Block select CA BEGIN
:local count 0;
:local listCA ""
:foreach var in=[/certificate print  as-value where trusted=yes] do={
	:set $listCA ("$listCA \r\n	$count $($var->"name")");
	:set $count ($count+1)
}
$PromtInput QstUser=("\r\nSelect CA (enter number): $listCA")  VarName="crtCnfcaName" DefaultValue=$crtCnfcaName LenValue=0
if ([:typeof $crtCnfcaName] = "num") do={
	:set $crtCnfcaName ([/certificate get number=$crtCnfcaName value-name=name])
}
# Block select CA END

$PromtInput QstUser=("\r\nInsert certificate validity period")  VarName="crtCnfdaysValid" DefaultValue=$crtCnfdaysValid LenValue=0
$PromtInput QstUser=("\r\nInsert pass-phrase for key")  VarName="crtCnfAskPassword" DefaultValue="" LenValue=8


:put "\r\nParam:"
:put ("Type instalation:	$crtCnfNameConfig")
:put ("Local folder name:	$nameFolderConfLocal")
:put ("External server address:$crtCnfServerAddress")
:put ("VPN profile:		$crtCnfovpnProfile")
:put ("Local address:		$ServerAddressLocal")
:put ("CA name:		$crtCnfcaName")
:put ("User:			$crtCnfUserName")
:put ("Password:		$crtCnfPassword")
:put ("Pass-phrase:		$crtCnfAskPassword")
:put ("Validity period:	$crtCnfdaysValid days")



$PromtInput QstUser=("\r\nPlease enter \"y\" for continue or press enter for cancel")  VarName="crtCnfContinue" DefaultValue=false LenValue=0
if ($crtCnfContinue=false) do={
	$crtCnfEndProgramm MessageText=("You press cancel. Stop program")
}
:put "Run programm"

# Block Promt main Variable END



# Block add user BEGIN

if ([:len [/ppp secret find where name=$crtCnfUserName]]=0) do={
	do {
		/ppp secret add name=$crtCnfUserName password=$crtCnfPassword profile=$crtCnfovpnProfile service=ovpn
	} on-error={
		$crtCnfEndProgramm MessageText=$MessageText
	}
} else={
	$crtCnfEndProgramm MessageText=("User \"$crtCnfUserName\" is exist. Stop program")
}

# Block add user END

# Block create folder tree BEGIN

$crtCnfCreateTree $nameFolderConfLocal $crtCnfUserName $NameOrg;
# File create very quikly, need wait
:delay 1
if ([:len [/file find where name=$crtCnfFullPath]]>0) do={
	:put ("Current full path \"".$crtCnfFullPath."\"")
} else={
	$crtCnfEndProgramm MessageText=("Full path \"".$crtCnfFullPath."\" is not exist. Stop program")
}

# Block create folder tree END

# Block CERT BEGIN
# Block generate cert BEGIN
if ([:len [/certificate find where name=$crtCnfUserName]]>0) do={
	:put ("Cert \"$crtCnfUserName\" is exist.")
} else={
	:local CA [/certificate get $crtCnfcaName]
	:if ([:typeof $CA]="array" && [:len $CA]>="0") do={
		do {
		:put $crtCnfUserName
		:put $CA
		
			/certificate add name=$crtCnfUserName country=($CA->"country") state=($CA->"state") locality=($CA->"locality") organization=($CA->"organization") unit=($CA->"unit") common-name=$crtCnfUserName key-size=2048 days-valid=$crtCnfdaysValid key-usage=tls-client 
			/certificate sign $crtCnfUserName ca=$crtCnfcaName
		} on-error={
			$crtCnfEndProgramm MessageText=("Issue create or sign cert \"$crtCnfUserName\". Stop program")
		}
	} else={
		$crtCnfEndProgramm MessageText=("CA \"$crtCnfcaName\" is not exist. Stop program")
	}
}
# Block generate cert END

# Block export cert BEGIN
do {
	/certificate export-certificate $crtCnfUserName type=pkcs12 file-name=("$nameFolderConfLocal/$crtCnfUserName/$NameOrg/$crtCnfUserName") export-passphrase=$crtCnfAskPassword
} on-error={
	$crtCnfEndProgramm MessageText=("Issue export cert. Stop program")
}

# Block export cert END
# Block CERT END

# Block export config files BEGIN
# Block create oVPN file BEGIN
:local contents
:set contents ($contents."client")
:set contents ($contents."\r\ndev tun")
:set contents ($contents."\r\nproto tcp")
:set contents ($contents."\r\nremote $crtCnfServerAddress")
:set contents ($contents."\r\npersist-key")
:set contents ($contents."\r\npersist-tun")
:set contents ($contents."\r\nauth-nocache")
:set contents ($contents."\r\npkcs12 \"C:\\\\Program Files\\\\OpenVPN\\\\$crtCnfNameConfig\\\\$NameOrg\\\\$crtCnfUserName.p12\"")
:set contents ($contents."\r\naskpass \"C:\\\\Program Files\\\\OpenVPN\\\\$crtCnfNameConfig\\\\$NameOrg\\\\1.cfg\"")
if ($crtCnfNameConfig="config-auto") do={
	:set contents ($contents."\r\nauth-user-pass \"C:\\\\Program Files\\\\OpenVPN\\\\$crtCnfNameConfig\\\\$NameOrg\\\\2.cfg\"")
} else={
	:set contents ($contents."\r\nauth-user-pass")
}
:set contents ($contents."\r\nlog \"C:\\\\Program Files\\\\OpenVPN\\\\$crtCnfNameConfig\\\\$NameOrg\\\\$NameOrg.log\"")
:set contents ($contents."\r\nremote-cert-tls server")
:set contents ($contents."\r\nroute $RouteAddressAdd 255.255.255.255 $ServerAddressLocal")
:set contents ($contents."\r\ncipher AES-256-CBC")

:put "\r\n______________________"
:put "Config begin"
:put "______________________"
:put $contents
:put "______________________"
:put "Config end"
:put "______________________\r\n"
$CreateFile FileName=("$nameFolderConfLocal/$crtCnfUserName/$NameOrg_$crtCnfUserName.ovpn") Contents=$contents

# Block create oVPN file END

# Block create auth-user-pass file BEGIN

if ($crtCnfNameConfig="config-auto") do={
	:set contents ""
	:set contents ($contents.$crtCnfUserName)
	:set contents ($contents."\r\n$crtCnfPassword")
	$CreateFile FileName=("$nameFolderConfLocal/$crtCnfUserName/$NameOrg/2.cfg") Contents=$contents
}
# Block create auth-user-pass file END

# Block create ask-pass file BEGIN

$CreateFile FileName=("$nameFolderConfLocal/$crtCnfUserName/$NameOrg/1.cfg") Contents=$crtCnfAskPassword

# Block create ask-pass file END
# Block export config files END

$crtCnfRemoveENV
:put "END PROGRAMM"

При создании скрипта использовались старые наработки - удаление глобальных переменных, а также новые функции:

  1. Создать каталог
    1. Обход параметров функции
  2. Ввод пользователя
В картинках:






Проверяем конфиг


Скачиваем и устанавливаем ПО для клиента


Копируем каталог и конфигурационный файл


Перезапускаем службу


Проверяем




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

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