Category: System Administration

Fedora 26 => 27 & PHP

Since I like to discover major changes by upgrading my server and then realizing something doesn’t work (well “like” might be too strong a word … but I certainly do it) … I randomly upgraded to Fedora 27 without reading any documentation on changes. Aaaand we have PHP! Evidently mod_php has gone away and I’m going to have to figure out how to use FastCGI (php-fpm). Luckily there’s a quick way to switch back to mod_php in the interim:


LoadModule mpm_prefork_module modules/

# worker MPM: Multi-Processing Module implementing a hybrid
# multi-threaded multi-process web server
# See:
#LoadModule mpm_worker_module modules/

# event MPM: A variant of the worker MPM with the goal of consuming
# threads only for connections with active processing
# See:
#LoadModule mpm_event_module modules/


<IfModule !mod_php5.c>
<IfModule prefork.c>
LoadModule php7_module modules/

<IfModule !mod_php5.c>
<IfModule !prefork.c>
# ZTS module is not supported, so FPM is preferred
LoadModule php7_module modules/


Unable to ‘send as’ from Outlook With Exchange

I’ve had a confounding problem — we have sendmail magicing up millions of e-mail addresses for us, but occasionally we need to be able to send from one of these addresses too. I’ve got a web form that allows text-based messages (html or plain text), but I don’t want to figure out how to upload and attach images via a web form. Until I get around to updating the web form, I just set up a new Exchange mailbox and grant myself full access (which includes send as permission)

Except ever since we got always updating Office 2016, I’ve gotten nondelivery reports when I subsequently try to send from this new mailbox that claim I don’t have permission to send as the user in question. And I’ve verified my access three times. Even added explicit send-as in addition to full mailbox access.

I’ve finally discovered why I get this false error. The ‘from’ in Outlook allows free-form text which then may or may not resolve against an Exchange mailbox. And based on the permissions of the mailbox (or the lack of permissions of the non-resolved mailbox), it may or may not work. So I don’t have permission to send from newmailbox@ourdomain.ccTLD, I do have permission to send from the Exchange mailbox that happens to have that as its primary SMTP address. Sigh!.

When you use offline mode / cached Exchange mode, and an offline address list, the SMTP address doesn’t resolve out to that mailbox. And Exchange quite properly reports an error. To get the whole thing to work (assuming “wait until tomorrow” isn’t a good answer):

First, the offline address needs to be updated (either wait or hit the powershell management console on the server)

Update-OfflineAddressBook -Identity "Default Offline Address Book"

Secondly, Outlook needs to retrieve the updated address book. Within the Outlook client, use send/receive to update the address lists. Then you can send as the mailbox to which you have perfectly configured access.

WordPress Pages With Custom Info (SEO-type stuff)

I happened across a business who wanted to create several hundred unique WordPress pages so a long list of cities would have a “customized” page offering the service in that area. Makes sense, especially as an SEO endeavor since I search for ‘service city state’ fairly often when it is something I specifically want to obtain locally. Thing is, they were looking to pay someone to duplicate the post & manually edit each duplicate to use the individual locations. There’s a much easier way – the wp_insert_post function. Now it requires that you be able to execute PHP code either from the server’s command line (i.e. it’s your OS) or upload custom code (can be a WordPress plug-in, but it is easier if the code can be installed next to WordPress and called from its URL).

You need a variable for the template text – I wanted to include the location information in both the page title and page content, so I have a title variable as well. Include in the text some string that would never appear in your template (here, I used VARCSZ for variable containing city, state, and zip). Iterate through an array of locations and use str_replace to insert the individual locations into the title and content. Then create the page. Voila, 350 pages posted in a few minutes.

To create a single-column page (although different types of pages can be created by altering the $strContent variable):


$strTitle = 'Service Offered In VARCSZ';
$strContent = '<section id="builder-section-text_11" class="builder-section-first builder-section builder-section-text builder-section-last builder-text-columns-1" style="background-size: cover; background-repeat: no-repeat;background-position: center center;">
     <div class="builder-section-content">
     <div class="builder-text-row">
     <div class="builder-text-column builder-text-column-1" id="builder-section-text_11-column-1">
     <div class="builder-text-content">
     <p><b>Service Offered In VARCSZ</b></p>
     <p>And here is where we provide some information about the service we are offering, why you want this service, and what we do that is super awesome. </P>
     <p><b>More about our service in VARCSZ</b></p>
     <p>Info about our company and the service we provide in VARCSZ</p>
     <p><b>Call NOW for our service in VARCSZ</b></p>
     <p>For this service in VARCSZ, call us.</p>
     <p><b>Call 800-555-1212</b></p>

$strArrayOfLocations = array('Abington, PA 19001', 'Ambler, PA 19002', 'Ardmore, PA 19003', 'Bala Cynwyd, PA 19004', 'Huntingdon Valley, PA 19006', 'Bristol, PA 19007', 'Broomall, PA 19008', 'Bryn Athyn, PA 19009', 'Bryn Mawr, PA 19010', 'Cheltenham, PA 19012', 'Chester, PA 19013', 'Aston, PA 19014', 'Brookhaven, PA 19015', 'Chester, PA 19016', 'Chester Heights, PA 19017', 'Clifton Heights, PA 19018', 'Philadelphia, PA 19019', 'Bensalem, PA 19020', 'Croydon, PA 19021', 'Crum Lynne, PA 19022', 'Darby, PA 19023', 'Dresher, PA 19025', 'Drexel Hill, PA 19026', 'Elkins Park, PA 19027', 'Edgemont, PA 19028', 'Essington, PA 19029', 'Fairless Hills, PA 19030', 'Flourtown, PA 19031', 'Folcroft, PA 19032', 'Folsom, PA 19033', 'Fort Washington, PA 19034', 'Gladwyne, PA 19035', 'Glenolden, PA 19036', 'Glen Riddle, PA 19037', 'Glenside, PA 19038', 'Gradyville, PA 19039', 'Hatboro, PA 19040', 'Haverford, PA 19041', 'Holmes, PA 19043', 'Horsham, PA 19044', 'Jenkintown, PA 19046', 'Langhorne, PA 19047', 'Fort Washington, PA 19048', 'Fort Washington, PA 19049', 'Lansdowne, PA 19050', 'Lenni, PA 19052', 'Feasterville, PA 19053', 'Levittown, PA 19054', 'Levittown, PA 19055', 'Levittown, PA 19056', 'Levittown, PA 19057', 'Levittown, PA 19058', 'Garnet Valley, PA 19060', 'Marcus Hook, PA 19061', 'Media, PA 19063', 'Springfield, PA 19064', 'Media, PA 19065', 'Merion Station, PA 19066', 'Morrisville, PA 19067', 'Morton, PA 19070', 'Narberth, PA 19072', 'Newtown Square, PA 19073', 'Norwood, PA 19074', 'Oreland, PA 19075', 'Prospect Park, PA 19076', 'Ridley Park, PA 19078', 'Sharon Hill, PA 19079', 'Wayne, PA 19080', 'Swarthmore, PA 19081', 'Upper Darby, PA 19082', 'Havertown, PA 19083', 'Villanova, PA 19085', 'Wallingford, PA 19086', 'Wayne, PA 19087', 'Radnor, PA 19088', 'Radnor, PA 19089', 'Willow Grove, PA 19090', 'Media , PA 19091', 'Philadelphia , PA 19092', 'Philadelphia , PA 19093', 'Woodlyn, PA 19094', 'Wyncote, PA 19095', 'Wynnewood, PA 19096', 'Holmes , PA 19098', 'Philadelphia , PA 19099', 'Philadelphia, PA 19101', 'Philadelphia, PA 19102', 'Philadelphia, PA 19103', 'Philadelphia, PA 19104', 'Philadelphia, PA 19105', 'Philadelphia, PA 19106', 'Philadelphia, PA 19107', 'Philadelphia, PA 19108', 'Philadelphia, PA 19109', 'Philadelphia, PA 19110', 'Philadelphia, PA 19111', 'Philadelphia, PA 19112', 'Philadelphia, PA 19113', 'Philadelphia, PA 19114', 'Philadelphia, PA 19115', 'Philadelphia, PA 19116', 'Philadelphia, PA 19118', 'Philadelphia, PA 19119', 'Philadelphia, PA 19120', 'Philadelphia, PA 19121', 'Philadelphia, PA 19122', 'Philadelphia, PA 19123', 'Philadelphia, PA 19124', 'Philadelphia, PA 19125', 'Philadelphia, PA 19126', 'Philadelphia, PA 19127', 'Philadelphia, PA 19128', 'Philadelphia, PA 19129', 'Philadelphia, PA 19130', 'Philadelphia, PA 19131', 'Philadelphia, PA 19132', 'Philadelphia, PA 19133', 'Philadelphia, PA 19134', 'Philadelphia, PA 19135', 'Philadelphia, PA 19136', 'Philadelphia, PA 19137', 'Philadelphia, PA 19138', 'Philadelphia, PA 19139', 'Philadelphia, PA 19140', 'Philadelphia, PA 19141', 'Philadelphia, PA 19142', 'Philadelphia, PA 19143', 'Philadelphia, PA 19144', 'Philadelphia, PA 19145', 'Philadelphia, PA 19146', 'Philadelphia, PA 19147', 'Philadelphia, PA 19148', 'Philadelphia, PA 19149', 'Philadelphia, PA 19150', 'Philadelphia, PA 19151', 'Philadelphia, PA 19152', 'Philadelphia, PA 19153', 'Philadelphia, PA 19154', 'Philadelphia, PA 19155', 'Philadelphia, PA 19160', 'Philadelphia, PA 19161', 'Philadelphia, PA 19162', 'Philadelphia, PA 19170', 'Philadelphia, PA 19171', 'Philadelphia, PA 19172', 'Philadelphia, PA 19173', 'Philadelphia, PA 19175', 'Philadelphia, PA 19176', 'Philadelphia, PA 19177', 'Philadelphia, PA 19178', 'Philadelphia, PA 19179', 'Philadelphia, PA 19181', 'Philadelphia, PA 19182', 'Philadelphia, PA 19183', 'Philadelphia, PA 19184', 'Philadelphia, PA 19185', 'Philadelphia, PA 19187', 'Philadelphia, PA 19188', 'Philadelphia, PA 19190', 'Philadelphia, PA 19191', 'Philadelphia, PA 19192', 'Philadelphia, PA 19193', 'Philadelphia , PA 19194', 'Philadelphia , PA 19195', 'Philadelphia, PA 19196', 'Philadelphia, PA 19197', 'Philadelphia , PA 19244', 'Philadelphia , PA 19255', 'Paoli, PA 19301', 'Atglen, PA 19310', 'Avondale, PA 19311', 'Berwyn, PA 19312', 'Brandamore, PA 19316', 'Chadds Ford, PA 19317', 'Chatham, PA 19318', 'Cheyney, PA 19319', 'Coatesville, PA 19320', 'Cochranville, PA 19330', 'Concordville, PA 19331', 'Devon, PA 19333', 'Downingtown, PA 19335', 'Concordville , PA 19339', 'Concordville , PA 19340', 'Exton, PA 19341', 'Glen Mills, PA 19342', 'Glenmoore, PA 19343', 'Honey Brook, PA 19344', 'Immaculata, PA 19345', 'Kelton, PA 19346', 'Kemblesville, PA 19347', 'Kennett Square, PA 19348', 'Landenberg, PA 19350', 'Lewisville, PA 19351', 'Lincoln University, PA 19352', 'Lionville, PA 19353', 'Lyndell, PA 19354', 'Malvern, PA 19355', 'Mendenhall, PA 19357', 'Modena, PA 19358', 'New London, PA 19360', 'Nottingham, PA 19362', 'Oxford, PA 19363', 'Parkesburg, PA 19365', 'Pocopson, PA 19366', 'Pomeroy, PA 19367', 'Sadsburyville, PA 19369', 'Suplee, PA 19371', 'Thorndale, PA 19372', 'Thornton, PA 19373', 'Toughkenamon, PA 19374', 'Unionville, PA 19375', 'Wagontown, PA 19376', 'West Chester, PA 19380', 'West Chester, PA 19381', 'West Chester, PA 19382', 'West Chester, PA 19383', 'West Chester, PA 19388', 'West Grove, PA 19390', 'Westtown, PA 19395', 'Southeastern, PA 19397', 'Southeastern, PA 19398', 'Southeastern, PA 19399', 'Norristown, PA 19401', 'Norristown, PA 19403', 'Norristown, PA 19404', 'Bridgeport, PA 19405', 'King Of Prussia, PA 19406', 'Audubon, PA 19407', 'Eagleville, PA 19408', 'Fairview Village, PA 19409', 'Eagleville , PA 19415', 'Arcola, PA 19420', 'Birchrunville, PA 19421', 'Blue Bell, PA 19422', 'Cedars, PA 19423', 'Blue Bell , PA 19424', 'Chester Springs, PA 19425', 'Collegeville, PA 19426', 'Conshohocken, PA 19428', 'Conshohocken , PA 19429', 'Creamery, PA 19430', 'Devault, PA 19432', 'Frederick, PA 19435', 'Gwynedd, PA 19436', 'Gwynedd Valley, PA 19437', 'Harleysville, PA 19438', 'Hatfield, PA 19440', 'Harleysville , PA 19441', 'Kimberton, PA 19442', 'Kulpsville, PA 19443', 'Lafayette Hill, PA 19444', 'Lansdale, PA 19446', 'Lederach, PA 19450', 'Mainland, PA 19451', 'Mont Clare, PA 19453', 'North Wales, PA 19454', 'North Wales , PA 19455', 'Oaks, PA 19456', 'Parker Ford, PA 19457', 'Phoenixville, PA 19460', 'Plymouth Meeting, PA 19462', 'Pottstown, PA 19464', 'Pottstown, PA 19465', 'Royersford, PA 19468', 'Saint Peters, PA 19470', 'Sassamansville, PA 19472', 'Schwenksville, PA 19473', 'Skippack, PA 19474', 'Spring City, PA 19475', 'Spring House, PA 19477', 'Spring Mount, PA 19478', 'Uwchland, PA 19480', 'Valley Forge, PA 19481', 'Valley Forge, PA 19482', 'Valley Forge , PA 19483', 'Valley Forge, PA 19484', 'Valley Forge, PA 19485', 'West Point, PA 19486', 'King Of Prussia, PA 19487', 'Norristown, PA 19488', 'Norristown, PA 19489', 'Worcester, PA 19490', 'Zieglerville, PA 19492', 'Valley Forge , PA 19493', 'Valley Forge , PA 19494', 'Valley Forge , PA 19495', 'Valley Forge , PA 19496', 'Adamstown, PA 19501', 'Bally, PA 19503', 'Barto, PA 19504', 'Bechtelsville, PA 19505', 'Bernville, PA 19506', 'Bethel, PA 19507', 'Birdsboro, PA 19508', 'Blandon, PA 19510', 'Bowers, PA 19511', 'Boyertown, PA 19512', 'Centerport, PA 19516', 'Douglassville, PA 19518', 'Earlville, PA 19519', 'Elverson, PA 19520', 'Fleetwood, PA 19522', 'Geigertown, PA 19523', 'Gilbertsville, PA 19525', 'Hamburg, PA 19526', 'Kempton, PA 19529', 'Kutztown, PA 19530', 'Leesport, PA 19533', 'Lenhartsville, PA 19534', 'Limekiln, PA 19535', 'Lyon Station, PA 19536', 'Maxatawny, PA 19538', 'Mertztown, PA 19539', 'Mohnton, PA 19540', 'Mohrsville, PA 19541', 'Monocacy Station, PA 19542', 'Morgantown, PA 19543', 'Mount Aetna, PA 19544', 'New Berlinville, PA 19545', 'Oley, PA 19547', 'Pine Forge, PA 19548', 'Port Clinton, PA 19549', 'Rehrersburg, PA 19550', 'Robesonia, PA 19551', 'Shartlesville, PA 19554', 'Shoemakersville, PA 19555', 'Strausstown, PA 19559', 'Temple, PA 19560', 'Topton, PA 19562', 'Virginville, PA 19564', 'Wernersville, PA 19565', 'Womelsdorf, PA 19567', 'Reading, PA 19601', 'Reading, PA 19602', 'Reading, PA 19603', 'Reading, PA 19604', 'Reading, PA 19605', 'Reading, PA 19606', 'Reading, PA 19607', 'Reading, PA 19608', 'Reading, PA 19609', 'Reading, PA 19610', 'Reading, PA 19611', 'Reading, PA 19612', 'Reading, PA 19640');

echo "<ul>\n";
foreach($strArrayOfLocations as $strLocation){
     $strSEOTitle = str_replace(VARCSZ,$strLocation,$strTitle);
     $strSEOContent = str_replace(VARCSZ,$strLocation,$strContent);

     $postObject = array();
     $postObject['post_title'] = $strSEOTitle;
     $postObject['post_content'] = $strSEOContent;
     $postObject['post_status'] = 'publish';
     $postObject['post_author'] = 1;
     $postObject['post_type'] = 'page';
     $postObject['post_category'] = array(0);
     $iPostID = wp_insert_post( $postObject);
     echo "<li>$iPostID created for $strLocation</li>\n";
echo "</ul>\n";

Now if you wanted to get really fancy … add some code to list all of the city/state/zip combos for the country (or subset there-of). And for the other attributes you can set on a post, see

Extracting RPM Packages

I’ve encountered a few scenarios of late where I couldn’t install an RPM package but needed its content. One is the security config at work where I have sudo access for cp but not install rights. Sigh! But more recently, I needed to compare a library from an updated package to the currently installed one. Listing package content confirms it is the same file name and path.

[root@fedora02 tmp]# rpm -q --filesbypkg -p ./mariadb-libs-10.2.13-2.fc27.i686.rpm
mariadb-libs              /etc/my.cnf.d/client.cnf
mariadb-libs              /usr/lib/.build-id
mariadb-libs              /usr/lib/.build-id/7c
mariadb-libs              /usr/lib/.build-id/7c/c8e65deafbdcc28b3089da60f295a6f757cf4f
mariadb-libs              /usr/lib/


Extracting the rpm allowed me to actually compare the files, swap back and forth to see which worked, etc.

[lisa@fedora tmp]# rpm2cpio mariadb-libs-10.2.13-2.fc27.x86_64.rpm | cpio -idmv

WebLogic LDAP Authentication

Configuring an LDAP Authentication provider in WebLogic (version 11g used in this documentation)

  • In configuring LDAP authentication, I add a new authentication provider but continue to use the local provider for the system account under which WebLogic is launched. Partially because I don’t really use WebLogic (there’s an Oracle app with its own management site that runs within WebLogic – very small number of users, so our configuration is in no way optimized), but partially because using a network-sourced system account can prevent your WebLogic instance from launching. If your config isn’t right, or if the network is down, or a firewall gets in the way, or the LDAP server is down …. Your WebLogic fails to launch because its system ID is not validated.

WebLogic Configuration

Lock & Edit the site so we can make changes. On the left-hand pane, scroll down & find Security Realms

Go into your realm, select the “providers” tab. Supply a name for the provider (I included “LDAP” in the name to ensure it was clear which provider this was – may even want to specify something like “CompanyXLDAPAuthProvider”)

Select type “LDAPAuthenticator” for generic LDAP (I was using Sun DSEE, and moved to Oracle OUD without changing the authenticator type). Click OK to create.

Change the control flag on your default authenticator. Click the hyperlink for the default provider. On the “Common” tab, change the “Control Flag” to “SUFFICIENT” and save.

Click the hyperlink for the newly created provider. On the “Common” tab, change the “Control Flag” to “SUFFICIENT” and save.

Select the “Provider specific” tab.


Host:     <your LDAP server>

Port:      636

Principal:             <Your system account, provided when you request access to the LDAP directory>

Credentials:        <Your system account password>

Confirm Credentials:       <same as credentials>

SSLEnabled:        Check this box (for testing purposes, i.e. if you are unable to connect with these instructions as provided, you can set the port to 389 and not check this box to help with troubleshooting the problem. But production authentication needs to be done over SSL)


User Base DN:    <get this from your LDAP admin. Ours is “ou=people,o=CompanyX”)

All User Filter:    (&(objectClass=inetOrgPerson))

For applications with a single group restricting valid users, you can use the filter: (&(objectClass=inetOrgPerson)(isMemberOf=cn=GroupNameHere,ou=groups,o=CompanyX))

Users from name filter:  (&(uid=%u)(objectClass=inetOrgPerson))

User Search Type:                           subtree (onelevel may be fine, but verify with your LDAP administrator)

User Name Attribute:                     uid

User Object Class:                           inetOrgPerson

Use Retrieved User Name as Principal – I didn’t select this, don’t really know what it does


Group Base DN:               <another one to get from your LDAP admin. Ours is “ou=groups,o=CompanyX”>

All Groups Filter:              (&(objectClass=groupOfUniqueNames))

If your group names all have the same prefix, you could limit “all” groups to just your groups with a filter like (&(objectClass=groupOfUniqueNames)(cn=MyApp*))

Group from name filter: (&(cn=%g)(objectclass=groupofuniquenames))

Group search scope:                      subtree (again, onelevel may be fine)

Group membership searching:    <We select ‘limited’ because there are no nested groups in the LDAP directories. If you need to resolve nested group memberships, this and the next value will be different>

Max group membership search level:      0

Ignore duplicate membership:     Doesn’t really matter as we don’t have duplicates. I left this unchecked.

Static groups

Static group Attribute name:       cn

Static group Object Class:             groupOfUniqueNames

Static Member DN Attribute:       uniqueMember

Static Group DNs from Member filter:     (&(uniquemember=%M)(objectclass=groupofuniquenames))

Dynamic Groups              this section is left blank/defaults as we don’t use dynamic groups


Connection Pool Size:     Ideal value dependent on your anticipated application load – default of 6 is a good place to start.

Connect timeout:             Default is 0. I don’t know if this is something particular to WebLogic, but I generally use a 15 or 30 second timeout. If the server hasn’t responded in that period, it is not going to respond and there’s no need to hang the thread waiting.

Connection Retry Limit: Default is 1, this should be sufficient but if you see a lot of connection errors, either increase the connect timeout or increase this retry limit

Parallel Connect Delay:  0 (default) is fine

Result time limit:              0 (default) is OK. On my the LDAP server, there is no time limit for searches. Since WebLogic is making very simple searches, you could put a limit in here to retry any search that takes abnormally long

Keep Alive Enabled:         Please do not enable keep alive unless you have a specific need for it. Bringing up a new session uses slightly more time/resources on your app server than re-using an existing connection but that keep alive is a LOT of extra “hey, I’m still here” pings against the LDAP servers

Follow Referrals:              Un-check this box unless your LDAP admin tells you referrals are in use and should be followed.

Bind Anonymously on referrals:  Leave unchecked if you are not following referrals. If referrals are used and followed – ask the LDAP admin how to bind

Propagate cause for logon exception:      I check this box because I *want* the ugly LDAP error code that explains why the logon failed (49 == bad user/password pair; 19 == account locked out). But no *need* to check the box

Cache Related Settings:  This is something that would require more knowledge of WebLogic than I have 😊

If you enable caching, you may not see changes for whatever delta-time is the cache duration. So, the defaults of enabling cache & retaining it for 60 seconds wouldn’t really create a problem. If you set the cache duration to one day (a silly setting to make the problem cache can create clear) …. If I logged into your application at 2PM, did a whole bunch of work, went home, came back the next morning & saw my “your password is about to expire” warning … so go out to the password portal and change my password. Reboot, get logged back into my computer …. and try to access your application, I will get told my password is invalid. I could try again, even type what I *know* is my password into notepad & paste it into your app … still not able to log on. My old password, were I to try it, would work … but otherwise I’d have to wait until after 2PM before my new password would work.

Group membership changes could be a problem too – with the same 24 hour cache, if I am a valid user of your application who signs in at 2PM today, but my job function changes tomorrow morning & my access is revoked … I will still have application access until the cache expires. I am not sure if WebLogic does negative caching – basically if I am *not* a user, try to sign in and cannot because I lack the group membership & get an access request approved *really quickly* to become a group member, I may still be unable to access the application until the “Lisa is not a member of group XYZ” cache expires. If WebLogic does not do negative caching, then this scenario is not an issue.

So you might be able to lower utilization on your app server & my LDAP server by enabling cache (if your app, for instance, re-auths the object **each time the user changes pages** or something, then caching would be good). If you are just checking authentication and authorization on logon … probably not going to do much to lower utilization. But certainly keep the cache TTL low (like minutes, not days).

GUID Attribute:  nsUniqueID

Establishing The SSL Trust

For encryption to be negotiated with the LDAP servers, you need to have a keystore that includes the public keys from the CA used to sign the LDAP server cert. Obtain the base 64 encoded public keys either from the PKI admin or the LDAP admin. Place these file(s) on your server – I use the /tmp/ directory since they are no longer needed after import.

From the domain structure section, select: Environment=>Servers and select your server. On the “Configuration” tab, click the keystores sub-tab. If you are not already using a custom trust, you need to change they keystore type to use a custom trust (and specify a filename in a path to which the WebLogic account has access – keystore type is JKS and the password is whatever you are going to make the keystore password). If you *are* already using a custom trust, just record the file name of the custom trust keystore.

Use keytool to import the CA keys to the file specified in the custom trust. The following examples use a root and signing CA from my company, the CA chain which signs our LDAP SSL certs.

./keytool -import -v -trustcacerts -alias WIN-ROOT -file /tmp/WIN-ROOT-CA.b64 -keystore /path/to/the/TrustFile.jks -keypass YourKeystorePassword -storepass YourKeystorePassword

./keytool -import -v -trustcacerts -alias WIN-WEB -file /tmp/WIN-WEB-CA.b64 -keystore /path/to/the/TrustFile.jks -keypass YourKeystorePassword -storepass YourKeystorePassword

*** Under advanced, I had to check off “Use JSSE SSL” for SSL to work. Without that checked off, I got the following error in the log:

####<Feb 23, 2018 10:11:36 AM EST> <Notice> <Security> <> <AdminServer> <[ACTIVE] ExecuteThread: ’12’ for queue: ‘weblogic.kernel.Default (self-tuning)’> <<WLS Kernel>> <> <58b1979606d98df5:292a2ff6:161c336d0ba:-8000-0000000000000007> <1519398696289> <BEA-090898> <Ignoring the trusted CA certificate “CN=WIN-WEB-CA,DC=CompanyX,DC=com”. The loading of the trusted certificate list raised a certificate parsing exception PKIX: Unsupported OID in the AlgorithmIdentifier object: 1.2.840.113549.1.1.11.>

####<Feb 23, 2018 10:11:36 AM EST> <Notice> <Security> <> <AdminServer> <[ACTIVE] ExecuteThread: ’12’ for queue: ‘weblogic.kernel.Default (self-tuning)’> <<WLS Kernel>> <> <58b1979606d98df5:292a2ff6:161c336d0ba:-8000-0000000000000007> <1519398696289> <BEA-090898> <Ignoring the trusted CA certificate “CN=WIN-Root-CA”. The loading of the trusted certificate list raised a certificate parsing exception PKIX: Unsupported OID in the AlgorithmIdentifier object: 1.2.840.113549.1.1.11.>

An alternate solution would be to update your WebLogic instance – there are supposedly patches, but not sure which rev and it wasn’t worth trial-and-erroring WebLogic patches for my one WebLogic instance with a dozen users.

Whew, now save those changes. Activate changes & you will probably need to restart your WebLogic service to have the changes go into effect. You can go into the roles & add LDAP groups as — specifically, I added our LDAP group’s CN to the administrators WebLogic role.

Active Directory Federation Services (ADFS) Relying Party Trust Cert Expiry

At work, we received a critical ticket for an application that was unable to authenticate to ADFS. Nothing globally wrong – other applications are authenticating. A long call later, we discovered that the app’s certificate has expired. Why would the application not monitor their certificate expiry dates?? That’s an excellent question, but not one over which I have any control.

can monitor their certs on our side. So I wrote a quick powershell script to grab certificates from the relying party trusts and alerts us if any certs will be expiring in the next 30 days. It has to run on the ADFS server – I’d love to get it moved to the automation server in the future. I expect get-adfsrelyingpartytrust returns disabled agreements. I want to filter out disabled agreements.

Spectre & Meltdown

The academic whitepapers for both of these vulnerabilities can be found at — or El Reg’s article and their other article provide a good summary for those not included to slog through technical nuances. There’s a lot of talk about chip manufacturer’s stock drops and vendor patches … but I don’t see anyone asking how bad this is on hosted platforms. Can I sign up for a free Azure trial and start accessing data on your instance? Even if they isolate free trial accounts (and accounts given to students through University relationships), is a potential trove of data worth a few hundred bucks to a hacker? Companies run web storefronts that process credit card info, so there’s potentially profit to be made. Hell, is the data worth a few million to some state-sponsored entity or someone getting into industrial espionage? I’m really curious if MS uses the same Azure farms for their hosted Exchange and SharePoint services.

While Meltdown has patches (not such a big deal if you’re use cases are GPU intensive games, but does a company want a 30% performance hit on business process servers, automated build and testing machines, data mining servers?), Spectre patches turn IT security into TSA regulations. We can make a patch to mitigate the last exploit that occurred. Great for everyone else, but doesn’t help anyone who experienced that last exploit. Or the people about to get hit with the next exploit.

I wonder if Azure and AWS are going to give customers a 5-30% discount after they apply the performance reducing patch? If I agreed to pay x$ for y processing capacity, now they’re supplying 0.87y … why wouldn’t I pay 0.87x$?


My company held a ransomware response through experiment recently – and, honestly, every ransomware response I’ve seen has been some iteration of “walk through backups until we find good files”. Maybe use something like the SharePoint versioning to help identify a good target date (although that date may be different for different files … who knows!). But why wouldn’t you attempt a proactive identification of compromised files?

The basis of ransomware is that it encrypts data and you get the password after paying so-and-so a bitcoin or three. Considering that NGO virus authors (e.g. those who aren’t trying to slow down Iran’s centrifuges) are generally interested in creating mayhem. There’s not a lot of disincentive to creating mayhem and making a couple of bucks. I don’t anticipate ransomware to become less prevalent in the future; in fact I anticipate seeing it in vigilante hacking: EntityX gets their files back after they publicly donate 100k to their antithesis organisation.

Since it’s probably not going away, it seems worthwhile to immediately identify the malicious data scrambling. Reverting to yesterday’s backups sucks, but not as much as finding that your daily backups have aged out and you’re stuck with the monthly backup from 01 Nov as your last “good” data set. It would also be good to merge whatever your last good backup is into the non-encrypted files so the only ‘stuff’ that reverts is a worthless scramble of data anyway. Sure someone may have worked on the file this morning and sucks for them to find their work back-rev’d to last night … but again that’s better than everyone having to reproduce their last two and a half months of work.

Promptly identifying the attack: There are routine processes that read changed files. Windows Search indexing, antivirus scanner, SharePoint indexing. Running against the Windows Search index log on every computer in the organisation is logistically challenging. Not impossible, but not ideal either. A central log for enterprise AV software or the SharePoint indexing log, however, can be parsed from the data centre. Scrape the log files for “unable to read this encrypted file” events. Then there are a myriad of actions that can be taken. Alert the file owner and have them confirm the file should be encrypted. Alert the IT staff when more than x encrypted files are identified in a unit time. Check the create time-stamp and alert the file owner for any files that were created prior to encountering them as encrypted.

Restoring only scrambled files: Since you have a list of encrypted files, you have a scope for the restore job. Instead of restoring everything in place (because who has 2x the storage space to restore to an alternate location?!). Restore just the recently identified as encrypted files – to an alternate location or in place. Ideally you’ve gotten user input on the encrypted files and can omit any the user indicated they encrypted too.

Scraping OpenHAB Karaf Console Data

Realized an easier way of scraping the Karaf console output – no need to SSH into the console (which, evidently, can timeout for inactivity … something I sort on my OpenSSH server with a config parameter whenever I’m looking to use tee and scrape output).

You can just pipe the startup script to tee. Have to push stderr into stdout to get the *errors* logged.

./ 2>&1 | tee -a /tmp/logfile.txt

The output gets a little funky – maybe because of the color flags on some of the text? Dunno, but it’s grabbing the text and something like tail displays it without funky odd stuff

ESC[31m ESC[0m __ _____ ____ ESC[0m
ESC[31m ____ ____ ___ ____ ESC[0m/ / / / | / __ ) ESC[0m
ESC[31m / __ \/ __ \/ _ \/ __ \ESC[0m/ /_/ / /| | / __ | ESC[0m
ESC[31m/ /_/ / /_/ / __/ / / / ESC[0m__ / ___ |/ /_/ / ESC[0m
ESC[31m\____/ .___/\___/_/ /_/ESC[0m_/ /_/_/ |_/_____/ ESC[0m
ESC[31m /_/ ESC[0m 2.2.0-SNAPSHOTESC[0m
ESC[31m ESC[0m Build #1114 ESC[0m

Hit 'ESC[1m<tab>ESC[0m' for a list of available commands
and 'ESC[1m[cmd] --helpESC[0m' for help on a specific command.
Hit 'ESC[1m<ctrl-d>ESC[0m' or type 'ESC[1msystem:shutdownESC[0m' or 'ESC[1mlogoutESC[0m' to shutdown openHAB.


But you get the java exceptions too:

      Exception in thread "pool-45-thread-5" java.lang.NullPointerException
              at java.util.AbstractCollection.addAll(
              at com.zsmartsystems.zigbee.ZigBeeNode.setNeighbors(
              at com.zsmartsystems.zigbee.ZigBeeNetworkMeshMonitor$
              at java.util.concurrent.ThreadPoolExecutor.runWorker(
              at java.util.concurrent.ThreadPoolExecutor$


The Colloquial Occam’s Razor

Occam’s razor – it is futile to do with more things that which can be done with fewer – is colloquially rendered as “the simplest solution is the most likely”. We had multiple tickets opened today for authentication failures on an Apache web server. Each malfunctioning site uses LDAP authentication and authorization against an Oracle Unified Directory. Nothing in the error logs. The service account from the Apache configuration can log in and query the directory from the box using ldapsearch, so the account is valid and there is nothing in the OUD preventing access from this particular host.

That’s a puzzler, and I was about to take down a lot of web sites to reload the service with its log level set to debug. Not even sure what made me do it, but I went out to the groups and looked at their member lists. Oops. Something had gone wrong with the identity management platform and employee accounts had been cleared from the groups (all of the contractors were still members, which made it even stranger). Added a few people back into groups appropriate for their position, voila they could log into their site again.

No idea how the identity management group restored the memberships, but verifying people who should have been members (who had been members and had done nothing to remove their memberships) were actually members of the group saved a lot of time running through debug logs. Sometimes the simplest answer is the most likely.