A friend pointed out that pants offer a little protection for me, but they are a lot of protection for you … So we can all stop wearing pants now, right? And protesting stores that require customers to wear pants! Freedom! Liberty!
Blueberries!
I’ve wanted to include a lot of permaculture in our home food production. Permaculture is basically creating a continuously agriculturally productive design. Trees, bushes, perennial plants are used to create a garden that is planted once and harvested for decades. I have some hazelnuts planted, and an apple tree from the locus year planting debacle. My idea is to add something every year instead of buying and planting everything in one season.
This year, I’m adding blueberries. In researching the purchase, we wanted to produce fruit this year (or, worst case, next). This meant buying 3 year-old plants. The local nurseries want 35$ per plant for 1-2 year old plants. Which … yeah, we’re not getting a lot of blueberries that way! I found a few places, though, that sell 3 year-old plants for about 14$ a plant (including shipping). I’ve got ten Hardyblue and ten Blueray plants ordered. We’ll have a lot of blueberries (although I’m sure we’ll lose some plants and won’t actually have 20).
In the future, I want to add pawpaw, raspberries, rhubarb, more apple trees, peach trees, and more hazelnuts.
LDAP Authentication and Authorization: PHP
Blah
<?php
error_reporting(0);
#=== FUNCTION ==================================================================
# NAME: ldapAuthenticationAndAuthorizationWithAttributes
# PARAMETERS:
# $strLDAPHost String LDAP Server URI
# $strUIDAttr String Schema attribute for user ID search
# $strSystemUser String System credential username
# $strSystemPassword String System credential password
# $strUserBaseDN String User search LDAP base DN
# $strLogonUserID String Input user ID
# $strLogonUserPassword String Input user password
# $arrayAttrsToReturn String Attributes to be returned
# $strGroupBaseDN String (optional) Group search LDAP base DN
# $strGroupNamingAttribute String (optional) Schema attribute for group search
# $strMembershipAttr String (optional) Schema attribute for group membership
# $strAuthGroup String (optional) Group name
# DESCRIPTION: Verify authentication and authorization against AD server.a
#
# RETURNS: array(BindReturnCode, Authorized, array(returnValues))
# BindReturnCode: -1 indicates LDAP connection failure, -2 indicates system account auth failed, -3 indicates user auth not attempted, >=0 is IANA-registered resultCode values (https://www.iana.org/assignments/ldap-parameters/ldap-parameters.xml#ldap-parameters-6)
# NOTE: 0 is successful authentication in IANA-registered resultCode
# Authorized: 0 authorization not attempted, -1 is not a member of the located group, 1 is member of the located group
# arrayUserAttributeValues Array with values of $arrayAttrsToReturn
#
# USAGE: $arrayUserAuthorized = ldapAuthenticationAndAuthorizationWithAttributes("ldaps://ad.example.com","sAMAccountName","ldapquery@example.com", "Sy5t3mP@ssw0rdG03sH3re", "ou=example,dc=example,dc=com", $strInputUserName, $strInputUserPassword, array('givenName', 'sn'), "ou=securitygroups,dc=example,dc=com","cn", "member", "LJRTestGroup")
#===============================================================================
function ldapAuthenticationAndAuthorizationWithAttributes($strLDAPHost,$strUIDAttr, $strSystemUser, $strSystemPassword, $strUserBaseDN, $strLogonUserID, $strLogonUserPassword, $arrayAttrsToReturn, $strGroupBaseDN=null, $strGroupNamingAttribute=null, $strMembershipAttr=null, $strAuthGroup=null){
$arrayAuthResults = array();
$arrayUserAttributeValues = array();
// Validate password is not null, otherwise directory servers implementing unauthenticated bind (https://tools.ietf.org/html/rfc4513#section-5.1.2) will return 0 on auth attempts with null password
if( strlen($strLogonUserPassword) < 1){
$arrayAuthResults['BindReturnCode'] = -3;
$arrayAuthResults['Authorized'] = -1;
}
else{
// Connect to the LDAP directory for system ID queries
$systemDS = ldap_connect($strLDAPHost);
ldap_set_option($systemDS, LDAP_OPT_PROTOCOL_VERSION, 3);
if ($systemDS) {
// Bind with the system ID and find $strLogonUserID FQDN
$systemBind = ldap_bind($systemDS, $strSystemUser, $strSystemPassword);
if(ldap_errno($systemDS) == 0){
$strLDAPFilter="(&($strUIDAttr=$strLogonUserID))";
$result=ldap_search($systemDS,$strUserBaseDN,$strLDAPFilter, $arrayAttrsToReturn);
$entry = ldap_first_entry($systemDS, $result);
$strFoundUserFQDN= ldap_get_dn($systemDS, $entry);
if($strFoundUserFQDN){
$userDS = ldap_connect($strLDAPHost);
ldap_set_option($userDS, LDAP_OPT_PROTOCOL_VERSION, 3);
$userBind = ldap_bind($userDS, $strFoundUserFQDN, $strLogonUserPassword);
$arrayAuthResults['BindReturnCode'] = ldap_errno($userDS);
ldap_close($userDS);
if($arrayAuthResults['BindReturnCode'] == 0){
$objFoundUser = ldap_get_entries($systemDS, $result);
for($arrayAttrsToReturn as $strAttributeName){
$arrayUserAttributeValues[$strAttributeName] = $objFoundUser[0][$strAttributeName];
}
$arrayAuthResults['AttributeValues'] = $arrayUserAttributeValues;
//////////////////////////////////////////////////////////////////////////////////////
// If an auth group has been supplied, verify authorization
//////////////////////////////////////////////////////////////////////////////////////
if($strAuthGroup){
// Escapes in DN need to be double-escaped or bad search filter error is encountered
$strGroupQuery = "(&($strGroupNamingAttribute=$strAuthGroup)($strMembershipAttr=" . str_replace("\\","\\\\", $strFoundUserFQDN) . "))";
$groupResult = ldap_search($systemDS,$strGroupBaseDN, $strGroupQuery);
$authorisedState = ldap_count_entries($systemDS ,$groupResult);
// If a group matching the filter is found, the user is authorised
if($authorisedState == 1){
$arrayAuthResults['Authorized'] = 1;
}
// Otherwise the user is not a member of the group and is not authorised
else{
$arrayAuthResults['Authorized'] = -1;
}
}
else{
$arrayAuthResults['Authorized'] = 0;
}
//////////////////////////////////////////////////////////////////////////////////////
ldap_close($systemDS);
}
// If the bind failed, the user has not logged in successfully so they cannot be authorized
else{
$arrayAuthResults['Authorized'] = -1;
ldap_close($systemDS);
ldap_close($userDS);
}
}
// User not found in directory
else{
$arrayAuthResults['BindReturnCode'] = 32;
$arrayAuthResults['Authorized'] = -1;
}
}
// system bind failed
else{
$arrayAuthResults['BindReturnCode'] = -2;
$arrayAuthResults['Authorized'] = -1;
ldap_close($systemDS);
}
}
// ldap connection failed
else{
$arrayAuthResults['BindReturnCode'] = -1;
$arrayAuthResults['Authorized'] = -1;
}
}
return $arrayAuthResults;
}
print "User password not supplied:\n";
$arrayNullPassword = array();
$arrayNullPassword = ldapAuthenticationAndAuthorizationWithAttributes("ldaps://ad.example.com","sAMAccountName","ldapquery@example.com", "Sy5t3mP@ssw0rdG03sH3re", "ou=example,dc=example,dc=com", "e0012345", '');
var_dump($arrayNullPassword);
print "Bad password:\n";
$arrayBadPassword = array();
$arrayBadPassword = ldapAuthenticationAndAuthorizationWithAttributes("ldaps://ad.example.com","sAMAccountName","ldapquery@example.com", "Sy5t3mP@ssw0rdG03sH3re", "ou=example,dc=example,dc=com", "e0012345", 'N0tTh3P@s5w0rd',"ou=SecurityGroups,dc=example,dc=com","cn", "member");
var_dump($arrayBadPassword);
print "\nInvalid user:\n";
$arrayUserNotInDirectory = array();
$arrayUserNotInDirectory = ldapAuthenticationAndAuthorizationWithAttributes("ldaps://ad.example.com","sAMAccountName","ldapquery@example.com", "Sy5t3mP@ssw0rdG03sH3re", "ou=example,dc=example,dc=com", "xe0012345", 'xDoesN0tM@tt3r');
var_dump($arrayUserNotInDirectory);
print "\nGood password without authorization:\n";
$arrayUserAuthenticated = array();
$arrayUserAuthenticated = ldapAuthenticationAndAuthorizationWithAttributes("ldaps://ad.example.com","sAMAccountName","ldapquery@example.com", "Sy5t3mP@ssw0rdG03sH3re", "ou=example,dc=example,dc=com", "e0012345", 'Us3rP@s5w0rdG035H3re|Us3rP@s5w0rdG035H3re');
var_dump($arrayUserAuthenticated);
print "\nGood password with authorized user:\n";
$arrayUserAuthorized = array();
$arrayUserAuthorized = ldapAuthenticationAndAuthorizationWithAttributes("ldaps://ad.example.com","sAMAccountName","ldapquery@example.com", "Sy5t3mP@ssw0rdG03sH3re", "ou=example,dc=example,dc=com", "e0012345", 'Us3rP@s5w0rdG035H3re|Us3rP@s5w0rdG035H3re',"ou=SecurityGroups,dc=example,dc=com","cn", "member", "cfyP_Unix_UnixUsers");
var_dump($arrayUserAuthorized);
print "\nGood password with unauthorized user:\n";
$arrayUserNotAuthorized = array();
$arrayUserNotAuthorized = ldapAuthenticationAndAuthorizationWithAttributes("ldaps://ad.example.com","sAMAccountName","ldapquery@example.com", "Sy5t3mP@ssw0rdG03sH3re", "ou=example,dc=example,dc=com", "e0012345", 'Us3rP@s5w0rdG035H3re|Us3rP@s5w0rdG035H3re',"ou=SecurityGroups,dc=example,dc=com","cn", "member", "WIN AM Team West");
var_dump($arrayUserNotAuthorized);
print "\nBad system account:\n";
$arrayBadSystemCred = array();
$arrayBadSystemCred = ldapAuthenticationAndAuthorizationWithAttributes("ldaps://ad.example.com","sAMAccountName","ldapquery@example.com", "xSy5t3mP@ssw0rdG03sH3re", "ou=example,dc=example,dc=com", "e0012345", 'Us3rP@s5w0rdG035H3re|Us3rP@s5w0rdG035H3re');
var_dump($arrayBadSystemCred);
?>
LDAP Authentication: PHP and Active Directory
This is a very brief function that authenticates a user against Active Directory. Because you can authenticate using a fully qualified DN, sAMAccountName, or userPrincipalName … there’s no need to use a system credential or search for the user provided you’ve got a single domain in your forest (i.e. you know what to prepend to the sAMAccountName or postpend to userPrincipalName).
If you need to perform authorization as well as authentication, you’ll need the user’s FQDN so use the generic LDAP authentication and authorization function.
<?php
error_reporting(0);
#=== FUNCTION ==================================================================
# NAME: activeDirectoryLDAPAuthentication
# PARAMETERS:
# $strLDAPHost String LDAP Server URI
# $strLogonUserID String Input user ID
# $strLogonUserPassword String Input user password
# DESCRIPTION: Verify authentication againt Active Directory server.
#
# RETURNS: int BindReturnCode: -2 indicates LDAP connection failure, -3 indicates user auth not attempted, >=0 is IANA-registered resultCode values (https://www.iana.org/assignments/ldap-parameters/ldap-parameters.xml#ldap-parameters-6)
# NOTE: 0 is successful authentication in IANA-registered resultCode
#
# USAGE: $iBindResult = activeDirectoryLDAPAuthentication("ldaps://ad.example.com", $strInputUserName, $strInputUserPassword)
#===============================================================================
function activeDirectoryLDAPAuthentication($strLDAPHost, $strLogonUserID, $strLogonUserPassword){
$iBindReturnCode = null;
// Validate password is not null, otherwise directory servers implementing unauthenticated bind (https://tools.ietf.org/html/rfc4513#section-5.1.2) will return 0 on auth attempts with null password
if( strlen($strLogonUserPassword) < 1){
$iBindReturnCode = -1;
}
else{
$userDS = ldap_connect($strLDAPHost);
if($userDS){
ldap_set_option($userDS, LDAP_OPT_PROTOCOL_VERSION, 3);
$userBind = ldap_bind($userDS, $strLogonUserID . '@example.com', $strLogonUserPassword);
$iBindReturnCode = ldap_errno($userDS);
ldap_close($userDS);
}
// ldap connection failed
else{
$iBindReturnCode = -2;
}
}
return $iBindReturnCode;
}
$iBadUser = activeDirectoryLDAPAuthentication("ldaps://ad.example.com", "xe0012345", 'N0tTh3P@s5w0rd');
print "\nInvalid user: $iBadUser\n";
$iUserAuthenticated = activeDirectoryLDAPAuthentication("ldaps://ad.example.com", "e012345", 'Go0dP@s5w0rdH3r3');
print "\nGood password: $iUserAuthenticated\n";
$iBadPassword = activeDirectoryLDAPAuthentication("ldaps://ad.example.com", "e0012345", 'N0tTh3P@s5w0rd');
print "\nBad password: $iBadPassword\n";
$iBadHost = activeDirectoryLDAPAuthentication("ldaps://abc.example.com", "e0012345", 'N0tTh3P@s5w0rd');
print "\nBad host: $iBadHost\n";
?>
Hops!
I ordered some hop plants from Great Lakes Hops — these are awesome. They ship actual plants, which are much more robust looking than the rhizomes I ordered years ago when we started growing hops. They came packed in what looks like wheat chaff – I assume it was moistened when they shipped the plants, but it was quite dry by the time I opened the box. The plants were a little wilted, but they perked right up when I got them into temporary pots with some more dirt and watered them. I love that the packing material can all be composted!
The best part? A free plant 🙂
We’ve added Triumph, a multiheaded Neomexicana that I knew as Medusa from a beer kit, and our freebie Sterling.
Bare Cupboards
I loathe how Trump is interviewed. Reporters let him blather on, throwing blame for our current situation without clarifying facts. David Muir interviewed Trump on the news tonight. Trump claims to have come into the office with the cupboards bare — in a really bad position, with broken tests, blah blah blah. And Muir pushed back a little — it’s the third year of Trump’s presidency … if he knew there were massive gaps in our preparedness, shouldn’t he have filled them by now?
Which is a decent question, The Trump administration’s own budget request from Feb 2020 (i.e. the budget submitted after reasonable people realized this virus was going to be a problem) didn’t ask for any increase in funding for the strategic national stockpile.
But the line of questioning doesn’t address the omitted facts from Trump’s original claim. Firstly, Obama didn’t leave Trump a stockpile of functional SARS-CoV-2 tests because the virus had not been encountered in humans yet. There are probably millions of viruses we’ve not yet encountered, and Obama didn’t use his vast psychic powers to order the Time Force to travel into the future and bring back a few million tests (and, really, I think Obama was clever enough he’d probably have ordered them to travel farther into the future and bring back a vaccine and manufacturing instructions). That’s an outright silly assertion.
There were supplies that the national stockpile lacked. Why? The Obama administration asked for 65 million dollars to increase the stock. Didn’t get it. Equipment was used during the swine flu outbreak, and Obama wanted a 10% budget increase to replenish the supply. Didn’t get it. There was a Republican push to reduce budgets across the board — remember the tea party? The CDC had budget increases due to biosecurity concerns after 9/11, so they were an obvious target. The cut-budgets-or-sequestration debt ceiling debacle — with the predictable result that no agreement could be made on targeted budget cuts — farther reduced CDC funding. While it’s technically true that the Obama administration reduced funding for the CDC, there’s a lot of duress that’s glossed over. And it’s not like the Republicans were sidelined as a minority screaming about how we needed to spend this money.
Alas, to Trump’s benefit and the detriment of politics in general … there’s very little interest in diving into the details. Democrats assume Trump is some combination of incompetent and dishonest, Republicans assume Trump’s right and it’s all Obama’s fault.
Ink Chromatography Experiment
Materials:
- Glass vessels – glass cup, graduated cylinder, etc
- Coffee filter
- Scissors
- Shish kabob skewers
- Water
- Colored markers – ideally include a few black markers from different companies
Process
- Cut the coffee filter into strips
- About 1” from the bottom of the strip, put a dot using one of the markers
- Skewer the strip at the top and hang over a glass of water
- Hang the strip over the glass
- Carefully fill the glass with water until it just touches the bottom of the coffee filter strip.
- It will take a few minutes for the water to move up the strip. Once the water has finished moving up the strip, take the skewer and strip off the glass. Empty the glass of water, then place the strip and skewer back on the glass to dry.
- Notice how different inks are made of different color combinations. Notice different inks carry different distances up the filter strip.
Biosecurity and a return to normal
I’ve been hearing a lot, lately, about the “return to normal” — what do you most want to do when we return to normal, when do you think we’ll be returning to normal, what changes do you think they’ll need to make before we can return to normal. And the questions strike me as wrong-footed. Especially as Trump and Pompeo talk about SARS-CoV-2 coming from a lab. Now “came from a lab” doesn’t necessitate malicious intent. The fundamental, longstanding problem I’ve had with gain of function research (the reason I wasn’t at all upset when the Obama administration put thought into the cost and benefits of this research and subsequently dropped government funding for this research and I didn’t think it was a stellar idea to resume funding) is that biosecurity is so difficult. And the spread of this virus highlights how vulnerable we were.
Sure, nation-states have forsworn biological warfare … but that’s not everyone. This release was probably accidental. I don’t say that because of any insider knowledge, but if I wanted to release an infectious disease … I’d have done a better job of infecting people. Get some infectious people at the Super Bowl – eating and drinking downtown, riding the public transit system, walking around the stadium. Or send people to ride mass transit in a few major cities – spend a day riding trains through Waterloo station, a day milling around Grand Central. If there are suicide bombers willing to literally blow themselves up for the cause … it seems like they’d be equally willing to inject themselves with some infectious disease. And the border agents can search whatever they want — the easiest thing in the world to ‘smuggle’ into a country is your own bloodstream. No explosive or drug sniffing dog is going to notice, no aeroport scanner will see anything because there’s basically nothing to find. Unless this is malicious intent with the forethought to make it look accidental (or a different actor framing the ‘obvious’ culprit) … it’s accidental.
The fact no one has done it yet is rather amazing. We’ve demonstrated our susceptibility to biological attack. We’re in the middle of demonstrating our unwillingness to take actions to prevent the spread of a disease. I absolutely believe this is an attack vector that will be exploited in the future. So why would we want to return to the previous “normal”?!
Energy Usage – Update
We’ve been tracking the energy draw of our various appliances since getting our geothermal system a few years ago. It was great to see the diminished usage with the geothermal system, but I’d like to reduce our usage farther. Not just to save money on our bill and reduce our impact, but reducing our draw will reduce the number of solar panels we’ll need to support our home.
The items we’ve audited so far account for 88.46% of our energy usage in the past year — the oven, cook-top, microwave, bathroom exhaust fan/heater, lighting (we installed LED bulbs several years ago), and misc small plug-in devices (tools, small appliances, laptops/tablets, phones) are 11.54% of our usage. While I’ll certainly make improvements wherever possible … I’ll see a bigger savings cutting the septic aerator usage in half than completely eliminating the untracked draw. The biggest savings comes from investigating the top of the list.
HVAC is still our biggest draw. We’ve dropped the temperature rather significantly in winter without seeing much decrease in usage, so just changing the thermostat isn’t a big win. I’ll be building a thermal imaging camera with a MLX90640 IR sensor — it’s a cool toy, and I’d be able to identify thermal leaks. Unfortunately, I picked a really bad time to look for thermal imaging bits. All of the temp checks we’re seeing to identify coronavirus infections? Thermal imaging everything is on backorder. I put my order in, so I’ve got a place in the queue.
The water heater … I’m going to spend more time investigating why our desuperheater doesn’t seem to do much. We’ve seen savings in the winter — when I’d expect to see the least benefit from the desuperheater — and nothing in the summer. I want to research insulating some of the water pipes and installing heat traps. And we’ll finally purchase the “smart” controller that hooks up to the WiFi and lets you drop the set-point on a schedule.
Next largest draw is an old refrigerator — that’s an easy change. We’ve got a newer unit that needs some repair. One year of energy savings will just about pay for the part, so it makes sense to retire the old fridge and bring the broken one back into service.
Those changes, plus fully shutting down the old server, should reduce our power consumption by about 4,500 kWh/year — a 450$ savings, and a significant reduction in solar system sizing.
| Item | kWh/year |
| HVAC | 4932 |
| Septic | 4668 |
| Water heater | 2676 |
| White refrigerator (1989) | 2250 |
| Car charging | 1164 |
| Black refrigerator | 1134 |
| New server | 1103 |
| Dryer | 245 |
| Family Room TV | 144 |
| Dishwasher | 118 |
| Bedroom air filter | 61 |
| Water pump | 53 |
| Washing Machine | 30 |
| Downstairs bedroom TV | 12 |
| Raven charging | 7 |
And, hopefully, we’ll finally hit the ‘efficient neighbors’ line 🙂
Oracle Database Uptime
Quick statement to find the uptime for an Oracle database
SELECT to_char(startup_time,'DD-MON-YYYY HH24:MI:SS') "DB Startup Time" FROM sys.v_$instance;



