Wasteful coinputing

The Bitcoin network consumes a significant portion of world’s energy to produce nothing but hot air and lucky winners. What a waste.

Stop right there, OMG block chain is cool right ?! Sure, I find the concept of a transparent distributed block chain, cryptographic verification and decentralized consensus interesting theoretically, but this post has less to do with theory and more to do with unfortunate consequences of applying the tech in practice. The system just isn’t good enough when a huge energy waste is an important aspect of maintaining network trust, security and integrity.

So, what’s up with all those electrons driving the Bitcoin* business ? In short, Bitcoin mining nodes compete to produce the next valid global block of transactions by hashing some random number along with other permutable details. There are specific requirements to what makes the resulting hash number valid (level of difficulty), which makes the process computationally expensive. In other words, making a valid next block that the network can accept requires effort and time (typically a fixed average amount of time is targeted). So when a miner presents her shiny new block, its validity is proof of work (and proof of wasted energy). Checking for validity is easy, producing a valid block is hard. This throttles the rate at which new transaction blocks can be created, which is important to prevent hostile take-overs, spamming, etc. Defining the next block of facts cannot be free, otherwise, anyone could claim to hold the truth in a much too easy manner.

*..or whatever-coin with similar properties, really.

So, for the network to agree on new blocks, a lot of energy is required.  Clean energy ? Hardly, with over half of the world’s energy production coming from coal, oil and gas. Bitcoin mining is consuming power at the scale of entire countries. Terawatt-hours worth per year. Close to 500 kilowatt-hours per transaction, at the time of writing. And transactions are slow. And most of the computation being performed just goes to waste, as only one valid block will be the next on the chain, and all the losers just get a higher electricity bill.

A Norwegian company was called out in the media for terrorizing a whole neighbourhood with cooling machinery noise from a mining operation data center. Statements given by this useless business showed little respect for the people living nearby, a business driven by prospects of financial gain in the crypto coin market using subsidized electrical power. The same business is of course complaining about the Norwegian government considering exclusion of coin mining activities from data center power subsidization. The government absolutely should stop subsidizing such operations.

Stop the madness. If you’re in participating in this crypto currency scheme in the hope of becoming rich and “happy”, think about all the negative consequences and evident shortcomings of current applications.

Just /quit #Bitcoin.

References

  • https://digiconomist.net/bitcoin-energy-consumption
  • https://en.wikipedia.org/wiki/World_energy_consumption


Run shell script as different user with proper argument handling

So you have a shell script which needs to drop or modify its privileges by switching to an appropriate system user before continuing its execution.

Here are some alternatives to accomplish this while preserving all original command line arguments properly. Privileges are dropped by switching to the nobody user, adapt the RUN_AS variable as desired.

Alternative 1 – using only su, requires being root (uid 0) or the target user to run
#!/bin/sh

# This script must run as
RUN_AS=nobody

if [ `id -nu` != $RUN_AS ]; then
    if [ `id -u` -ne 0 ]; then
        echo >&2 "Sorry, you must be either root or $RUN_AS to run me."
        exit 1
    fi

    # This environment variable is just a safe guard for endless re-exec loop
    # and something the script can use to test up to this point if it has
    # dropped privileges by re-executing itself
    if [ "$EXEC_SU" ]; then
        echo >&2 "Re-exec loop circuit breaker engaged, something is wrong"
        exit 1
    fi

    exec su $RUN_AS -s /bin/sh -c "EXEC_SU=1 \"$0\" \"\$@\"" -- "$0" "$@"
fi

# At this point, we can be sure we are running as the desired user.
echo Running as `id -nu`
for arg in "$@"; do
    echo Argument $((n=n+1)): $arg
done

This alternative requires being root or the target user when invoking the script. Command line arguments are preserved after switching.

Alternative 2 – using sudo
#!/bin/sh

# This script must run as
RUN_AS=nobody

if [ `id -nu` != $RUN_AS ]; then
    if [ -z "$SUDO_EXEC" ]; then
        exec sudo -u $RUN_AS SUDO_EXEC=1 "$0" "$@"
    else
        echo >&2 'Re-exec loop circuit breaker engaged, something is wrong'
        exit 1
    fi
fi

# At this point, we can be sure we are running as the desired user.
echo Running as `id -nu`
for arg in "$@"; do
    echo Argument $((n=n+1)): $arg
done

Unlike alternative 1, this method does not require the script to be invoked by root or the target user directly, if switching to a system user that typically has no password set on its own. (Su will also prompt for password automatically, but that requires the target user password.)

Alternative 3 – when a clean login environment is required

If you require the script to run in a clean login environment for the target user, then things become slightly more complicated. It can be accomplished by combining sudo with su:

#!/bin/sh

# This script must run as
RUN_AS=nobody

if [ `id -nu` != $RUN_AS ]; then
    if [ -z "$SUDO_SU_EXEC" ]; then
        exec sudo su -s /bin/sh - $RUN_AS -c "SUDO_SU_EXEC=1 \"$0\" \"\$@\"" -- "$0" "$@"
    else
        echo >&2 'Re-exec loop circuit breaker engaged, something is wrong'
        exit 1
    fi
fi

# At this point, we can be sure we are running as the desired user.
echo Running as `id -nu`
echo USER=$USER HOME=$HOME 
for arg in "$@"; do
    echo Argument $((n=n+1)): $arg
done

Note that since the example above switches to nobody, which is a system user that by default does not have a shell configured, we explicitly set the shell using su argument "-s /bin/sh".

VirtualBox + Secure Boot + Ubuntu = fail

Here are the steps I did to enable VirtualBox to work properly in Ubuntu with UEFI Secure Boot fully enabled*. The problem is the requirement that all kernel modules must be signed by a key trusted by the UEFI system, otherwise loading will fail. Ubuntu does not sign the third party vbox* kernel modules, but rather gives the user the option to disable Secure Boot upon installation of the virtualbox package. I could do that, but then I would see an annoying “Booting in insecure mode” message every time the machine starts, and also the dual boot Windows 10 installation I have would not function.

*Ubuntu 16.04 on a Dell Latitude E7440 with BIOS A18, and with a dual boot Windows 10 installation.

Credit goes to the primary source of information I used to resolve this problem, which applies specifically to Fedora/Redhat:
http://gorka.eguileor.com/vbox-vmware-in-secureboot-linux-2016-update/

And a relevant Ask Ubuntu question:
http://askubuntu.com/questions/760671/could-not-load-vboxdrv-after-upgrade-to-ubuntu-16-04-and-i-want-to-keep-secur

Steps to make it work, specifically for Ubuntu/Debian

  1. Install the virtualbox package. If the installation detects that Secure Boot is enabled, you will be presented with the issue at hand and given the option to disable Secure Boot. Choose “No”.
  2. Create a personal public/private RSA key pair which will be used to sign kernel modules. I chose to use the root account and the directory /root/module-signing/ to store all things related to signing kernel modules.
    $ sudo -i
    # mkdir /root/module-signing
    # cd /root/module-signing
    # openssl req -new -x509 -newkey rsa:2048 -keyout MOK.priv -outform DER -out MOK.der -nodes -days 36500 -subj "/CN=YOUR_NAME/"
    [...]
    # chmod 600 MOK.priv
  3. Use the MOK (“Machine Owner Key”) utility to import the public key so that it can be trusted by the system. This is a two step process where the key is first imported, and then later must be enrolled when the machine is booted the next time. A simple password is good enough, as it is only for temporary use.
    # mokutil --import /root/module-signing/MOK.der
    input password:
    input password again:
  4. Reboot the machine. When the bootloader starts, the MOK manager EFI utility should automatically start. It will ask for parts of the password supplied in step 3. Choose to “Enroll MOK”, then you should see the key imported in step 3. Complete the enrollment steps, then continue with the boot. The Linux kernel will log the keys that are loaded, and you should be able to see your own key with the command: dmesg|grep 'EFI: Loaded cert'
  5. Using a signing utility shippped with the kernel build files, sign all the VirtualBox modules using the private MOK key generated in step 2. I put this in a small script /root/module-signing/sign-vbox-modules, so it can be easily run when new kernels are installed as part of regular updates:
    #!/bin/bash
    
    for modfile in $(dirname $(modinfo -n vboxdrv))/*.ko; do
      echo "Signing $modfile"
      /usr/src/linux-headers-$(uname -r)/scripts/sign-file sha256 \
                                    /root/module-signing/MOK.priv \
                                    /root/module-signing/MOK.der "$modfile"
    done
    
    # chmod 700 /root/module-signing/sign-vbox-modules
  6. Run the script from step 5 as root. You will need to run the signing script every time a new kernel update is installed, since this will cause a rebuild of the third party VirtualBox modules. Use the script only after the new kernel has been booted, since it relies on modinfo -n and uname -r to tell which kernel version to sign for.
  7. Load vboxdrv module and fire up VirtualBox:
    # modprobe vboxdrv

The procedure can also be used to sign other third party kernel modules, like the nvidia graphics drivers, if so is required. (I have not tested that myself.)