Cybersecurity
DevOps Cloud
IT Operations Cloud
NetIQ has a new product called Identity Governance that has a lot of new features and functionality. There is much to learn about this tool.
In the first article in this series I started talking about the product, how to enable logging, and then identified the main configuration tools.
This time I want to talk a little bit about the configutil.sh file that is used to manage the Identity Governance side of the product. (This is as distinct from the OSP part, which is configured with configupdate.sh as before).
I had mentioned that configutil.sh (located and run as /opt/netiq/idm/apps/idgov/bin/configutil.sh -password IgOpsDBPassword ) is an enhancement on the configupdate.sh tool we were used to for OSP and the Identity Apps in IDM.
Both have an X-Windows based GUI and both have a console mode. However the Console modes are very different. The configupdate.sh console mode uses a series of menus, like dxcmd, for example. However configutil.sh gives you a command prompt where you execute commands.
For the most part it is optimized to execute commands, which manage key value pairs in the configuration.
The interface will look something like this:
[gcarman@idgovidlv001 logs]$ /opt/netiq/idm/apps/idgov/bin/configutil.sh -password MyPassword -console
Config Util
config>
Execute the command ?help and you see:
config> ?help
This is Cliche shell (https://forge.provo.novell.com/modules/xfmod/project/?idmcommon).
To list all available commands enter ?list or ?list-all, the latter will also show you system commands. To get detailed info on a command enter ?help command-name. Enter exit to close the app.
So let's try ?list-all:
config> ?list-all
abbrev name params
^rs ^run-script (filename)
^el ^enable-logging (fileName)
^dl ^disable-logging ()
^gle ^get-last-exception ()
^sdt ^set-display-time (do-display-time)
^e ^echo (message)
?la ?list-all ()
?ghh ?generate-HTML-help (file-name, include-prefixed)
?l ?list ()
?l ?list (startsWith)
?h ?help ()
?h ?help (command-name)
sbd set-backup-dir (dir)
om offline-mode (bool-indicator)
sp stop-polling ()
ap add-property (configType, key, value)
ap add-property (key, value)
ap add-property (configType, key, value, obscurity)
b backup (configType)
b backup ()
es export-sql (configType)
es export-sql ()
io is-offline ()
soc save-offline-changes ()
coc cancel-offline-changes ()
glbf get-last-backup-file ()
glsf get-last-sql-file ()
gbd get-backup-dir ()
sbfn set-backup-file-name (fileName)
gbfn get-backup-file-name ()
dc display-configs ()
dc display-configs (filter)
dc display-configs (filter, isRegexp)
ie import-external (configType, importFile)
apo add-property-only (key, value)
apo add-property-only (configType, key, value, obscurity)
apo add-property-only (configType, key, value)
lct list-config-types ()
sp set-property (key, value)
c clear (configType)
c clear ()
cp clear-property (configType, key)
cp clear-property (key)
The ?list command is slightly less verbose:
config> ?list
abbrev name params
sbd set-backup-dir (dir)
om offline-mode (bool-indicator)
sp stop-polling ()
ap add-property (configType, key, value)
ap add-property (key, value)
ap add-property (configType, key, value, obscurity)
b backup (configType)
b backup ()
es export-sql (configType)
es export-sql ()
io is-offline ()
soc save-offline-changes ()
coc cancel-offline-changes ()
glbf get-last-backup-file ()
glsf get-last-sql-file ()
gbd get-backup-dir ()
sbfn set-backup-file-name (fileName)
gbfn get-backup-file-name ()
dc display-configs ()
dc display-configs (filter)
dc display-configs (filter, isRegexp)
ie import-external (configType, importFile)
apo add-property-only (key, value)
apo add-property-only (configType, key, value, obscurity)
apo add-property-only (configType, key, value)
lct list-config-types ()
sp set-property (key, value)
c clear (configType)
c clear ()
cp clear-property (configType, key)
cp clear-property (key)
I used the 'es' command which dumped the configuration that is stored in the database, (export SQL) into a file, which was useful. As I noted in the previous article, but since it is not in the documentation anywhere I can find, it is very worth repeating, some of the configuration is stored in the ism-configuration.properties file (in tomcat/conf) but almost ten times as much (50K of content) is stored in the DB itself in a table called ISM_GLOBAL_CONFIG and the contents are imported from the global.properties file in the tomcat/conf directory.
Ok, the configuration is bifurcated. But how is it imported into the DB then? Well it turns out the installer, manages the global.properties file, and when it is complete, executes two scripts, that call configutil.sh with a script file parameter.
In fact, if you apply the 3.0.1 patch to Identity Governance, you are asked to run a similar command to apply some database changes. This is a much more flexible model than previously available.
Let's look at the two script files it imports, during the install.
You execute a script with a command of this format, using the example from the 3.01 patch:
/opt/netiq/idm/apps/idgov/bin/configutil.sh -password MyDBPassword -script /tmp/idg/identity-governance-3.0.1/additional/script/patch-301-configs.script
The contents of that file looks like:
# Additional properties introduced in the patch
add-property GLOBAL com.netiq.ar.cmpnt.api_server.svc.ids "EmailTemplateService"
add-property-only GLOBAL com.netiq.ar.cmpnt.api_server.svc.EmailTemplateService.type "DC_SPI"
add-property-only GLOBAL com.netiq.ar.cmpnt.api_server.svc.EmailTemplateService.heartBeatInterval "${com.netiq.ar.service.heartBeatInterval}"
add-property-only GLOBAL com.netiq.ar.cmpnt.api_server.svc.EmailTemplateService.path "/configuration"
add-property-only GLOBAL com.netiq.ar.cmpnt.api_server.svc.EmailTemplateService.isRestEndpoint "true"
This basically adds in some new settings into the Database.
The two files that run during the install you can find references in the /opt/netiq/idm/apps/idgov/logs which has the output from the install process in a series of files in:
config-create-log.txt
This shows:
Command: "/opt/netiq/idm/apps/jre/bin/java" -Djava.util.logging.config.file="/opt/netiq/idm/apps/idgov/conf/logging.properties" -Dcom.netiq.ism.config="/opt/netiq/idm/apps/idgov/conf/ig-configuration-init.properties" -jar "/opt/netiq/idm/apps/idgov/lib/ig-configutil.jar" -useDb false -script "/opt/netiq/idm/apps/idgov/scripts/init-configs.script"
Output:
Backup completed, GLOBAL config types backed up to /opt/netiq/idm/apps/idgov/conf/global.properties
Backup completed, NODE config types backed up to /opt/netiq/idm/apps/idgov/conf/ig-configuration-init.properties
Alas when I did my install, my database was incorrectly configured, so it failed and I had to run it again later, even though it seems like it thinks it worked.
If you look at the file below, it contains:
/opt/netiq/idm/apps/idgov/scripts/import-configs.script
# Clear existing GLOBAL values
clear GLOBAL
# Import base data
import-external GLOBAL "/opt/netiq/idm/apps/tomcat/conf/global.properties"
# Import governance propertiesimport-external NODE "/opt/netiq/idm/apps/idgov/conf/ig-configuration-init.properties"
# Import reporting propertiesimport-external NODE "/opt/netiq/idm/apps/idrpt/conf/rpt-configuration-init.properties"
So this script format is a series of commands, one per line. We see it first issues a clear command, to clear the GLOBAL values, which I assume are stored in the table ISM_GLOBAL_CONFIG and then it imports from an external file, to the GLOBAL location, the global.properties file.
You can also see they commented out the import of the ig-configuration-init.properties which is now moved into a second script that is called by the installer. That file, basically calls for the import of the ig-configuration-init.properties file which has about 22 lines of key value pairs.
The other log file shows much the same thing where a file is being read as a script that imports the settings.
All this is good to know and interesting to see how things are happening under the covers. Would have been nice if this was better explained elsewhere, but now you have some kind of reference.
In the /opt/netiq/idm/apps/idgov/logs directory, each of the DB creating commands is also logged. You will see files that look like:
-rwxrwxr-x. 1 novlua users 9349 Jul 2 15:18 checksums-log.txt
-rwxrwxr-x. 1 novlua users 512 Jul 2 15:18 config-combine-log.txt
-rwxrwxr-x. 1 novlua users 598 Jul 2 15:18 config-create-log.txt
-rwxr-xr-x. 1 root root 82728 Jul 2 15:19 Identity_Governance_InstallLog.log
-rwxrwxr-x. 1 novlua users 3456 Jul 2 15:19 liquibase-ara-log.txt
-rwxrwxr-x. 1 novlua users 3456 Jul 2 15:18 liquibase-dcs-log.txt
-rwxrwxr-x. 1 novlua users 3511 Jul 2 15:18 liquibase-ops-log.txt
-rwxrwxr-x. 1 novlua users 3448 Jul 2 15:18 liquibase-wf-log.txt
Looking at the liquibase-ops-log.txt file since it is representative, there is some interesting content.
Command: "/opt/netiq/idm/apps/jre/bin/java" -Djava.util.logging.config.file="/opt/netiq/idm/apps/idgov/conf/logging.properties" -DschemaName=igops -Dreport.viewer.role=IG_REPORT_ROLE -classpath "/opt/netiq/idm/apps/idgov/lib/msjdbc.jar":"/opt/netiq/idm/apps/idgov/lib/liquibase-core.jar":"/opt/netiq/idm/apps/idgov/lib/persistence.jar":"/opt/netiq/idm/apps/idgov/lib/persistutil.jar" liquibase.integration.commandline.Main --driver=com.microsoft.sqlserver.jdbc.SQLServerDriver --url="jdbc:sqlserver://myDBServer.acme.com:1433;databaseName=igops" --changeLogFile=IacOpsChangeLog.xml --username=igops --password=xxx --databaseClass=liquibase.database.ext.MSSQLUnicodeDatabase --logLevel=warning updateSQL
Unexpected error running Liquibase: com.microsoft.sqlserver.jdbc.SQLServerException: Login failed for user 'igops'. ClientConnectionId:4d308e2e-1851-4374-9138-23b0e7290f64
SEVERE 7/2/18 3:18 PM: liquibase: com.microsoft.sqlserver.jdbc.SQLServerException: Login failed for user 'igops'. ClientConnectionId:4d308e2e-1851-4374-9138-23b0e7290f64
liquibase.exception.DatabaseException: liquibase.exception.DatabaseException: com.microsoft.sqlserver.jdbc.SQLServerException: Login failed for user 'igops'. ClientConnectionId:4d308e2e-1851-4374-9138-23b0e7290f64
at liquibase.integration.commandline.CommandLineUtils.createDatabaseObject(CommandLineUtils.java:53)
at liquibase.integration.commandline.Main.doMigration(Main.java:849)
at liquibase.integration.commandline.Main.run(Main.java:170)
at liquibase.integration.commandline.Main.main(Main.java:89)
Caused by: liquibase.exception.DatabaseException: com.microsoft.sqlserver.jdbc.SQLServerException: Login failed for user 'igops'. ClientConnectionId:4d308e2e-1851-4374-9138-23b0e7290f64
at liquibase.database.DatabaseFactory.openConnection(DatabaseFactory.java:231)
at liquibase.database.DatabaseFactory.openDatabase(DatabaseFactory.java:141)
at liquibase.integration.commandline.CommandLineUtils.createDatabaseObject(CommandLineUtils.java:44)
... 3 more
Caused by: com.microsoft.sqlserver.jdbc.SQLServerException: Login failed for user 'igops'. ClientConnectionId:4d308e2e-1851-4374-9138-23b0e7290f64
at com.microsoft.sqlserver.jdbc.SQLServerException.makeFromDatabaseError(SQLServerException.java:259)
at com.microsoft.sqlserver.jdbc.TDSTokenHandler.onEOF(tdsparser.java:256)
at com.microsoft.sqlserver.jdbc.TDSParser.parse(tdsparser.java:108)
at com.microsoft.sqlserver.jdbc.SQLServerConnection.sendLogon(SQLServerConnection.java:4548)
at com.microsoft.sqlserver.jdbc.SQLServerConnection.logon(SQLServerConnection.java:3409)
at com.microsoft.sqlserver.jdbc.SQLServerConnection.accessLogonCommand.doExecute(SQLServerConnection.java:3373)
at com.microsoft.sqlserver.jdbc.TDSCommand.execute(IOBuffer.java:7344)
at com.microsoft.sqlserver.jdbc.SQLServerConnection.executeCommand(SQLServerConnection.java:2713)
at com.microsoft.sqlserver.jdbc.SQLServerConnection.connectHelper(SQLServerConnection.java:2261)
at com.microsoft.sqlserver.jdbc.SQLServerConnection.login(SQLServerConnection.java:1921)
at com.microsoft.sqlserver.jdbc.SQLServerConnection.connectInternal(SQLServerConnection.java:1762)
at com.microsoft.sqlserver.jdbc.SQLServerConnection.connect(SQLServerConnection.java:1077)
at com.microsoft.sqlserver.jdbc.SQLServerDriver.connect(SQLServerDriver.java:623)
at liquibase.database.DatabaseFactory.openConnection(DatabaseFactory.java:223)
... 5 more
For more information, use the --logLevel flag
---
Exit code: 255
So first up, my Database configuration was broken, and the connection failed. Once I figured all that out, I was able to grab the Command from this log file, to re-execute and properly set up the database.
/opt/netiq/idm/apps/jre/bin/java" -Djava.util.logging.config.file="/opt/netiq/idm/apps/idgov/conf/logging.properties" -DschemaName=igops -Dreport.viewer.role=IG_REPORT_ROLE -classpath "/opt/netiq/idm/apps/idgov/lib/msjdbc.jar":"/opt/netiq/idm/apps/idgov/lib/liquibase-core.jar":"/opt/netiq/idm/apps/idgov/lib/persistence.jar":"/opt/netiq/idm/apps/idgov/lib/persistutil.jar" liquibase.integration.commandline.Main --driver=com.microsoft.sqlserver.jdbc.SQLServerDriver --url="jdbc:sqlserver://myDBServer.acme.com:1433;databaseName=igops" --changeLogFile=IacOpsChangeLog.xml --username=igops --password=xxx --databaseClass=liquibase.database.ext.MSSQLUnicodeDatabase --logLevel=warning updateSQL
First thing to note is that the password is re-written to the literal string 'xxx', and if you wish to re-execute the command, of course, please remember to fix the xxx and replace with the real password.
This is a Java app, that runs, and uses a tool called Liquibase, (http://www.liquibase.org) that describes itself as:
Liquibase is an open-source database-independent library for tracking, managing and applying database schema changes. It was started in 2006 to allow easier tracking of database changes, especially in an agile software development environment
This is a great tool as it allows the developers to send us Database changes or configurations in a way they can manage. The database for the Identity Apps/User application use this tool as well.
The trick I found was figuring out where the referenced change log file is hidden, since it has the actual commands that get executed. The switch --changeLogFile=IacOpsChangeLog.xml defines it, but good luck finding the file. They are just files, in the IDMdb.jar file in the Identity Apps, not sure yet where they are hidden in Identity Governance.
The actual class that is run is "liquibase.integration.commandline.Main". It then executes the command updateSQL in my example. To actually update the database, execute 'update' instead of 'updateSQL'. If you use updateSQL it actually traces to standard out all the actions it takes, so it is pretty easy to get the list of things generated.
In my case, we were using a MS-SQL 2016 SP1 server (the supported version! I am told it is important to be at that minimal level, since some stuff that is needed is fixed in SP1) but this was the Dev instance and it was running as MS SQL Instance. Apparently, or so I am told, you would normally assign the instance to its own unique port value. But in this dev system they did not. Thus I started with this kind of connect string.
--url="jdbc:sqlserver://myDBServer.acme.com:1433;databaseName=igops"
Based on the information I had, this was correct. However because of the instance issue, I needed to specify a DB instance with the extra switch of instanceName=myInstance which would make you think you could use a connect string like:
--url="jdbc:sqlserver://myDBServer.acme.com:1433;instanceName=myInstance;databaseName=igops"
That too did not work. I finally found out that you can either have a port or an instance name specified, but not both. Who knew? Anyway, once that was determined the proper connect string below started to work.
--url="jdbc:sqlserver://myDBServer.acme.com;instanceName=myInstance;databaseName=igops"
Then we went and edited the tomcat/conf/server.xml file for all the <r;Resource>r; nodes and fixed the references so Tomcat could talk to the DB's properly. (We thought initially a special character in our password might have been the issue, but was not the case, so we also had to figure out how to redo the encrypted passwords, using the encrypt-password.sh script). As noted earlier, it looks like the passwords in ism-configuration.properties are configured with the same encryption model as well which is great and makes the whole thing much easier, re paste the same generated string into all occupancies of the password.
Of course figuring this out was painful as the error:
SEVERE 7/2/18 3:18 PM: liquibase: com.microsoft.sqlserver.jdbc.SQLServerException: Login failed for user 'igops'. ClientConnectionId:4d308e2e-1851-4374-9138-23b0e7290f64
liquibase.exception.DatabaseException: liquibase.exception.DatabaseException: com.microsoft.sqlserver.jdbc.SQLServerException: Login failed for user 'igops'. ClientConnectionId:4d308e2e-1851-4374-9138-23b0e7290f64
Alas it did not really tell us WHY it failed to log us in. Thus our snipe hunt down the password complexity (special characters not properly escaping or the like) path. But we got to see some interesting errors along the way. I should really start collecting Identity Governance errors for a series, but for now I think I will just keep writing about them as I find them.
You get all sorts of bad errors in the catalina.out log file for Tomcat when the database connection does not work. But the good news is that they are all pretty clear that the issue is with database connectivity. Very verbose errors, but they are pretty obvious to read.
One thing I did in troubleshooting that I thought would help, was noticing that the first line in the global.properties in the tomcat/conf directory was:
com.netiq.iac.database.type = MSSQL
So that seemed pretty important and seemed like something that ism-configuration.properties should have included. After all if you do not properly define the database type, how would you expect any connections to ever work.
In my attempts to fix that, I added the contents of global.properties (640 more lines) to the ism-configuration.properties (96 lines) thinking that the installer failed to properly copy the info over.
It was only later that I found out that in fact it was configutil.sh importing that global.properties file of key value pairs into the database, as discussed above.
Turns out, does not work when you have it in the ism-configuration.properties and not in the database. You do get further along, but not far enough.
It was explained to me, that since Identity Governance is designed to be clustered, there are really two types of settings. We have global settings, that apply to the entire cluster of servers. Every server shares these settings. Thus these are stored in the one place available to all nodes, the database itself. A previous approach, used in the Identity Apps in Identity Manager was to use objects under the User Application IDM driver in the directory (Configuration object under AppConfig in an XML blob) as the common shared place to store settings that applied to all nodes.
For Identity Governance, it is designed to be directory agnostic, and not require any particular directory service, so eDirectory as the shared storage location for the settings is not quite an option. Therefore the database seems like the obvious next place. Maintaining all the settings on each node would become unwieldy very quickly.
On the other hand there are local node settings, where each node might be configured slightly differently, perhaps a different certificate or different URL. Those settings are local to the server and thus stored in the file system. These are stored in Tomcat's ism-configuration.properties file locally.
The database connectivity information is set in the server.xml file, which allows enough connectivity to read the needed settings from the database.
I think that is enough for now, still have more stuff to talk about so stay tuned for further articles on this topic.