понедельник, 4 октября 2021 г.

RocketChat и Active Directory

 Буквально пару параметров, для тех кто ищет и не может никак найти решение.


Поиск пользователей, Фильтр:

(&(memberOf:1.2.840.113556.1.4.1941:=CN=ACL_ROCKETCHAT,CN=Users,DC=domain,DC=local)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))

Берем всех незаблокированных пользователей группы ACL_ROCKETCHAT, включая вложенные группы


И второй важный параметр:

Фильтр групп пользователей: (&(cn=#{groupName})(member=#{userdn}))

Вот именно последнюю фишку #{userdn} запилили в 2020 году. Без нее приходится бегать по домену скриптом, и писать свойства туда, где их нет, чтобы хоть как-то работал этот RocketChat (((

https://github.com/RocketChat/Rocket.Chat/pull/16273


вторник, 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


среда, 17 марта 2021 г.

VsDesk на nginx

 Ставим убунту 16 по инструкции, ставим fcgiwrap, nginx, php7.0-fpm, ставим все зависимости, кроме апача.

apt install -y htop mc wget sudo iptables nano ncdu dnsutils net-tools psmisc curl nginx php-fpm php-gd php-imap php-ldap php-imagick php-mbstring php-mcrypt php-curl php-mysqli php-zip php-xml zip mysql-server php-ldap fcgiwrap


Распаковываем дистр в каталог /var/www/vsdesk

копируем php.ini в /etc/php/7.0/fpm, копируем модуль ioncube в каталог /usr/lib/php, копируем mysqld.cnf в /etc/mysql/mysql.conf.d

Можно вообще ничего не исправлять, кроме имени домена. Ниже конфиг для nginx

nano /etc/nginx/sites-enabled/default


server { listen 80 default_server;

server_name vsdesk vsdesk.yourdomain.com;

rewrite ^ https://$host$request_uri permanent; }

server { listen 443 ssl http2 default_server; ssl_certificate /etc/nginx/ssl/public.crt; ssl_certificate_key /etc/nginx/ssl/private.key; root /var/www/vsdesk; index index.php;

server_name vsdesk vsdesk.yourdomain.com;

location ~\.(css|js|png|ttf|woff|ico|woff2|jpg|pdf|tif|tiff|jpeg|bmp)$ { try_files $uri /index.php; }

location / { rewrite / /index.php?$request_uri; }

location ~ \.php$ { include snippets/fastcgi-php.conf; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_pass unix:/run/php/php7.0-fpm.sock; }

location /cgi-bin/ { gzip off; add_header 'X-Accel-Buffering' 'no' always; root /usr/lib; fastcgi_pass  unix:/run/fcgiwrap.socket; include /etc/nginx/fastcgi_params; fastcgi_param SCRIPT_FILENAME /usr/lib$fastcgi_script_name; }

}
если не будет работать (404 ошибка), то можно сделать:
chmod -R 777 /var/www/vsdesk && chown -R root:root /var/www/vsdesk

Секьюрность, HSTS и прочие свистелки/перделки добавите по вкусу. Задача была просто запустить vsdesk на nginx без регистрации и смс.

понедельник, 18 января 2021 г.

Проверим в домене активацию Windows и Office

Скрипт бежит по домену, подключается к компьютерам, считывает информацию о лицензировании, выводит в консоль в формате CSV

Наздоровье

[code]strComputer=""

strFilter=""

strQuery=""



On Error Resume Next

Set objDomain=Getobject("WinNT://DOMAIN")


For Each object In objDomain

If object.Class="Computer" Then

strComputer=object.Name

strWin=GetWindowsStatus

strOff=GetOfficeStatus

Wscript.echo strComputer & ";" & strWin & ";" & strOff

End If

Next

Function GetActivationStatus

On Error Resume Next

On Error Goto 0

Err.Clear

Status="Not found"

Set objWMI = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")

If Err.Number=0 Then

i=0

Set col = objWMI.ExecQuery(strQuery)

Status="Not activated"

For Each obj in col

i=i+1

If obj.LicenseStatus=1 Then Status="Activated": Exit For

Next

If i=0 Then Status="Not Found"

End If

GetActivationStatus=Status

End Function


Function GetWindowsStatus

strFilter="Windows"

strQuery="SELECT LicenseStatus FROM SoftwareLicensingProduct WHERE Description like '%" & strFilter & "%'"

strGWS=GetActivationStatus

GetWindowsStatus=strGWS

End Function

Function GetOfficeStatus

strFilter="Office"

strQuery="SELECT LicenseStatus FROM SoftwareLicensingProduct WHERE Description like '%" & strFilter & "%'"

strGOS=GetActivationStatus

GetOfficeStatus=strGOS

End Function

[/code]