• Archives

  • Categories

Welding torch holders Posted on 06.02.2019 by greg.kuchyt

Just some built in-house torch holders I made for our MIG and TIG torches based off various versions built by others found via Google searches.

Process: Designed in Fusion 360 using the Sheet Metal module, cut on a CNC plasma table, bent by hand, abrasive blasted, spray painted, then the text/graphics were etched with a C02 laser.

Machining and fabricating work Posted on 04.03.2019 by greg.kuchyt

As I work more on metal working/machining projects I have started to document them. Maybe at some point it will serve as a lens to see skills progress and possibly an informational repository to keep track of drawings, etc.

This content will be under the Machining/fabrication page

Selenium Grid with remote ChromeDriver Posted on 07.13.2018 by greg.kuchyt

Trying to get Selenium Grid using standalone server 3.13 was kind of a pain. My Chrome remote webdriver calls would error with WebDriverException: Message: unknown error: DevToolsActivePort file doesn't exist.

The solution of adding –headless and –no-sandbox to the arguments to ChromeDriver is easily found with a basic Google search but implementing that in Python wasn’t exactly straight-forward. In my reading of the docs it seemed that adding an array to the capabilities dictionary keyed  as ‘args’ would pass those arguments on to the subsequent call to chromedriver. Testing showed that that was wrong.

Fortunately, the ChromeOptions object has a method that will convert it into a DesiredCapabilities object that can be passed to the webdriver.Remote instantiation. See below.

chrome_options = webdriver.ChromeOptions()
self.browser = webdriver.Remote(command_executor="http://seleniumhub.domain.fqdn:4444/wd/hub", desired_capabilities=chrome_options.to_capabilities())

Analysis of Raumer wedge bolts in schist Posted on 05.07.2017 by greg.kuchyt


A report from a Vermont climber about issues on the route War on Drugs at the 82 crag prompted inspection of the route’s fixed protection. While dealing with a separate set of problems, the last bolt identified as needing replacement was a 10mm Raumer double wedge bolt. These bolts were thought to be bomber in Northern Vermont schist along with their Fixe counterparts. What was troubling was the way in which the bolt was removed, or more accurately the ease with which it was removed. The manner and ease of removal indicated that the expansion mechanism of the bolt was in no way actually engaging or functioning. It is very troubling for a wedge bolt to be exhibiting behavior like this. Wedge bolts are often thought of as “fool” proof in that once they are installed in the hole, any attempt to remove them or load them will further engage the mechanical expansion effect keeping the bolt in the hole. The exception to this is in soft rock which typically does not allow for proper engagement of the expansion sleeves to achieve proper expansion.

Raumer double wedge; vintage early 2000s

Failure mode

These bolts can be quickly identified with a torque wrench. Attempting to tighten the bolt and hit the target torque value will yield a nut that turns endlessly until the nut bottoms out on the threads of the stud. The corollary to this statement is that tightening the nut is backing the bolt out of the hole! Once the threads are bottomed out is it a simple matter to lightly funk the bolt out with a cable and hammer or sometimes even remove it by hand if the beginning of the hole is sloppy. Similarly one can remove the bolt simply by adding a spacer to allow the nut to be repositioned to allow for additional displacement of the bolt body. The below video shows the state after the threads have been bottomed out and how easily the bolt is removed.

Additionally, the below composite photo of four different Raumer single and double wedge bolts shows the state of the expansion sleeves once the bolt have been removed from the hole. As well, the high-resolution photo on the right taken at home also lends credence to this point. Note that the expansion sleeve shows little evidence of having ever expanded in all of the photos.

After conferring with Greg Barnes at the ASCA and discussing the video, photos, and taking measurements of the hole and bolt, the evidence suggests that the expansion sleeve is never “grabbing” the sides of the hole and allowing the rest of the bolt to pass-through ,engaging the tapered cone into the sleeve. The thought process here is that rock is too soft and/or the expansion sleeve is too burly and requires too much force/friction than is present to properly engage. Instead the minimal compression fit of the oversized expansion sleeve is the only resistance met. It is likely that with a regular spanner wrench, the friction of the compression fit is mistaken for proper engagement and tightening of the bolt. However, with a torque wrench it is readily apparent that the bolt is not properly tightening.

This theory is consistent with other observed performance shortcomings of this model of bolt in soft rock [1] as well as the input of a prominent fixed anchor manufacturer.

The seemingly random distribution of failures is likely attributable to the highly variable quality of green schist, wherein a vein of softer rock is often encountered which is likely promoting this behavior. This would be consistent with previously observed and understood failings of wedge bolts in soft rock. This is also likely the reason double wedge bolt models have had the best track record in schist as these bolts are designed for specific types of concrete where it is likely to have large pebbles in the concrete that may be shattered by drilling leaving a void. The theory behind this bolt’s design is that two expansion points provide an assurance that one of the expansion sleeves will engage in the matrix of the concrete. For schist, substitute a muscovite vein for a shattered pebble and we likely have a similar situation in many of our cliffs.


As of this analysis, an inspection has been performed on approximately 95 bolts on 13 different routes, installed by three different developers on three different cliffs. Of these 95 inspected bolts, 22 have shown to be non-functional.

The failure rate of this sample pool breaks down to 23%.

Given the consistency of the condition of the bolt and the manner in which it is removed as well as the spatial distribution of occurrences, it is hard to argue that this trend would not apply to all installations of these bolts in Northern Vermont schist.

The list of routes inspected and number of bolts removed are provided below.

Route name Failed bolts
Doggfather 1
War on Drugs 1
Politics of Dancing 2
Crimp Chimp 4
Year of the Dog 3
Quills 1
Arms Reduction 4
Truffle Hog 1
Pussy Galore 1
Wandering the Halls 1
Playground Bully 1
Anything for an A 2

The Fixe bolts are likely unaffected by this particular problem as their expansion sleeve is significantly thinner and likely requires less “oomph” to engage. This is supported by tests of these bolts at 82 which showed all the protection bolts on Quills and Kid Charlemagne hitting specified torque easily. More confirmation is needed in order to dismiss this possibility however.

Proposed Action

Given the random distribution, likely the only way to identify these non-functional bolts is to “take off and nuke the entire site from orbit”, which is to say that each and every bolt should be torque tested to ensure proper function. Therefore it’ll be necessary to try and inventory all the routes that do or likely do have these bolts installed. Applying the roughly 25% failure rate to the total number of bolts will provide a ball park number of replacement bolts to ensure are available in order to effect the replacement of non-functional bolts as identified.


  1. http://routes.sydneyrockies.org.au/confluence/display/thelab/Trubolts+%28wedge+type+anchors%29
WebAuth wa_keyring import/export patch Posted on 06.07.2016 by greg.kuchyt

I had a need to export individual keys from the keyrings used by our WebKDC and main WebLogin servers to ensure that our dev pool and production pool had a common key from which to pivot around. This allowed the previous keys used to still be valid but also provide the shared key to cut-over to our next-gen servers. Digging into the keyring.c library I noticed that most of the framework was already in the library and that only a couple accessory functions needed to be written for the wa_keyring binary. The patch for this is below.

From d824d427eff88c318877d64e0dfe3b2a56e4191f Mon Sep 17 00:00:00 2001
From: Greg Kuchyt <gkuchyt@uvm.edu>
Date: Tue, 7 Jun 2016 08:44:37 -0400
Subject: [PATCH] Add export/import functionality of individual keys in wa_keyring

 tools/wa_keyring.c |   68 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 68 insertions(+), 0 deletions(-)

diff --git a/tools/wa_keyring.c b/tools/wa_keyring.c
index e78f7fb..44ad3b0 100644
--- a/tools/wa_keyring.c
+++ b/tools/wa_keyring.c
@@ -32,6 +32,7 @@ Usage: %s [-hv] -f <keyring> list\n\
   add <valid-after>                 # add a new random key\n\
+  export <id>                       # export key by id\n\
   gc <oldest-valid-after-to-keep>   # garbage collect old keys\n\
   list                              # list keys\n\
   remove <id>                       # remove key by id\n\
@@ -273,6 +274,60 @@ list_keyring(struct webauth_context *ctx, const char *keyring, bool verbose)
+ * Export the key at the given slot such that it can be imported into a
+ * keyring on a different server. Thus allowing keys to be synchronized
+ * across servers that have divergent key histories.
+ */
+static void
+export_key(struct webauth_context *ctx, const char *keyring, unsigned long n)
+    struct webauth_keyring *ring;
+    struct webauth_keyring *newring;
+    const char *newkeyring = "export_wakeyring";
+    struct webauth_keyring_entry *entry;
+    int s;
+    s = webauth_keyring_read(ctx, keyring, &ring);
+    if (s != WA_ERR_NONE)
+        die_webauth(ctx, s, "cannot read keyring %s", keyring);
+    entry = &APR_ARRAY_IDX(ring->entries, n, struct webauth_keyring_entry);
+    newring = webauth_keyring_new(ctx, 1);
+    webauth_keyring_add(ctx, newring, entry->creation, entry->valid_after, entry->key);
+    s = webauth_keyring_write(ctx, newring, newkeyring);
+    if (s != WA_ERR_NONE)
+        die_webauth(ctx, s, "cannot write new keyring %s", newkeyring);
+ * Import one keyring into another keyring. This allows keys from another
+ * server to be synchronized with servers that have a divergent key history.
+ */
+static void
+import_key(struct webauth_context *ctx, const char *keyring, const char *impkeyring) {
+    struct webauth_keyring *ring;
+    struct webauth_keyring *impring;
+    struct webauth_keyring_entry *impentry;
+    int s;
+    size_t i;
+    s = webauth_keyring_read(ctx, keyring, &ring);
+    if (s != WA_ERR_NONE)
+        die_webauth(ctx, s, "cannot read keyring %s", keyring);
+    s = webauth_keyring_read(ctx, impkeyring, &impring);
+    if (s != WA_ERR_NONE)
+        die_webauth(ctx, s, "cannot read keyring %s", impkeyring);
+    for (i = 0; i < (size_t) impring->entries->nelts; i++) {
+        impentry = &APR_ARRAY_IDX(impring->entries, i, struct webauth_keyring_entry);
+        webauth_keyring_add(ctx, ring, impentry->creation, impentry->valid_after, impentry->key);
+    }
+    s = webauth_keyring_write(ctx, ring, keyring);
+    if (s != WA_ERR_NONE)
+        die_webauth(ctx, s, "cannot write new keyring %s", keyring);
  * Add a new key to a keyring.  Takes the path to the keyring and the offset
@@ -418,6 +473,19 @@ main(int argc, char **argv)
         if (argc > 0)
         list_keyring(ctx, keyring, verbose);
+    } else if (strcmp(command, "export") == 0) {
+        if (argc != 1)
+            usage(1);
+        errno = 0;
+        id = strtoul(argv[0], &end, 10);
+        if (errno != 0 || *end != '\0')
+            die("invalid key id: %s", argv[0]);
+        export_key(ctx, keyring, id);
+    } else if (strcmp(command, "import") == 0) {
+        if (argc != 1)
+            usage(1);
+        errno = 0;
+        import_key(ctx, keyring, argv[0]);
     } else if (strcmp(command, "add") == 0) {
         if (argc != 1)
Patagonia Ascentionist ski-hook carry mod Posted on 03.07.2016 by greg.kuchyt

After getting into ski mountaineering races this year and sampling some of the specialized gear I found one of the things that I’d really like to have available in more pedestrian touring is a ski-hook carry system. For those unfamiliar with the concept, skimo race packs have a circular ring made of a stiff material on one side at the back of hip belt. On the other side of the pack on the shoulder strap there is a hook on an elastic cord. The tails of a ski are put in the ring until the heel posts catch on the ring, then the skis are swung across the back to the other side with the hook. The hook is pulled back over the shoulder and clipped to the skis. This is a quick, easy, and secure way to carry skis used in skimo races when skis need to be shouldered for a boot pack. So I wanted to put a similar system on the pack I usually use for skiing.

Building off the guide at SkinTrack, I made a few modifications to make things modular. Starting with the ski loop, I took an 18″ Voilé ski trap and zip-tied the working end of the strap so that it wouldn’t unbuckle. Next I zip-tied that to the hip belt in the area where the shoulder straps are stitched in because there is a small gap behind the buckle (see picture).

For the hook, I followed the directions at SkinTrack but made things a little easier by building a small bending jig with a 1/4″ spacer and used that to create the tie-off loops and the initial bend radius for the “claw” of the hook. After that I used a vise to do the other bends. I used 1/8″ shock cord and a cord toggle to attach the hook to the pack. I tied the shock cord around the hook’s eyes with a bowline with an overhand stopper then passed the other end of shock cord through the cord toggle. I then passed the end of the shock cord through the top most daisy chain loop on the shoulder straps used for the sternum strap and then back through the toggle. This allows you to adjust the total length of the ski hook and  more importantly the tension of the hook which makes it more versatile for a range of skis.

I’m going to try to find some slightly beefier shock cord, maybe 3/16″ because the 1/8″ is a little weak for this usage. If I decide on a specific length being adequate I’ll run the cord through some 9/16″ webbing and then put some shrink tubing over the hook eyes to help minimize the possibility the knot will come undone.

Testing PeopleSoft login with Webinject/Nagios Posted on 08.28.2015 by greg.kuchyt

We monitor various PeopleSoft environments and the ability to login to them with Webinject and Nagios. Recently after some updates on the PS side our login test were no longer working. When we performed our POST request with our login creds we were getting served a 200 to the sign-in page. This hinted at something missing in the request.

After some digging I identified that the PeopleSoft signin page is executing some javascript on form submission that sets a cookie in the browser before the POST request is built. This cookie is called PS_DEVICEFEATURES and it enumerates the capabilities of the client’s browser (e.g. width, height, pixel density, geolocation, canvas, etc). Basically it’s performing a bunch of things modernizr could do, but putting it in a cookie.

Since Webinject is not a browser it’s not actually processing the response and it can’t handle form events; ipso facto this cookie isn’t being set. As well, Webinject doesn’t provide a way to set a new cookie that isn’t provided in a response header Set-Cookie header.

So I hacked up a patch to Webinject that supports this

diff --git a/lib/Webinject.pm b/lib/Webinject.pm
index ddb43af..6dc801a 100644
--- a/lib/Webinject.pm
+++ b/lib/Webinject.pm
@@ -904,7 +904,18 @@ sub _http_defaults {
     my $request   = shift;
     my $useragent = shift;
     my $case      = shift;
+       if($case->{'addcookie'}) {
+               my $cookie_jar = $useragent->cookie_jar();
+               # add cookies to the cookie jar
+               # can add multiple cookies with a pipe delimiter
+               for my $addcookie (split /\|/mx, $case->{'addcookie'}) {
+                               my ($ck_version, $ck_key, $ck_val, $ck_path, $ck_domain, $ck_port, $ck_path_spec, $ck_secure, $ck_maxage, $ck_discard) = split(/,/, $addcookie);
+                               $cookie_jar->set_cookie( $ck_version, $ck_key, $ck_val, $ck_path, $ck_domain, $ck_port, $ck_path_spec, $ck_secure, $ck_maxage, $ck_discard);
+               }
+               $cookie_jar->save();
+               $cookie_jar->add_cookie_header($request);
+       }
     # add an additional HTTP Header if specified
     if($case->{'addheader'}) {
         # can add multiple headers with a pipe delimiter

This enables a new test case attribute entitled addcookie which takes a comma-delimited list of options that configure the cooke you wish to add. This patch is pretty bare bones and the fields in the addcookie list are the exact arguments needed to be passed on to the underlying HTTP::Cookies::set_cookie method with no type or error checking.

Example: addcookie="0,PS_DEVICEFEATURES,width:1680,/,host.domain.tld,0,1,1,86400,0"

Here’s the commit in the Webinject GitHub project

Installing Puppet Open Source on RHEL 6 Notes Posted on 07.15.2015 by greg.kuchyt

We’re just starting to get our feet wet with Puppet and we found the documentation woefully underwhelming. Here are our notes for getting a basic master/agent setup going with a “Hello world” manifest.

Basic Assumptions: SELinux is disabled

On your master

  • Install puppet and puppetserver packages
        yum install puppet puppetserver
  • Create the Puppet Master CA by running
        sudo puppet master --verbose --no-daemonize

    Once Notice: Starting Puppet master version appears, CTRL+C

  • Configure the following in the main/master section of /etc/puppet/puppet.conf
        dns_alt_names = host.fqdn.tld,alt_name
        environment_timeout = unlimited
        environmentpath = $confdir/environments
  • Create the following directories
    • /etc/puppet/environments/production
    • /etc/puppet/environments/production/manifests
    • /etc/puppet/environments/production/modules
  • Create /etc/puppet/environments/production/manifests/site.pp with the following
    notify {"Hello world!":}
  • Run service puppetserver start
  • Open 8140 on firewall rules

On your agent

  • Install puppet
  • Configure /etc/puppet/puppet.conf with the following
        server = hostname.fqdn.tld
        environment = production
  • Run puppet agent --test or start the puppet agent via service puppet start

On Master

  • Run puppet cert sign agenthostname.fqdn.tld

On agent

  • Run puppet agent --test and look for Hello world notice
WebAuth One-Way Trust patch Posted on 04.27.2015 by greg.kuchyt

The WebAuth WebKDC is able to get tickets in any realm it has a trust in but it is currently only able to verify credentials in one realm. In a two-way realm trust this works great. Realm A trusts tickets from Realm B, so if a principal in Realm B authenticates to Realm A’s WebKDC, the WebKDC in Realm A can get a TGT for Realm B. This breaks down when the trust is only one-way (i.e. Realm B trusts Realm A but Realm A does not trust Realm B).

At UVM we have a default realm for UVM and a new realm for former students. This separate realm prevents us from keeping users who should no longer be granted access to UVM realm resources in the UVM realm. There are however certain web application servrices (WASes) that we want to let both UVM and FORMERSTUDENT realm principals be able to access. In this case, FORMERSTUDENT trusts UVM but UVM doesn’t trust FORMERSTUDENT. Thus we have a WebKDC problem.

Since the WebKDC (in UVM) can currently only get tickets in its default realm, we wanted to instead be able to look at the WebKDC keytab and see if verification would be possible with any of the principals contained within the keytab. This is valid in Kerberos but just not in how WebAuth behaves. Thus a re-write of the credential verification process in WebAuth is required.

WebAuth opens the keytab specified in WebKDCKeytab and retrieves either the first principal in the keytab or the principal specified by the optional parameter to the WebKDCKeytab directive. So any patch will need to preserve the behavior of taking an optional specific principal and attempting verification with that. What we want is for the verification process to iterate through the keytab and see if there is a principal that matches the realm of the user principal. If there is, we then attempt verification and repeat if necessary until we’ve stepped through the keytab.

You can view the patch here for the one-way realm cred verification described above.

La Sportiva Storm Fighter GTX Pant Posted on 01.02.2015 by greg.kuchyt

The La Sportiva Storm Fighter GTX pant is a lightweight shell pant with active fit (i.e. not a baggy ski pant), Gore Tex Active fabric, and nearly-full side zips. I’ve been impressed with the Gore Tex Active material since I started using an Arc’teryx Beta FL a couple seasons ago (before Arc’teryx stopped using Gore Tex Active in their FL series). The thought of using a pant in the material is intriguing to me; perhaps even the solution to a layering problem I’ve been trying to solve. I was interested to get my hands on these pants and see if they would meet my needs and pass my picky standards for gear design.

The pants come in small/medium/large,etc sizing and are adjustable at the waist using two velcro tabs at the rear of the pant. The down size of this is that down-sizing the pants creates a pocket of material that could allow any snow to breach the waist hem of your upper layers to enter your pant more easily. The inseam in the smalls was approximately 32″ (I’m a 30×32 pant size). You can see in the photos how the pants fit over a pair of La Sportiva Nepal Primes. Additional minor details include some reflective highlights that won’t hurt if you spend some mornings skinning up groomers before the lifts start running.

The Storm Fighter pant is cut to fit over large boots (double mountain/ski) but not baggy like a contemporary ski pant. This is one of the selling points for me and for climbers looking for a hard shell pant. The pants feature an “almost” full zip, meaning that the zipper stops just below the waist so they can easily be put on over boots but not with skis or crampons still on. Each side’s zipper has two zips; one at the top and one at the bottom to adjust the pant as necessary. La Sportiva made an interesting and kind of cool design choice with the full zippers by sewing in a pocket at the top. So the first 4″ of unzipping the upper zip opens the hand pockets on either side of the pant. Combined with a rear zippered pocket this shell pant doesn’t mean you have to sacrifice pocket accessibility. Admittedly I’m not super literate on shell pant offerings, so perhaps this isn’t as novel as I may think, but I like it none-the-less.

The pants feature a snow gaiter that is separable allowing the pants to be put on over boots that are already on your feet. The closure on the snow gaiter has a top and bottom snap closure with velcro. A desired feature would be to make this snow gaiter removable if desired. While the bottom of the snow gaiter has a grippy rubber lining I personally would have liked to see the pants feature grommets or tie downs inside of the outer shell’s pant cuff as when approaching in deep snow I find I love this feature of my Patagonia soft shells (that are fitted with bungee cord stirrups). When climbing or skiing I remove the stirrup to allow pant mobility and access to my boots (re-lacing or switching from tour/ski). The pant cuffs also feature a large instep patch to protect the less durable Gore Tex Active from your crampon points or ski edges.

The major let down and the reason these pants won’t end up in my wardrobe is the zipper design. Near the pocket the zipper easily catches the fabric that is used to stitch the pocket in place. This is a frustrating problem that I seem to encounter in many outdoor products (this was the major design flaw in my opinion in the Mountain Hardwear Mountain Speed sleeping bag). All the more frustrating is a relatively simple solution of stitching in some nylon webbing in this area would prevent this from happening (look at the draft collar on Patagonia down jackets and Western Mountaineering sleeping bags). I don’t want to be trying to do my touring transition while dealing with stuck zippers, nor do I want to shred a $300 pair of shells because I’m ruefully trying to get my zippers opened/closed.

At $300 retail, one of the biggest selling points of this shell (in my opinion) is the material and the more form fitting cut followed by the full-zips. Since the zips don’t really function as I need them to, these pants fall short in what they were designed for. If the zippers were fixed, I think this would be a good piece for anyone looking to have bombproof protection from conditions yet still retain some layering flexibility. In short, a good first draft that needs some refining before I would put it in my closet. Your mileage may vary.