Logo Oracle Deutschland   DB Community  -  April 2016
Cloud Control: DBSNMP Accounts entsperren und neue Passwörter setzen
von Ralf Durben, Oracle Deutschland B.V. & Co. KG

Der Datenbankbenutzer DBSNMP wird in der Datenbank angelegt und benutzt, um dem Oracle Management Agent einen Zugriff für ein Monitoring zu ermöglichen. Das Passwort dieses Benutzers wird in Cloud Control für die jeweilige Datenbank als "Monitoring Credential" hinterlegt. Problematisch in der Handhabung dieses Datenbankbenutzers wird es immer dann, wenn dessen Passwort aufgrund der Passwort-Policy in der Datenbank abläuft. Der Agent versucht weiterhin, sich als DBSNMP anzumelden und so dauert es nicht lange, bis der Benutzer DBSNMP in der Datenbank gesperrt ist, also den Status "LOCK" hat.

Es ergeben in diesem Zusammenhang zwei Herausforderungen:

  • Einfaches Entsperren von DBSNMP Benutzern über Cloud Control
  • Vorsorgliches und regelmäßiges Ändern der Passwörter von DBSNMP Benutzern
In einer Umgebung mit vielen Zielsystemen ist dabei die GUI nicht praktikabel und so bietet sich EMCLI, das Kommandozeilen-Tool von Cloud Control an. In der Skripting Variante von EMCLI wird die Sprache Jython verwendet. Eine einfache Einführung in Jython für Anwender mit PL/SQL Kenntnissen wurde schon in einem älteren Tipp gegeben.

Auch das Ändern von Passwörtern für Datenbankbenutzer wurde schon in einem Tipp beschrieben. Der Einsatzfall für den Datenbankbenutzer DBSNMP ist aber besonders, da im Hintergrund der Agent kontinuierlich Anmeldeversuche unternimmt und dabei während des Prozesses der Passwortänderung den Benutzer DBSNMP wieder sperrt. Die Lösung besteht in einem mehrschrittigen Verfahren:
  • Temporäres Setzen eines Passworts für DBSNMP
  • Entsperren des Benutzers DBSNMP
  • Monitoring Credential auf neues Passwort setzen
  • Neues Passwort für Benutzer DBSNMP setzen, inklusive von Named Credentials in Cloud Control
  • Entsperren des Benutzers DBSNMP
Der erste Schritt des Setzens eines temporären Passworts ist notwendig, weil das endgültige Setzen des neuen Passworts inklusive Anpassung aller Named Credentials die Angabe des alten Passworts benötigt, und das ist typischerweise nicht zur Hand, wenn die Passwörter ständig verändert werden. Grundsätzlich ist es auch ratsam, dass die Passwörter von DBSNMP möglichst zufällig generiert werden, denn schließlich muß nur der jeweilige Agent das Passwort kennen. Aus diesem Grund stelle ich gleich ein Skript für EMCLI vor, in dem ein Zufallsstring als neues Passwort erzeugt wird. Dabei bekommt das temporäre Passwort immer das Zeichen "A" vorangestellt, damit im Kommando "ALTER USER..." das Passwort auch ohne Gänsefüsschen angegeben werden kann (bei einer führenden Ziffer gäbe es Probleme).

Das Skript unlock_dbsnmp_single.py entsperrt einen DBSNMP Account einer Datenbank, die sie angeben. Das Passwort wird zufällig neu gesetzt mit einer Zufallszeichenkette bestehend aus 15 Zeichen.

Die Länge des Passworts können Sie im Skript sehr leicht anpassen. Suchen Sie die Zeile "pwdlen = 15" und geben Sie eine neue Passwortlänge statt 15 an.

Zur besseren Darstellung sind im unteren Listing Zeilenumbrüche enthalten. Laden Sie das Skript hier herunter, um es nutzen zu können.
# -*- coding: utf-8 -*-

################################################################################
# emcli Jython script zum Entsperren des DBSNMP Accounts fuer eine             #
# Datenbank. Setzt auch ein neues Passwort fuer den DBSNMP Account             #
#                                                                              #
# Autor: Ralf Durben                                                           #
# Datum: 11.02.2016                                                            #
# Aenderungen:                                                                 #
################################################################################


## Notwendiger Import von Klassen
import sys
from emcli import *
import re
from xml.etree import ElementTree as ET
import os.path
import string
import cStringIO
import random
import socket
import oracle.sysman.emSDK.emCLI.Password as Password
import java.lang.String as String

## Prozedur zum Erzeugen eines Zufallsstrings
def getrstr(self, length):
    seq = string.letters + string.digits
    return ''.join(random.choice(seq) for _ in xrange(length))

## Prozedur zum Lesen von Daten (Sichtbar)
def get_normal_input(msg, default):
    v_input = raw_input(msg)

    if default != "" and v_input == "":
        v_input = default

    if v_input == "":
       print("Geben Sie einen nichtleeren Wert ein: ")
       v_input = get_normal_input(msg, default)

    return v_input

## Prozedur zum Lesen von Daten (mit Sternen maskiert für Passwoerter)
def get_password(msg):
 while(True):
   read = Password.readPassword(msg)
   password = String(read.getPassword())
   if str(password) == "":
      print("Geben Sie einen nichtleeren Wert ein: ")
   else:
      return str(password)

## Prozedur zum Ausführen von EMCLI Kommandos mit ordentlicher Ausgabe von Fehlermeldungen
def exec_emcli(commandstring):
 try:
  exec "ret = " + commandstring
  print ('Successfull')
 except emcli.exception.VerbExecutionError, e:
  print (str(e))
  
## Prozedur zum Anmelden an den OMS
def login_to_em():
 set_client_property('EMCLI_OMS_URL','https://sccloud049.de.oracle.com:7802/em')
 set_client_property('EMCLI_TRUSTALL','TRUE')
 set_client_property('EMCLI_OUTPUT_TYPE','JSON')
 omsusername = get_normal_input("\nOMS Login, Benutzername:[sysman] ","sysman")
 omspassword = get_password("OMS Login, Passwort    : ")
 try:
  ret = login(username=omsusername, password=omspassword)
  print("Logged in")
  return True
 except emcli.exception.VerbExecutionError, e:
  print (str(e))
  return False

## Prozedur zum Neusetzen des Passworts von DBSNMP
def set_pwd_dbsnmp (p_dbname, p_emdtyp):
 emdtyp = p_emdtyp
 dbname = p_dbname
 print("\n--------------------------------------------------------------")
 print("\n Arbeite fuer Datenbank " + dbname)
 print("\n--------------------------------------------------------------")
 pwdlen = 15
 temppwd = "A"+getrstr(pwdlen,pwdlen)
 newpwd = getrstr(pwdlen,pwdlen)

 command =  'execute_sql (sql="alter user dbsnmp identified by ' + temppwd + 
            ' account unlock;" ,targets="' + dbname + ':' + emdtyp + '")'
 exec_emcli (command)
 print ("\n Temp PWD gesetzt")

 command = 'set_monitoring_credential (target_name="' + dbname + '",target_type="' + 
            emdtyp + '",set_name="DBCredsMonitoring",cred_type="DBCreds",attributes="DBUserName:dbsnmp;DBPassword:' + 
            newpwd + ';DBRole:normal")'
 exec_emcli (command)
 print ("\n MonCred  gesetzt")

 command = 'update_db_password (target_name="' + dbname + 
           '",user_name="dbsnmp",change_all_references="yes",change_at_target="yes",old_password="' + 
           temppwd + '",new_password="' + newpwd + '",retype_new_password="' + newpwd +'")'
 exec_emcli (command)
 print ("\n New  PWD gesetzt")

 command =  'execute_sql (sql="alter user dbsnmp  account unlock;" ,targets="' + dbname + ':' + emdtyp + '")'
 exec_emcli (command)
 print ("\n DBSNMP UNLOCK")

 return dbname


###############################
# An OMS anmelden             #
###############################
while (login_to_em() == False):
  donothing=1

###########################################
# Fuer einzelne Datenbank                 #
###########################################
## Anzeige Targets vom Typ oracle_database
tlist = get_targets(targets="oracle_database")
for tlist in tlist.out()['data']:
     print(tlist['Target Name'] + "   " + tlist['Target Type'])

## Anzeige Targets vom Typ rac_database
tlist = get_targets(targets="rac_database")
for tlist in tlist.out()['data']:
     print(tlist['Target Name'] + "   " + tlist['Target Type'])

dbname = get_normal_input("\nEingabe des Datenbanknamens         : ","")
emdtyp = get_normal_input("\nEingabe des Types [oracle_database] : ","oracle_database")
ret = set_pwd_dbsnmp (dbname,emdtyp) 
Sie können ein solches Skript natürlich auch vorsorglich über alle Zielsysteme laufen lassen. Dann entfällt die Eingabe einer einzelnen Datenbank im unteren Teil des Skripts und der Aufruf von "set_pwd_dbsnmp" wird in einer Schleife für alle Targets ausgeführt. Als weitere Änderung wird überprüft, in welchem Status die Datenbank ist, und ob ein Zugriff überhaupt möglich ist. Auch werden alle ausgeführten Kommandos ausgedruckt, inklusive der Passwörter. Diese Zeilen dienen der Nachvollziehbarkeit dieses Skripts und sollten in der Produktion entfernt werden!

Wenn Sie dieses Skript regelmäßig ausführen, und das auch innerhalb der Gültigkeit von Passwörtern, haben Ihre DBSNMP Benutzer immer sichere Passwörter und werden nie gesperrt. Das Skript unlock_dbsnmp_all.py sieht wie folgt aus (wobei auch hier zur besseren Lesbarkeit einige Zeilenumbrüche eingebaut wurden. Eine lauffähige Version laden Sie hier herunter):
# -*- coding: utf-8 -*-

################################################################################
# emcli Jython script zum Entsperren des DBSNMP Accounts fuer alle             #
# Datenbanken, die in EM bekannt sind.                                         #
# Setzt auch ein neues Passwort fuer den DBSNMP Account                        #
#                                                                              #
# Autor: Ralf Durben                                                           #
# Datum: 11.02.2016                                                            #
# Aenderungen:                                                                 #
################################################################################


## Notwendiger Import von Klassen
import sys
from emcli import *
import re
from xml.etree import ElementTree as ET
import os.path
import string
import cStringIO
import random
import socket
import oracle.sysman.emSDK.emCLI.Password as Password
import java.lang.String as String

## Prozedur zum Erzeugen eines Zufallsstrings
def getrstr(self, length):
    seq = string.letters + string.digits
    return ''.join(random.choice(seq) for _ in xrange(length))

## Prozedur zum Lesen von Daten (Sichtbar)
def get_normal_input(msg, default):
    v_input = raw_input(msg)

    if default != "" and v_input == "":
        v_input = default

    if v_input == "":
       print("Geben Sie einen nichtleeren Wert ein: ")
       v_input = get_normal_input(msg, default)

    return v_input

## Prozedur zum Lesen von Daten (mit Sternen maskiert für Passwoerter)
def get_password(msg):
 while(True):
   read = Password.readPassword(msg)
   password = String(read.getPassword())
   if str(password) == "":
      print("Geben Sie einen nichtleeren Wert ein: ")
   else:
      return str(password)

## Prozedur zum Ausführen von EMCLI Kommandos mit ordentlicher Ausgabe von Fehlermeldungen
def exec_emcli(commandstring):
 try:
  exec "ret = " + commandstring
  print ('Successfull')
 except emcli.exception.VerbExecutionError, e:
  print (str(e))

## Prozedur zum Anmelden an den OMS
def login_to_em():
 set_client_property('EMCLI_OMS_URL','https://sccloud049.de.oracle.com:7802/em')
 set_client_property('EMCLI_TRUSTALL','TRUE')
 set_client_property('EMCLI_OUTPUT_TYPE','JSON')
 omsusername = get_normal_input("\nOMS Login, Benutzername:[sysman] ","sysman")
 omspassword = get_password("OMS Login, Passwort    : ")
 try:
  ret = login(username=omsusername, password=omspassword)
  print("Logged in")
  return True
 except emcli.exception.VerbExecutionError, e:
  print (str(e))
  return False

## Prozedur zum Neusetzen des Passworts von DBSNMP
def set_pwd_dbsnmp (p_dbname, p_emdtyp):
 emdtyp = p_emdtyp
 dbname = p_dbname
 print("\n--------------------------------------------------------------")
 print("\n Arbeite fuer Datenbank " + dbname)
 print("\n--------------------------------------------------------------")
 pwdlen = 15
 temppwd = getrstr(pwdlen,pwdlen)
 newpwd = getrstr(pwdlen,pwdlen)

 command =  'execute_sql (sql="alter user dbsnmp identified by ' + temppwd + 
            ' account unlock;" ,targets="' + dbname + ':' + emdtyp + '")'
 exec_emcli (command)
 print ("\n Temp PWD gesetzt")

 command = 'set_monitoring_credential (target_name="' + dbname + '",target_type="' + 
            emdtyp + '",set_name="DBCredsMonitoring",cred_type="DBCreds",attributes="DBUserName:dbsnmp;DBPassword:' + 
            newpwd + ';DBRole:normal")'
 exec_emcli (command)
 print ("\n MonCred  gesetzt")

 command = 'update_db_password (target_name="' + dbname + 
           '",user_name="dbsnmp",change_all_references="yes",change_at_target="yes",old_password="' + 
           temppwd + '",new_password="' + newpwd + '",retype_new_password="' + newpwd +'")'
 exec_emcli (command)
 print ("\n New  PWD gesetzt")

 command =  'execute_sql (sql="alter user dbsnmp  account unlock;" ,targets="' + dbname + ':' + emdtyp + '")'
 exec_emcli (command)
 print ("\n DBSNMP UNLOCK")

 return dbname


###############################
# An OMS anmelden             #
###############################
while (login_to_em() == False):
  donothing=1

###########################################
# Fuer alle Datenbanken                   #
###########################################

## Entsperren und Neusetzen fuer Targets vom Typ oracle_database
tlist = get_targets(targets="oracle_database")
for tlist in tlist.out()['data']:
     print(tlist['Target Name'] + "   " + tlist['Target Type'] +"   " + tlist['Status'])
     if tlist['Status'] == "Up":
        ret = set_pwd_dbsnmp (tlist['Target Name'],tlist['Target Type'])
     elif tlist['Status'] == "Down":
        print("\n--------------------------------------------------------------")
        print("\n " + tlist['Target Name'] + " ist nicht aufrufbar, versuche es trotzdem")
        print("\n--------------------------------------------------------------")
        ret = set_pwd_dbsnmp (tlist['Target Name'],tlist['Target Type'])
     elif tlist['Status'] == "Agent unreachable":
        print("\n--------------------------------------------------------------")
        print("\n " + tlist['Target Name'] + ": Agent ist nicht erreichbar, ueberspringe Target")
        print("\n--------------------------------------------------------------")
     else:
        donothing = 1


## Entsperren und Neusetzen fuer Targets vom Typ rac_database
tlist = get_targets(targets="rac_database")
for tlist in tlist.out()['data']:
     print(tlist['Target Name'] + "   " + tlist['Target Type'] +"   " + tlist['Status'])
     if tlist['Status'] == "Up":
        ret = set_pwd_dbsnmp (tlist['Target Name'],tlist['Target Type'])
     elif tlist['Status'] == "Down":
        print("\n--------------------------------------------------------------")
        print("\n " + tlist['Target Name'] + " ist nicht aufrufbar, versuche es trotzdem")
        print("\n--------------------------------------------------------------")
        ret = set_pwd_dbsnmp (tlist['Target Name'],tlist['Target Type'])
     elif tlist['Status'] == "Agent Unreachable":
        print("\n--------------------------------------------------------------")
        print("\n " + tlist['Target Name'] + ": Agent ist nicht erreichbar, ueberspringe Target")
        print("\n--------------------------------------------------------------")
     else:
        donothing = 1

Fazit

Mit dem oben geschilderten Weg können Sie schnell alle gesperrten DBSNMP Accounts entsperren, sowie neue Passwörter setzen.


Zurück zum Anfang des Artikels

Zurück zur Community-Seite