Month: May 2020

Garden Expansion

We built a pair of raised beds near our back woods — it was a nicely sheltered location, but too shady to grow much. The space is limited, too; and we wanted to have more garden space. The first step was going to be tilling, but we encountered an old tree stump under the grass. A good bit of excavation later, it was a pine tree with a very twisted root system. And several >3″ diameter roots running both horizontally into the garden area and down farther into the ground. Soooo … that’s a no on tilling a big patch to make a garden.

We have the blocks from the existing raised beds, so we’re creating four one-brick-high raised beds in the front yard. We’re going to compost grass clippings, leaves, kitchen scraps, etc between the beds and create hills for additional garden space next year. We’ll move the soil from the two raised beds into these new ones, and we’ve got some additional compost from the past year.

The first step is deconstructing the existing raised beds

And we’re constructing four new, lower, beds with the blocks

We’ve got two beds completed, and hauled about a third of the blocks for the remaining two

We’ve got to level off the geothermal hill — it’s been settling for a few years, and we can add compost on top if the area happens to fall farther. Unfortunately, a big storm blew in this evening … so we gathered up our stuff and ran for the house!

Pants?

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

  1. Cut the coffee filter into strips
  2. About 1” from the bottom of the strip, put a dot using one of the markers
  3. Skewer the strip at the top and hang over a glass of water
  4. Hang the strip over the glass
  5. Carefully fill the glass with water until it just touches the bottom of the coffee filter strip.
  6. 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.
  7. 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”?!