Skip to content

Blog

Processing Apollo


Sono sempre rimasto affascinato dai colori degli anni '70, ma non me la sono sentita di rendere la mia casa un pochino troppo scioccante. Non adesso, insomma. Magari più avanti ci penserò.

Per ora, credo di potermi accontentare di decorare allegramente il desktop del mio computer. E quale migliore occasione per sperimentare un po' di Processing?

Per prima cosa, vediamo di ricavare i cinque colori utilizzati nella figura, ad esempio in formato RGB, e per questo basta un qualsiasi programma di grafica:

int larghezza_figura = 100;
int altezza_figura = 100;

size(larghezza_figura*5, altezza_figura);
smooth();
noStroke();

pushMatrix();
fill(110, 50, 22); // colore 1
rect(0, 0, larghezza_figura, altezza_figura);

translate(larghezza_figura, 0);
fill(226, 180, 25); // colore 2
rect(0, 0, larghezza_figura, altezza_figura);

translate(larghezza_figura, 0);
fill(220, 115, 0); // colore 3
rect(0, 0, larghezza_figura, altezza_figura);

translate(larghezza_figura, 0);
fill(197, 75, 2); // colore 4
rect(0, 0, larghezza_figura, altezza_figura);

translate(larghezza_figura, 0);
fill(131, 37, 11); // colore 5
rect(0, 0, larghezza_figura, altezza_figura);
popMatrix();

Anche se i colori non sono precisi, va bene lo stesso, ed il risultato è una combinazione molto vintage e molto calda:

Penso che ci saranno molte occasioni in cui potrò riciclarla!

E per le dimensioni dei cerchi, che vanno via via a rimpicciolirsi? La scala non è fissa, e un buon risultato si ottiene partendo da una scala di 0,85 moltiplicata di volta in volta per 0,9. Ecco un esempio:

float dim_figura = 160;
float scala = 0.85;
float varia = 0.9;

size(int(dim_figura), int(dim_figura));
smooth();
noStroke();

pushMatrix();
background(110, 50, 22); // colore sfondo

dim_figura = dim_figura*scala;

fill(226, 180, 25); // colore 2
ellipse(width/2, height/2, dim_figura, dim_figura);

fill(220, 115, 0); // colore 3
scala = scala*varia;

dim_figura = dim_figura*scala;
ellipse(width/2, height/2, dim_figura, dim_figura);

fill(197, 75, 2); // colore 4
scala = scala*varia;
dim_figura = dim_figura*scala;
ellipse(width/2, height/2, dim_figura, dim_figura);

fill(131, 37, 11); // colore 5
scala = scala*varia;
dim_figura = dim_figura*scala;
ellipse(width/2, height/2, dim_figura, dim_figura);

popMatrix();

Le proporzioni ci assomigliano, ma manca ancora qualche cosa:

Sono certo che si possa fare di meglio, ma per ora il codice finale è il seguente:

float dim_figura = 160;
float scala = 0.85;
float varia = 0.9;

size(int(dim_figura), int(dim_figura));
smooth();
noStroke();

pushMatrix();
translate(width/2,height/2);
rotate(radians(45));
background(110, 50, 22); // colore sfondo

fill(226, 180, 25); // colore 2

dim_figura = dim_figura*scala;
ellipse(0, 0, dim_figura, dim_figura);

fill(220, 115, 0); // colore 3
scala = scala*varia;

translate((dim_figura-dim_figura*scala)/2, 0);
dim_figura = dim_figura*scala;
ellipse(0, 0, dim_figura, dim_figura);

fill(197, 75, 2); // colore 4
scala = scala*varia;

translate((dim_figura-dim_figura*scala)/2, 0);
dim_figura = dim_figura*scala;
ellipse(0, 0, dim_figura, dim_figura);

fill(131, 37, 11); // colore 5

scala = scala*varia;
translate((dim_figura-dim_figura*scala)/2, 0);
dim_figura = dim_figura*scala;
ellipse(0, 0, dim_figura, dim_figura);

translate(-width/2,-height/2);
popMatrix();

e il risultato, non sembra niente male!

Gestione Collegamenti LNK tramite VBS

È un dato di fatto: i desktop dei PC aziendali sono una schifezza. Sono pieni di collegamenti, magari vecchi ed obsoleti o che puntano al percorso errato.

A volte sono pieni di collegamenti scritti con le maiuscole, o con troppi spazi, o con troppo pochi spazi, oppure capitalizzati senza un criterio logico e comprensibile.

Spesso ancora i collegamenti vengono creati sul desktop (e quindi nel profilo) di ogni singolo utente , mentre a volte se ne starebbero più felici e contenti nel profilo All Users del computer.

Come rimediare ad una situazione del genere, e dare un tocco di pulizia e professionalità ai ~~catorci~~ PC della nostra azienda?
Con un poco di codice VBS, naturalmente!

Casi di utilizzo
Si potrebbero voler eliminare tutti i collegamenti non più necessari, quali ad esempio _\\ServerAziendale\Documenti\Incentivi_.

Oppure si potrebbero voler aggiornare dei collegamenti con un nuovo percorso (ad es. i collegamenti ad http://it.wikipedia.org potrebbero essere aggiornati alla nuova versione dell'enciclopedia all'indirizzo http://nonciclopedia.wikia.com).

In questo esempio farò molto di più!

Vado alla ricerca del collegamento ad un determinato programma (\\ServerAziendale\Programma\Start.exe) sul desktop di ogni singolo utente. Se almeno un utente dispone di questo link, li cancello tutti quanti, e ne ricreo uno solo (con il nome bello, pulito, e capitalizzato come si deve) nel desktop di All Users.

The Code

Set Start_Exe = CreateObject("System.Collections.ArrayList")

La variabile Start_Exe conterrà tutti gli eventuali link al nostro programma.

Rem *** Ricerca in tutti i profili ***

Const HKEY_LOCAL_MACHINE = &H80000002
Const HKEY_USERS         = &H80000003

strComputer = "."

Set objRegistry = GetObject("winmgmts:\\" & strComputer & "\root\default:StdRegProv")
Set ws = CreateObject("Wscript.Shell")
Set oFs = CreateObject("Scripting.FileSystemObject")

strKeyPath = "SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList"
objRegistry.EnumKey HKEY_LOCAL_MACHINE, strKeyPath, arrSubkeys

For Each objSubkey In arrSubkeys
  strValueName = "ProfileImagePath"
  strSubPath = strKeyPath & "\" & objSubkey
  objRegistry.GetExpandedStringValue HKEY_LOCAL_MACHINE, strSubPath, strValueName, strProfile
  ScanDesktop(strProfile & "\Desktop") ' dispongo del percorso del desktop!
Next

strAllUsersDesktopPath = ws.SpecialFolders.Item("AllUsersDesktop")

rem *** se si vogliono ricreare anche i collegamenti in AllUsers già esistenti,
rem *** decommentare le seguenti righe
'ScanDesktop(strAllUsersDesktopPath)

Il codice precedente legge l'elenco dei percorsi dei profili, a partire dalla chiave HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList. Che fare se il desktop è stato redirezionato da qualche parte? Ci pensiamo un'altra volta, please!


Ed ecco il codice che ricrea i collegamenti:

Rem ***verifica se ci sono collegamenti al programma
If Start_Exe.Count > 0 Then
  Set objShtCut = ws.CreateShortcut(strAllUsersDesktopPath & "\Programma Aziendale.lnk")
  objShtCut.TargetPath = "\\ServerAziendale.dominio.dc\Programma\Start.exe"
  objShtCut.WorkingDirectory = "\\ServerAziendale.dominio.dc\Programma\"
  objShtCut.Save
  Set objShtCut = Nothing

  For Each Lnk in Start_Exe
    Set fl = oFs.GetFile(Lnk)
    fl.Delete
    Set fl = Nothing
  Next
End If

Manca niente? Manca il codice che scansiona ogni singolo Despol:

Sub ScanDesktop(profile)
  If oFs.FolderExists(profile) Then
    Set oFolder = oFs.GetFolder(profile)

    For Each oFile In oFolder.Files
    name = oFile.Name
    if len(name)>4 and UCase(right(name,4)) = ".LNK" then
      Set objShtCut = ws.CreateShortcut(profile & "\" & oFile.Name)
      Select Case UCase(objShtCut.TargetPath)
      Case "\\SERVERAZIENDALE\PROGRAMMA\START.EXE"
        Start_Exe.Add profile & "\" & oFile.Name
      Case "\\SERVERAZIENDALE.DOMINIO.DC\PROGRAMMA\START.EXE
        Start_Exe.Add profile & "\" & oFile.Name
      End Select
      Set objShtCut = Nothing
    End If
End Sub

Naturalmente nella nostra sub ScanDesktop possiamo identificare i collegamenti tramite il metodo che preferiamo.
E naturalmente, come al solito, sto allegramente ignorando molte condizioni d'errore. Ma la vita è troppo corta per la gestione adeguata degli errori. E in VBS non vi è traccia di Try Catch Finally... oppure sì?

Bloccare gli aggiornamenti software

Disabilitare gli aggiornamenti del sistema operativo oppure del software applicativo è in genere una pratica da evitare : un sistema aggiornato è più stabile, e molto più sicuro!

Ad oggi le poche aziende che non hanno attivato un sistema di aggiornamento automatico per il sistema operativo Windows ne pagano le conseguenze (vedi ad esempio Conficker). Ed è curioso notare che, poiché un sistema Windows aggiornato e configurato adeguatamente offre sistemi di sicurezza avanzati e difficili da aggirare, si sta assistendo ad un cambio di tendenza e l'obiettivo principale degli attacchi del malware più recente si sta spostando dal sistema operativo agli applicativi utente installati sopra.

Applicazioni che girano in modalità utente quali Flash Player, Silverlight, Acrobat Reader, Internet Explorer, Firefox e relativi plugin, sono tutte ottime candidate.... i bug non mancano mai, e se ci riflettiamo un poco perché attaccare tutto il sistema? Dati sensibili, password e quant'altro si trovano proprio dal lato utente!

Naturalmente verso questo tipo di attacchi il famigerato UAC di Windows Vista e Windows 7 spesso non può fare molto... così come può fare ben poco la richiesta di password dei vari Linux, o MacOS: il sistema è protetto, e va bene, ma i dati degli utenti forse non troppo!

Perché quindi bloccare gli aggiornamenti del software applicativo?

Ci devono essere delle ottime ragioni, ed una di queste è che gli aggiornamenti spesso e volentieri sono progettati per funzionare su computer domestici , in cui l'utente ha accesso ad Internet completo ed accede al desktop con amministrativi.

In un'azienda non tutti i PC hanno accesso ad Internet , quelli che vi accedono probabilmente dispongono di un accesso filtrato , ed anche se riescono a scaricare gli aggiornamenti, propabilmente non riescono ad installarli poiché l'utente non dispone di diritti per aggiornare il software installato.

Normalmente il software applicativo presente nei computer aziendali non è lo stesso software che può scaricare l'utente finale: in genere è preferibile distribuire versioni amministrative o aziendali oppure versioni ripacchettizzate (es. Firefox, Flash Player, e Acrobat Reader si trovano anche in versione MSI, e nel caso in cui non si trovino, basta cercare per esempio sul sito www.appdeploy.com per trovare tutto quello di cui c'è bisogno).

E normalmente il software applicativo viene configurato per non aggiornarsi automaticamente: gli aggiornamenti sono disabilitati, perché è inutile disporre di decine di updater uno per ogni servizio, quando invece sono disponibili applicazioni apposite per la distribuzione di tutti gli aggiornamenti!

Cosa fare per tutto il software che invece è stato scaricato da una versione "tradizionale" e quindi con gli aggiornamenti attivati?

In genere le informazioni di configurazione sono sparsi in qualche chiave di registro, naturalmente nella HKLM. Abbiamo bisogno quindi di un buon script WSH da eseguire all'avvio della macchina da lanciare con privilegi amministrativi.

Creiamo una nuova policy, e su Computer Configuration - Windows Settings - Scripts aggiungiamo uno script di avvio simile al seguente:

Dim WshShell
Set WshShell = WScript.CreateObject("WScript.Shell")

On Error Resume Next

' disabilita aggiornamenti Adobe Acrobat

WshShell.RegWrite "HKLM\Software\Policies\Adobe\Acrobat Reader\8.0\FeatureLockdown\bUpdater", 0, "REG_DWORD"
WshShell.RegWrite "HKLM\SOFTWARE\Policies\Adobe\Acrobat Reader\9.0\FeatureLockdown\bUpdater", 0, "REG_DWORD"
WshShell.RegWrite "HKLM\SOFTWARE\Adobe\Acrobat Reader\9.0\AdobeViewer\EULA", 1, "REG_DWORD"
WshShell.RegWrite "HKLM\SOFTWARE\Adobe\Acrobat Reader\9.0\AdobeViewer\Launched", 1, "REG_DWORD"

' disabilita aggiornamenti Java

WshShell.RegWrite "HKLM\SOFTWARE\JavaSoft\Java Update\Policy\EnableJavaUpdate",  0, "REG_DWORD"
WshShell.RegWrite "HKLM\SOFTWARE\JavaSoft\Java Update\Policy\EnableAutoUpdateCheck", 0, "REG_DWORD"

Wscript.Quit

Questi sono i parametri che ho trovato in giro. Ovviamente possiamo personalizzare questo script a piacimento per supportare altri programmi oppure altre versioni. Naturalmente cerchiamo di farne buon uso!

Llo stesso identico procedimento può essere utilizzato per modificare all'avvio qualsiasi chiave di sistema oppure, se creiamo lo script lato utente, possiamo impostare le chiavi in HKEY_CURRENT_USER.

Molto utile quindi non solo per gli aggiornamenti, ma anche per poter preconfigurare ad ogni avvio una buona parte del software lato desktop.

Chi è l'utente .Default?

Sfogliando la chiave di registro HKEY_USERS possiamo osservare che sono presenti tutte le chiavi HKEY_CURRENT_USER, organizzate per SID, di tutti gli utenti attualmente connessi al sistema.

La HKEY_CURRENT_USER di ogni utente si trova nel file NTUSER.DAT all'interno di ogni profilo. Quando un utente effettua l'accesso al computer, il sistema operativo carica il file NTUSER.DAT nella HKEY_USERS assieme a tutti gli altri utenti, ed ogni utente nella HKEY_CURRENT_USER vede soltanto le sue chiavi.

Oltre ai SID degli utenti, possiamo osservare che è presente un utente .Default. Ma chi è l'utente .Default?

Naturalmente è ragionevole pensare che l'utente .Default sia utilizzato come profilo template per i nuovi utenti: modificando le impostazioni dell'utente .Default è logico aspettarsi che tutti i nuovi utenti creati nel sistema ereditino queste impostazioni.

In effetti la scelta del nome .Default è stata infelice, e le cose vanno diversamente. Le chiavi di registro dell'utente .Default appartengono all'utente Local System, mentre il template di default per i nuovi utenti di sistema non è generalmente caricato in memoria e si trova nel percorso C:\Users\Default\NTUSER.DAT per Vista o Seven, oppure in C:\Documents and Settings\Default User\NTUSER.DAT per XP.

Per modificare le impostazioni di default è possibile caricare manualmente questo Hive in memoria, modificarlo e salvarlo, anche se questo metodo non è ufficialmente supportato.

Conviene utilizzare il buon SysPrep... oppure ho risolto il problema con un meraviglioso script WSH all'avvio.

Per approfondire: The .Default user is not the default user.

Mappare Condivisioni di Rete in base al Gruppo di appartenenza

Molte volte nello script di logon c'è la necessità di mappare condivisioni di rete in base ai gruppi cui appartiene un utente.
L'articolo How Can I Map Drives Based on Membership in a Group? tratto dal sito di Microsoft spiega molto bene il problema e fornisce una valida introduzione; lo script presentato però è molto semplificato. Ho cercato di estenderlo in base alle esigenze della mia azienda.

Gruppo Primario
Il codice tratto dall'articolo di Microsoft restituisce tutti i gruppi, ad esclusione del gruppo primario dell'utente :

Set objSysInfo = CreateObject("ADSystemInfo")
Set objNetwork = CreateObject("Wscript.Network")

strUserPath = "LDAP://" & objSysInfo.UserName
Set objUser = GetObject(strUserPath)

For Each strGroup in objUser.MemberOf
  strGroupPath = "LDAP://" & strGroup
  Set objGroup = GetObject(strGroupPath)
  strGroupName = objGroup.CN
  Wscript.Echo strGroupName 
Next

Se la objectUser.MemberOf contiene soltanto un gruppo questo il codice qui sopra va in errore, poiché MemberOf non è un array. Bisogna quindi distinguere se si tratta di un array oppure di un gruppo singolo in questo modo:

If isArray(objUser.MemberOf) then
  For Each strGroup in objUser.MemberOf
      strGroupPath = "LDAP://" & strGroup
      Set objGroup = GetObject(strGroupPath)
      checkGroup Unit, objGroup.CN
  Next
Else
    strGroupPath = "LDAP://" & objUser.MemberOf
    Set objGroup = GetObject(strGroupPath)
    checkGroup Unit, objGroup.CN
End If

Alla collezione MemberOf manca dunque il gruppo primario dell'utente, che generalmente è Domain Users ma potrebbe sempre essere stato modificato. Possiamo però ottenere il codice del gruppo primario con objUser.PrimaryGroupID e scorrere l'elenco dei gruppi fino a che non troviamo il gruppo con questo ID. La funzione che restituisce il nome del gruppo dato l'ID è la seguente:

Function GetPrimaryGroup(ByVal GroupID)
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") = 100
objCommand.Properties("Timeout") = 30
objCommand.Properties("Cache Results") = False

Set objRootDSE = GetObject("LDAP://RootDSE")
strDNSDomain = objRootDSE.Get("defaultNamingContext")

objCommand.CommandText = "<ldap://" & strdnsdomain & ">;" &_
  "(objectClass=group);" &_
  "sAMAccountName,primaryGroupToken;subtree"

Set objRecordSet = objCommand.Execute

Do Until objRecordSet.Eof
  If objRecordSet("primaryGroupToken").Value = GroupID Then
    GetPrimaryGroup = objRecordSet("sAMAccountName").Value
    Exit Do
  End If
  objRecordSet.MoveNext
Loop

objRecordset.Close
objConnection.Close

Set objRecordset = Nothing
Set objConnection = Nothing
End Function

Non sono soddisfatto di questa funzione, in quanto non sono riuscito a filtrare il gruppo direttamente nel CommandText e sono pertanto costretto a scorrere tutti i gruppi tramite Loop, fino a che non trovo il gruppo con il codice ID che stavo ricercando, ma.... comunque funziona.

Associazione Gruppi-Condivisioni
Tramite l'oggetto Scripting.Dictionary creo un elenco di associazioni tra gruppi e relative aree condivise, come ad esempio:

Set d = CreateObject("Scripting.Dictionary")

d.Add "Finanziario", "\\server01\fs\Finanziario"
d.Add "Personale", "\\server01\fs\Personale"
d.Add "Legale", "\\server02\Legale"

MapNetworkDrive
A questo punto posso scorrere tutto l'elenco dei gruppi cui l'utente appartiene, passando ogni singolo gruppo alla seguente procedura. Se il gruppo appartiene alla lista dei gruppi a cui è associata un'area condivisa, tale area viene montata nel disco "Unita".

Sub checkGroup(ByRef Unita, ByVal Group)
On Error Resume Next
  If d.Exists(Group) Then
    objNetwork.RemoveNetworkDrive Unita & ":"
    objNetwork.MapNetworkDrive Unita & ":", d(Group), False
    Unita = Chr(Asc(Unita) + 1)
  End If
End Sub

Codice Finale
Il codice finale è il seguente, a cui vanno aggiunte la sub e la funzione presentate in precedenza:

On Error Resume Next

Set objSysInfo = CreateObject("ADSystemInfo")
Set objNetwork = CreateObject("Wscript.Network")

rem *** associazioni gruppo-percorso
Set d = CreateObject("Scripting.Dictionary")

d.Add "Finanziario", "\\server01\fs\Finanziario"
d.Add "Personale", "\\server01\fs\Personale"
d.Add "Legale", "\\server02\Legale"

rem *** prima unità di rete da connettere
Unit = "O"

rem *** utente corrente
Set objUser = GetObject("LDAP://" & objSysInfo.UserName)

rem *** controlla il gruppo primario ***
checkGroup Unit, GetPrimaryGroup(objUser.PrimaryGroupID)

rem *** controlla gli altri gruppi ***
For Each strGroup in objUser.MemberOf
strGroupPath = "LDAP://" & strGroup
Set objGroup = GetObject(strGroupPath)
checkGroup Unit, objGroup.CN
Next

rem *** cleanup
d.RemoveAll

Se un utente appartiene a più gruppi che dispongono di un'area associata, questo script monta tutti i percorsi di rete a partire dalla lettera O:, poi P:, Q: etc...
L'area di lavoro associata al gruppo primario verrà sempre montata come unità O:, mentre le altre aree saranno montate a seguire.
Se la stessa area di lavoro è associata a più gruppi, ed un utente appartiene a più di uno di questi gruppi, la stessa area verrà montata più volte.

Happy logging on!

Variabili d'ambiente nello script di Logon

Come settare le variabili di ambiente in uno script di logon?
In VBScript si può fare così:

Set WshShell = WScript.CreateObject("WScript.Shell")
Set WshUEnv = WshShell.Environment("User")

WshUEnv("INIAPP") = "%USERPROFILE%\APP.INI"

(in questo esempio ho settato alla variabile INIAPP, relativa all'ambiente dell'utente, il valore "%USERPROFILE%\APP.INI"). Per rendere la modifica permanente, aggiungo anche la seguente riga:

WshShell.RegWrite "HKCU\Environment\INIAPP", "%USERPROFILE%\APP.INI", "REG_EXPAND_SZ"

Capita spesso inoltre di dover modificare il PATH di ricerca. La soluzione che utilizzo è la seguente, e mi permette di evitare di avere PATH doppi:

Set WshShell = WScript.CreateObject("WScript.Shell")
Set WshUEnv = WshShell.Environment("User")
Set pp = CreateObject("Scripting.Dictionary")

for each p in Split(WshUenv("PATH"), ";")
  AddPath p
next

AddPath "C:\PROGRAMMA1"
AddPath "C:\ORACLE"
AddPath "C:\TEST"

WshUEnv("PATH") = Join(pp.Keys, ";")
WshShell.RegWrite "HKCU\Environment\PATH", Join(pp.Keys, ";"), "REG_EXPAND_SZ"

pp.RemoveAll
Set pp = Nothing
Set WshShell = Nothing
Set WshUEnv = Nothing

Sub AddPath(path)
  If Not pp.Exists(path) And Trim(path) <> "" Then
    pp.Add path, ""
  End If
End Sub

Suddivido il percorso originario in un array con i percorsi singoli, e con l'oggetto Scripting.Dictionary aggiungo ogni singolo percorso, senza duplicati. Poi ricompongo la stringa.
Semplice ma elegante!

Inviare Check Passivi a Nagios

Nagios è un programma di monitoraggio di computer o risorse di rete che permette di inviare degli avvisi quando un nodo od un servizio non risulta attivo, oppure al ripristino del suo funzionamento.

Per il monitoraggio degli apparati è possibile utilizzare uno dei numerosi comandi predefiniti (es. ping, snmp, etc) oppure è possibile scrivere un comando personalizzato - uno script bash, perl, python o altro va benissimo, l'importante è che tale script restituisca un valore che indica se il servizio che stiamo monitorando è attivo oppure se presenta qualche problema.

Il sistema si occupa in automatico di schedulare i test, di raccogliere i risultati, e di inviare eventuali avvisi.

Oltre a questa modalità di monitoraggio in cui il Nagios effettua dei test ed attende la risposta dagli host remoti, che è detta attiva , è possibile utilizzare una seconda modalità, detta passiva , in cui sono gli host o i servizi remoti che inviano il loro stato al Nagios.

L'utilizzo più tipico consiste nel monitoraggio delle operazioni pianificate, ad esempio backup, allineamento tabelle, etc.
Un task viene eseguito, svolge il backup, l'allineamento, o quant'altro, ed al termine invia al Nagios una notifica con l'esito dell'operazione. Se il Nagios non riceve tale esito entro un certo periodo di tempo (perché il task si blocca prima di inviare l'avviso, o perché il task dura troppo a lungo) può segnalare comunque di non aver ricevuto alcuna informazione e attivare un allarme.

Il servizio può essere definito in questo modo (i parametri possono essere personalizzati a piacere):

define service{
  use                    service-template
  host_name              myhost
  service_description    logs
  freshness_threshold    93600
  notification_period    24x7
  check_command          check_dummy!3 "Dati non ricevuti"
  active_checks_enabled  0
  passive_checks_enabled 1
  notification_interval  0
  check_freshness        1
  check_period           24x7
  max_check_attempts     1
  contact_groups         contatti
  notification_options   c,u,w,r
  notification_interval  0
  notification_period    24x7
}

In cui il comando check_dummy è definito nel seguente modo:

define command {
  command_name    check_dummy
  command_line    $USER1$/check_dummy $ARG1$
}

in questo esempio, Nagios attende il ricevimento di una notifica passiva, e se non riceve una notifica da più di 93600 secondi, allora esegue il comando check_dummy , che imposta lo stato del servizio ad UNKNOWN ed inserisce l'avviso che non riceve i log. Un amministratore di sistema si dovrà dunque preoccupare di approfondire il problema.

Come inviare i dati al server Nagios centrale? Esistono molti sistemi, ma il più semplice ed il mio preferito consiste nell'inviare l'esito dell'operazione direttamente al cmd.cgi che lo mette in coda agli altri eventi e lo fa processare.

Basta inviare un semplice POST HTTP al cgi che si trova in cgi-bin/cmd.cgi e siamo a posto! Come al solito il codice seguente è decisamente semplificato, e non tiene conto di ogni possibile errore o circostanza. In alternativa, è sempre possibile lanciare un buon wget --post="parametri" http://nagios/nagios/cgi-bin/cmd.cgi.

import httplib
import urllib
import sys

argc = len(sys.argv)

if (argc < 6):
  print "Nagios Passive Check Submit"
  print "Esempio:"
  print "nagiospcs Server \"Trasferimento Dati\" 3 \"Errore Import\" \"\""
else:
  data = urllib.urlencode(
    {"cmd_typ" : "30",
     "cmd_mod" : "2",
     "host" : sys.argv[1],
     "service" : sys.argv[2],
     "plugin_state" : sys.argv[3],
     "plugin_output" : sys.argv[4],
     "performance_data" : sys.argv[5]})
  f = urllib.urlopen(
    "http://user:password@servernagios/nagios/cgi-bin/cmd.cgi",
    data)
  s = f.read()
  f.close()

Ave.

Eliminare File Temporanei

Spesso applicazioni ed installer lasciano in giro per l'hard disk un sacco di spazzatura, dimenticandosi allegramente di cancellarla. Anche se è vero che i dischi sono sempre più capienti, e che un po' di file in eccesso non fanno poi così male come molti credono, ogni tanto vale la pena mettere un po' di ordine e di fare un poco di pulizia.

Posizioni dei file temporanei
Dove vengono creati generalmente i file temporanei? In genere vengono salvati nelle seguenti posizioni:

  • %WINDIR%\Temp (tipicamente C:\WINDOWS\Temp)
  • %TEMP% (tipicamente C:\Users\nomeutente\AppData\Local\TEMP, in Windows Vista)

(i file temporanei di Internet Explorer o di altri browser preferisco non toccarli: in questo articolo voglio solo eliminare soltanto i file che si trovano nelle cartelle temporanee utilizzate dagli altri programmi).

Per spazzare via un po' di file inutili dovrebbero quindi essere sufficienti i seguenti comandi:

DEL /F /S /Q "%WINDIR%\Temp"
DEL /F /S /Q "%TEMP%"

Giusto? Al di là del fatto che questi comandi sono particolarmente delicati (attenzione a non sbagliare il percorso!) è importante notare che questi comandi non sono sufficienti in un ambiente multiuser!

Si noti infatti che %TEMP% contiene soltanto il percorso dei file temporanei che appartengono all'utente corrente, ma ogni utente di sistema dispone della sua personale cartella temporanea!

Per svolgere bene il nostro compito, dobbiamo quindi scorrere tutte le variabili di ambiente di tutti gli utenti presenti nel sistema!

Come conoscere tutti gli utenti?
L'elenco degli identificativi degli utenti (detto SID) che dispongono di un profilo nel sistema lo si ottiene interrogando la chiave HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\ProfileList:

REG QUERY "HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\ProfileList

ed all'interno di ogni SID possiamo ottenere il percorso su disco del profilo interrogando la chiave ProfileImagePath.

Dove sono memorizzate le variabili d'ambiente?
Le variabili per l'utente corrente si trovano in HKEY_CURRENT_USER\Environment , mentre le vabili di tutti gli utenti attualmente attivi sul sistema le possiamo recuperare in HKEY_USERS\SID\Environment.

Se vogliamo fare le cose fatte bene dobbiamo scorrere le chiavi anche per tutti gli utenti che non sono attualmente attivi: in questo caso Windows non carica il profilo in memoria nella HKEY_USERS ma tutte le chiavi si trovano su disco nel file NTUSER.DAT che si trova all'interno di ogni ProfileImagePath.

Lo script
Nello script seguente ho mescolato un po' di tecniche diverse: WMI per scorrere il registro, WScript.Shell per leggere il registro in modalità non espansa (le variabili d'ambiente che contentono il percorso %USERPROFILE% escono tutte espanse con il profilo dell'utente che lancia lo script, e non va bene! Ovviamente non mi piace questo metodo, e spero di trovare una soluzione migliore), ed infine per caricare il file NTUSER.DAT in una chiave temporanea, non ho trovato nulla di meglio di lanciare dalla shell un buon REG LOAD...arh, brutto ma funzionale!
Fortuna che dopo qualche errore di codifica mi rifaccio, e per memorizzare l'elenco delle cartelle ho utilizzato un ArrayList, così come per le stringhe le espressioni regolari vanno sempre più che bene!

On Error Resume Next

Const HKEY_LOCAL_MACHINE = &H80000002
Const HKEY_USERS         = &H80000003

strComputer = "."

Set objRegistry = GetObject("winmgmts:\\" & strComputer & "\root\default:StdRegProv")
Set ws = CreateObject("Wscript.Shell")
Set temporaryFolders = CreateObject("System.Collections.ArrayList")
Set re = new RegExp

Function GetTemp(ProfilePath, K, Key)
On Error Resume Next
Temp = ws.RegRead(K & "\" & Key & "\Environment\TEMP")

If err.number = 0 Then
re.Pattern = "%USERPROFILE%"
Temp = re.Replace(Temp, ProfilePath)
Else
Temp = Null
End If

GetTemp = Temp
On Error Goto 0
End Function

strKeyPath = "SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList"
objRegistry.EnumKey HKEY_LOCAL_MACHINE, strKeyPath, arrSubkeys

For Each objSubkey In arrSubkeys
rem *** legge il percorso del profilo ***
strValueName = "ProfileImagePath"
strSubPath = strKeyPath & "\" & objSubkey
objRegistry.GetExpandedStringValue HKEY_LOCAL_MACHINE, strSubPath, strValueName, strProfile

rem *** legge il percorso della cartella TEMP ***
FolderTmp = GetTemp(strProfile, "HKEY_USERS", objSubkey)

if isNull(FolderTmp) Then
rem *** non ha letto la chiave, quindi prova a caricare il registro da NTUSER.DAT ***
ws.Run """%WINDIR%\System32\REG.EXE"" LOAD HKLM\TempHive """ & strProfile & "\NTUSER.DAT""", 7, true
msgbox "Leggo " & strProfile
FolderTmp = GetTemp(strProfile, "HKEY_LOCAL_MACHINE", "TempHive")
msgbox "Letto " & FolderTmp
ws.Run """%WINDIR%\System32\REG.EXE"" UNLOAD HKLM\TempHive", 7, true
End If
temporaryFolders.Add FolderTmp
Next

For Each folder In temporaryFolders
Wscript.Echo "Percorso temporaneo da svuotare: " & folder
Next

Come al solito il codice è brutto sporco e cattivo, inoltre in caso di errore.... be meglio verificare per bene prima di eliminare, non si sa mai! Ovviamente posso estendere questo codice, ed aggiungere i percorsi temporanei di Internet Explorer, Mozilla Firefox, oppure qualsiasi altro, basta solo conoscere la posizione di registro che indica il percorso dove sono salvati, oppure basta solo conoscere l'eventuale percorso del file di configurazione presente nel profilo.

Ah forse lo stesso risultato lo si poteva ottenere solamente con un file batch, qualche FOR e molte chiamate all'utility REG.EXE: sarebbe stato più coerente, ma sicuramente molto più illeggibile!

Attenzione
Nel caso in cui vengano utilizzati dei profili roaming, è sempre un rischio andare a toccare i profili salvati in locale, poiché si rischia di far perdere al sistema le informazioni di quale sia l'ultimo profilo utilizzato.

Paradossi della Statistica

Per confrontare la puntialità delle compagnie aeree, spesso vengono pubblicate delle statistiche che aiutano i viaggiatori a scegliere la compagnia migliore. La percentuale di puntualità di una compagnia aerea è definita come numero di voli con ritardi inferiori ai 15 minuti / numero voli totali: s embra ragionevole!

Vediamo un esempio di due compagnie aeree (tratto da un giornale fine anni '80): Nei totali, America West viene premiata (90% di aerei puntuali contro 87%), ma se ci soffermiamo sui dettagli, Alaska Airlines svogle un lavoro migliore presso ogni singolo aeroporto!

Perché questo paradosso? Alaska Airlines vola in proporzione molto di più verso aeroporti le cui condizioni atmosferiche sono spesso peggiori (come Seattle) mentre America West viaggia principalmente a Phoenix, dove il traffico è minore ed il tempo è molto più bello!

Tratto da "Introduction to Probability" lezione 10, dal sito http://ocw.mit.edu. E così ho provato anche ad includere un foglio di calcolo di google docs all'interno di un post (uhm non era difficile, comunque).

Conficker

Mentre molte aziende sono state colpite in maniera massiccia dall'infezione del worm Conficker, nella mia realtà il problema è passato quasi del tutto inosservato: d'altra parte i requisiti di sicurezza per evitare il propagarsi di un'infezione di questo tipo sono decisamente di base, e dovrebbe far riflettere il fatto che molte aziende che trattano dati personali o sensibili non siano state adeguatamente preparate ad una tale evenienza.

In ogni caso, è bene non abbassare la guardia, visto che il Conficker è un worm che non perdona alcuna leggerezza! Per tutte le informazioni relative al worm vedere questo articolo: "Considerazioni per un efficace contenimento dell'infezione Conficker.B".

Uno dei problemi principali consiste nell'individuare quali siano le macchine infette. Fortunatamente sono disponibili numerosi tool di scansione della rete in grado di rilevare eventuali sistemi infetti (vedere Conficker Remote Scanners), tra i quali è presente anche nmap. La sintassi per controllare una macchina od una rete remota è la seguente:

nmap -PN -T4 -p139,445 -n -v --script smb-check-vulns,smb-os-discovery --script-args safe=1 [targetnetworks]

Bisogna comunque tenere in considerazione il fatto che molti pc potrebbero essere spenti od essere collegati alla rete solo saltuariamente, e purtroppo avere un inventario esatti di tutti i pc che "mancano all'appello" non è sempre facile. Ho pensato quindi di far girare saltuariamente il seguente script su di un domain controller:

@echo off  
for /f "tokens=4 delims=: " %%a in ('netstat -na^|find "ESTABLISHED"') do (  
nmap -PN -T4 -p139,445 etc. %%a >> scan_%%a  
)

Questo script estrae tutti gli indirizzi IP che hanno una connessione correntemente aperta con il domain controller, ed effettua una scansione remota sugli indirizzi rilevati.

Certo il rischio è quello di effettuare numerose scansioni allo stesso indirizzo IP, ed il rischio è che alcune connessioni possano venire allegramente ignorate (meglio un windump piuttosto di un netstat, se non vogliamo farci sfuggire nulla). Naturalmente se sarà necessario, sono pronto a modificare lo script per renderlo più funzionale!

Ah un po' di informazioni sul comando for si trovano a questa pagina http://www.ss64.com/nt/for.html.