вторник, 22 июня 2021 г.

XCP-ng и автозапуск виртуальной машины после пропадания питания

 Небольшой скрипт по традиции:

#!/bin/sh
if [ ! -z $1 ]; then
        vm=`xe vm-list name-label=$1 | grep uuid | sed -e 's/ //g' | cut -d ":" -f 2`
        xe vm-param-set uuid=$vm other-config:auto_poweron=true
else
        echo "Enable auto poweron on VM"
        echo "Usage: "$0" [vm-name] [pool-name]"
        exit 0
fi
if [ ! -z $2 ]; then
        pool=`xe pool-list name-label=$2 | grep uuid | sed -e 's/ //g' | cut -d ":" -f 2`
        xe pool-param-set uuid=$pool other-config:auto_poweron=true
fi


Суть в том, что недостаточно включить галку на некоторой машине. Нужно включить auto_poweron в пуле, если Вы не делали этого ранее.


среда, 16 июня 2021 г.

Bitrix24-Коробка. Делаем, чтобы письма становились задачами. Простой загрузчик без форм

Сохраняю для себя, может пригодиться этот кусок говнокода.

Оповещения не реализованы. Смотрите по списку литературы - кагбэ не сложно, но просто не нужно в текущей реализации.

#
# Проверим, что модуль задачи активен
#
if (CModule::IncludeModule("tasks")) {
    #
    # Взять ID пользователя по адресу почты
    #
    $email = CMailUtil::ExtractMailAddress($arMessageFields['FIELD_FROM']);
    $from=$email;
    $rsUser = CUser::GetList(($by="ID"), ($order="desc"), array("email"=>$from));
    if ($oUser = $rsUser->Fetch())
    $id_created="1";
    $id_created = $oUser["ID"];
    #
    # Добавим задачи этим людям. Первый - исполнитель, остальные - соисполнители.
    #
    $arr_to=explode(";","additional_user_not_listed_here@gmail.com");
    foreach ($arr_to as $to)    {
        if (empty($to)) continue;
        $rsUser = CUser::GetList(($by="ID"), ($order="desc"), array("email"=>$to));
        if ($oUser = $rsUser->Fetch())
        $arr_responsible[] = $oUser["ID"];
    }
    #
    # Заполнить массив аргументов для создания задачи
    #
    $arFields = Array(  
        "TITLE" => "Bitrix24-Support: ".$arMessageFields['SUBJECT'],
        "DESCRIPTION" => htmlspecialcharsBack(TxtToHTML($email."\n".$arMessageFields['BODY'],true,0,"Y","N","Y","Y")),
        "RESPONSIBLE_ID" => $arr_responsible[0],
        "STATUS"=>2,
        "CREATED_BY" => $id_created  );
    #
    # Создать задачу
    #
    $obTask = new CTasks;
    $ID = $obTask->Add($arFields);
    #
    # Список соиссполнителей берем из ответственных
    # Добавим соисполнителей в задачу
    #
    $arr_accomp = array_splice($arr_responsible, 1, (count($arr_responsible)-1));
    #
    # Или вручную назначим исполнителя по UserID
    #
    # $arr_accomp = Array ( 59);
    #
    CTasks::AddAccomplices($ID, $arr_accomp);
    #
    # Список наблюдателей
    # Добавить наблюдателей в задачу
    # То же самое, что с исполнителем
    $arr_auditors= Array ( 6 );
    CTasks::AddAuditors($ID,$arr_auditors);

    #
    #Сохраняем вложения
    #
    $dbr_attach = CMailAttachment::GetList(Array("NAME" => "ASC", "ID" => "ASC"), Array("MESSAGE_ID" => $arMessageFields['ID']));
    while ($dbr_attach_arr = $dbr_attach->GetNext()) {
        if ($dbr_attach_arr["FILE_NAME"]=='1.tmp' ||
            preg_match_all('/\\.(?:exe|html|phtml|pl|js|htm|py|php|php4|php3|phtml|shtml)$/i', $dbr_attach_arr["FILE_NAME"], $p_matches, PREG_PATTERN_ORDER))
            continue;
        $attach_id = $dbr_attach_arr["ID"];
        $dbr = CMailAttachment::GetByID($attach_id);
        if($dbr_arr = $dbr->Fetch()) {
            $fname =  $_SERVER['DOCUMENT_ROOT']."/upload/from_mail/".$dbr_attach_arr["FILE_NAME"];
            $handle = fopen($fname, 'wb');
            fwrite($handle, $dbr_arr["FILE_DATA"]);
            fclose($handle);
            $arFile = CFile::MakeFileArray($fname);
            $arFile["old_file"] = "";
            $arFile["del"] = "Y";
            $arFile["MODULE_ID"] = "tasks";
            $storage = Bitrix\Disk\Driver::getInstance()->getStorageByUserId($id_created);
            $folder = $storage->getFolderForUploadedFiles();
            $file = $folder->uploadFile($arFile, array(
                'NAME' => $arFile["name"],
                'CREATED_BY' => $id_created
                ), array(), true);
            $FILE_ID = $file->getId();
            $oTaskItem = new CTaskItem($ID, $id_created);
        $rs = $oTaskItem->Update(array("UF_TASK_WEBDAV_FILES" => Array("n$FILE_ID")));
        }
    }
}
#
# Источники информации:
#
# https://dev.1c-bitrix.ru/support/forum/forum23/topic89021/
# https://pai-bx.com/wiki/1c-bitrix/44-kp-create-task-from-email/

вторник, 15 июня 2021 г.

Zimbra и бан через Reverse Proxy (Ошибка сети)

Мы опубликовали Zimba через reverse-proxy, прокинули все нужные заголовки, НО (!!!) пользователи периодически получают массово ошибку сети.

 Смотрим журнал /opt/zimbra/log/mailbox.log.

Нужно найти там записи типа bla-bla-bla suspended и IP reverse-proxy.

Если все эти звезды сошлись, то проблема в том, что jetty не прокидывает заголовок X-Forwarded-For.

/opt/zimbra/jetty/jetty.xml.in

Ищем forwardedForHeader и меняем его значение с bogus на X-Forwarded-For, рестартуем службы.

Профит.

среда, 9 июня 2021 г.

Смена пароля у пользователя в домене и генератор паролей

Вот такой небольшой скрипт по генерации паролей заданной длины + есть функция смены пароля у пользователя текущего домена (поиск по sAMAccountName)

 

'
' Скрипт генерации паролей. Запускать от администратора с повышением привилегий
'
Interactive=False
Complexity=False
strLow="abcdefghijklmnopqrstuvwxyz"
strUp="ABCDEFGHIJKLMNOPQRSTUVWXYZ"
strNumbers="1234567890"
strSpecial="!@#$%^&*()-_=+\|/"
PasswordLength=8

On Error Resume Next
Set objArgs=Wscript.Arguments
For i=0 to objArgs.Count-1
    If Ucase(objArgs.Item(i))="/COMPLEX" Then Complexity=True
    If Ucase(objArgs.Item(i))="/VERBOSE" Then Interactive=True
    If Ucase(left(objArgs.Item(i),9))="/PASSLEN:" Then PasswordLength=Cint(Mid(objArgs.Item(i),10))
    If Ucase(left(objArgs.Item(i),6))="/USER:" Then strUserName=Mid(objArgs.Item(i),7)
    If objArgs.Item(i)="/?" or Ucase(objArgs.Item(i))="/HELP" Then ShowUsage
Next
If Complexity Then strVoc=strSpecial & strLow & strUp & strNumbers Else strVoc=strLow & strUp & strNumbers
If Interactive Then print "Complexity: " & Complexity & vbcrlf & "PasswordLength: " & PasswordLength & vbcrlf & "Vocabulary: " & strVoc & vbcrlf & "UserName: " & strUserName
If strUserName="" Then
    print GeneratePassword
Else
    print "UserName: " & strUserName
    SetRandomPasswordFor strUserName
End If
Quit

Sub ShowUsage
    On Error Resume Next
    print "/COMPLEX - to generate ONLY complex passwords" & vbcrlf & "/VERBOSE - to be more loud" & vbcrlf & "/PASSLEN:N - to set password length to N" & vbcrlf & "/USER:username - to generate and set password for specified user in current domain" & vbcrlf & "/? or /HELP - this message" & vbcrlf & "NOTE: You must always run as administrator and be privileged"
    Quit
End Sub
Sub SetRandomPasswordFor(strUser)
    On Error Resume Next
    If Interactive Then print "Get distinguishedName for user: " & strUser
    Err.Clear
    strUserDN=GetLDAPValue("distinguishedName","sAMAccountName='" & strUser & "'")
    If strUserDN="" And Interactive Then print "Empty distinguishedName returned": Quit
    If Interactive Then print strUserDN
    If Interactive Then print "connecting to object"
    Err.Clear
    Set objUser=Getobject("LDAP://" & strUserDN)
    If Error And Interactive Then print "Something is wrong while connecting to object"
    If Interactive Then print "Generate password"
    strPassword=GeneratePassword
    print "Password is: >" & strPassword & "<"
    If Interactive Then print "Set password"
    objUser.SetPassword strPassword
    Err.Clear
    objUser.Setinfo
    If Error And Interactive Then print "Not set"
    print "Done"
End Sub
Function GeneratePassword
    On Error Resume Next
    Do
        strPass=""
        randomize timer
        i=0
        Do
            i=i+1
            x=int(rnd * len(strVoc)+1)
            strPass=strPass & mid(strVoc,x,1)
            If Len(strPass)>=PasswordLength Then Exit Do
        Loop
        If Complexity=False Then Exit Do
    Loop While IsComplex(strPass)=False
    GeneratePassword = strPass
End Function
Function IsComplex(strMyPassword)
    On Error Resume Next
    bSpecial=False
    bNumbers=False
    bLow=False
    bUp=False
    For i=1 to Len(strSpecial)
        If instr(1,strMyPassword,Mid(strSpecial,i,1))>0 Then bSpecial=True: Exit For
    Next
    For i=1 to Len(strNumbers)
        If instr(1,strMyPassword,Mid(strNumbers,i,1))>0 Then bNumbers=True: Exit For
    Next
    For i=1 to Len(strLow)
        If instr(1,strMyPassword,Mid(strLow,i,1))>0 Then bLow=True: Exit For
    Next
    For i=1 to Len(strUp)
        If instr(1,strMyPassword,Mid(strUp,i,1))>0 Then bUp=True: Exit For
    Next
    If bSpecial=True And bNumbers=True And bLow=True And bUp=True Then IsComplex=True Else IsComplex=False

End Function

Function GetLDAPValue(strValue,strFilter)
    On Error Resume Next
    Const ADS_SCOPE_SUBTREE = 2
    Set objConnection = CreateObject("ADODB.Connection")
    Set objCommand = CreateObject("ADODB.Command")
    objConnection.Provider = "ADsDSOObject"
    objConnection.Open "Active Directory Provider"
    Set objCommand.ActiveConnection = objConnection
    objCommand.Properties("Page Size") = 1000
    objCommand.Properties("Searchscope") = ADS_SCOPE_SUBTREE
    strQuery="SELECT " & strValue & " FROM 'LDAP://" & GetLDAPDomain & "' WHERE " & strFilter
    objCommand.CommandText =  strQuery
    Set objRecordSet = objCommand.Execute
    Do Until objRecordSet.EOF
        strVar = strVar & objrecordset.Fields(strValue).Value & "|"
        objRecordSet.MoveNext
    Loop
    If Len(strVar)>1 Then strVar=left(strVar,Len(strVar)-1)
    If strVar="|" Then strVar=""
    GetLDAPValue=strVar
End Function
Function GetLDAPDomain
    On Error Resume Next
    Set iAdRootDSE = GetObject("LDAP://RootDSE")
    GetLDAPDomain = iAdRootDSE.Get("defaultNamingContext")
End Function
Sub print(strWhat)
    On Error Resume Next
    Wscript.echo strWhat
End Sub
Sub Quit
    Wscript.Quit
End Sub
Function Error
    If Err.Number=0 Then Error=False Else Error=True
End Function