CINXE.COM
Tamper-Evident Boot with Heads | Linux Journal
<!DOCTYPE html> <html lang="en" dir="ltr" prefix="content: http://purl.org/rss/1.0/modules/content/ dc: http://purl.org/dc/terms/ foaf: http://xmlns.com/foaf/0.1/ og: http://ogp.me/ns# rdfs: http://www.w3.org/2000/01/rdf-schema# schema: http://schema.org/ sioc: http://rdfs.org/sioc/ns# sioct: http://rdfs.org/sioc/types# skos: http://www.w3.org/2004/02/skos/core# xsd: http://www.w3.org/2001/XMLSchema# " class="no-js wf-loading"> <head> <meta charset="utf-8" /> <meta name="Generator" content="Drupal 9 (https://www.drupal.org)" /> <meta name="MobileOptimized" content="width" /> <meta name="HandheldFriendly" content="true" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <link rel="icon" href="/themes/linuxjournal/favicon.ico" type="image/vnd.microsoft.icon" /> <link rel="canonical" href="https://www.linuxjournal.com/content/tamper-evident-boot-heads" /> <link rel="shortlink" href="https://www.linuxjournal.com/node/1340426" /> <title>Tamper-Evident Boot with Heads | Linux Journal</title> <link rel="stylesheet" media="all" href="/core/modules/system/css/components/align.module.css?smwnh4" /> <link rel="stylesheet" media="all" href="/core/modules/system/css/components/fieldgroup.module.css?smwnh4" /> <link rel="stylesheet" media="all" href="/core/modules/system/css/components/container-inline.module.css?smwnh4" /> <link rel="stylesheet" media="all" href="/core/modules/system/css/components/clearfix.module.css?smwnh4" /> <link rel="stylesheet" media="all" href="/core/modules/system/css/components/details.module.css?smwnh4" /> <link rel="stylesheet" media="all" href="/core/modules/system/css/components/hidden.module.css?smwnh4" /> <link rel="stylesheet" media="all" href="/core/modules/system/css/components/item-list.module.css?smwnh4" /> <link rel="stylesheet" media="all" href="/core/modules/system/css/components/js.module.css?smwnh4" /> <link rel="stylesheet" media="all" href="/core/modules/system/css/components/nowrap.module.css?smwnh4" /> <link rel="stylesheet" media="all" href="/core/modules/system/css/components/position-container.module.css?smwnh4" /> <link rel="stylesheet" media="all" href="/core/modules/system/css/components/progress.module.css?smwnh4" /> <link rel="stylesheet" media="all" href="/core/modules/system/css/components/reset-appearance.module.css?smwnh4" /> <link rel="stylesheet" media="all" href="/core/modules/system/css/components/resize.module.css?smwnh4" /> <link rel="stylesheet" media="all" href="/core/modules/system/css/components/sticky-header.module.css?smwnh4" /> <link rel="stylesheet" media="all" href="/core/modules/system/css/components/system-status-counter.css?smwnh4" /> <link rel="stylesheet" media="all" href="/core/modules/system/css/components/system-status-report-counters.css?smwnh4" /> <link rel="stylesheet" media="all" href="/core/modules/system/css/components/system-status-report-general-info.css?smwnh4" /> <link rel="stylesheet" media="all" href="/core/modules/system/css/components/tablesort.module.css?smwnh4" /> <link rel="stylesheet" media="all" href="/core/modules/system/css/components/tree-child.module.css?smwnh4" /> <link rel="stylesheet" media="all" href="/modules/contrib/poll/css/poll.base.css?smwnh4" /> <link rel="stylesheet" media="all" href="/modules/contrib/poll/css/poll.theme.css?smwnh4" /> <link rel="stylesheet" media="all" href="/core/modules/views/css/views.module.css?smwnh4" /> <link rel="stylesheet" media="all" href="/libraries/shariff/shariff.complete.css?smwnh4" /> <link rel="stylesheet" media="all" href="/modules/contrib/webform/modules/webform_bootstrap/css/webform_bootstrap.css?smwnh4" /> <link rel="stylesheet" media="all" href="/themes/linuxjournal/css/style.css?smwnh4" /> <link rel="stylesheet" media="all" href="/themes/linuxjournal/css/fonts.css?smwnh4" /> <!-- <script defer src="https://use.fontawesome.com/releases/v5.0.9/js/all.js" integrity="sha384-8iPTk2s/jMVj81dnzb/iFR2sdA7u06vHJyyLlAd4snFpCl/SnyUjRrbdJsw1pGIl" crossorigin="anonymous"></script>--> <script> // Non-blocking webfonts. (function iife() { 'use strict'; // JS is able to start executing. document.documentElement.classList.remove('no-js'); // Optimization for Repeat Views // Stop early for repeat views that have already cached fonts. if (sessionStorage.fontsLoadedLateef && sessionStorage.fontsLoadedOpensans /* && sessionStorage.fontsLoadedNEWFONT */) { document.documentElement.classList.remove('wf-loading'); document.documentElement.classList.add('wf-opensans'); document.documentElement.classList.add('wf-lateef'); // document.documentElement.classList.add('wf-NEWFONT'); return; } // Inlined Font Face Observer script + Promise polyfill. /*! Font Face Observer v2.0.13 - © Bram Stein. License: BSD-3-Clause */ (function(){'use strict';var f,g=[];function l(a){g.push(a);1==g.length&&f()}function m(){for(;g.length;)g[0](),g.shift()}f=function(){setTimeout(m)};function n(a){this.a=p;this.b=void 0;this.f=[];var b=this;try{a(function(a){q(b,a)},function(a){r(b,a)})}catch(c){r(b,c)}}var p=2;function t(a){return new n(function(b,c){c(a)})}function u(a){return new n(function(b){b(a)})}function q(a,b){if(a.a==p){if(b==a)throw new TypeError;var c=!1;try{var d=b&&b.then;if(null!=b&&"object"==typeof b&&"function"==typeof d){d.call(b,function(b){c||q(a,b);c=!0},function(b){c||r(a,b);c=!0});return}}catch(e){c||r(a,e);return}a.a=0;a.b=b;v(a)}} function r(a,b){if(a.a==p){if(b==a)throw new TypeError;a.a=1;a.b=b;v(a)}}function v(a){l(function(){if(a.a!=p)for(;a.f.length;){var b=a.f.shift(),c=b[0],d=b[1],e=b[2],b=b[3];try{0==a.a?"function"==typeof c?e(c.call(void 0,a.b)):e(a.b):1==a.a&&("function"==typeof d?e(d.call(void 0,a.b)):b(a.b))}catch(h){b(h)}}})}n.prototype.g=function(a){return this.c(void 0,a)};n.prototype.c=function(a,b){var c=this;return new n(function(d,e){c.f.push([a,b,d,e]);v(c)})}; function w(a){return new n(function(b,c){function d(c){return function(d){h[c]=d;e+=1;e==a.length&&b(h)}}var e=0,h=[];0==a.length&&b(h);for(var k=0;k<a.length;k+=1)u(a[k]).c(d(k),c)})}function x(a){return new n(function(b,c){for(var d=0;d<a.length;d+=1)u(a[d]).c(b,c)})};window.Promise||(window.Promise=n,window.Promise.resolve=u,window.Promise.reject=t,window.Promise.race=x,window.Promise.all=w,window.Promise.prototype.then=n.prototype.c,window.Promise.prototype["catch"]=n.prototype.g);}()); (function(){function l(a,b){document.addEventListener?a.addEventListener("scroll",b,!1):a.attachEvent("scroll",b)}function m(a){document.body?a():document.addEventListener?document.addEventListener("DOMContentLoaded",function c(){document.removeEventListener("DOMContentLoaded",c);a()}):document.attachEvent("onreadystatechange",function k(){if("interactive"==document.readyState||"complete"==document.readyState)document.detachEvent("onreadystatechange",k),a()})};function r(a){this.a=document.createElement("div");this.a.setAttribute("aria-hidden","true");this.a.appendChild(document.createTextNode(a));this.b=document.createElement("span");this.c=document.createElement("span");this.h=document.createElement("span");this.f=document.createElement("span");this.g=-1;this.b.style.cssText="max-width:none;display:inline-block;position:absolute;height:100%;width:100%;overflow:scroll;font-size:16px;";this.c.style.cssText="max-width:none;display:inline-block;position:absolute;height:100%;width:100%;overflow:scroll;font-size:16px;"; this.f.style.cssText="max-width:none;display:inline-block;position:absolute;height:100%;width:100%;overflow:scroll;font-size:16px;";this.h.style.cssText="display:inline-block;width:200%;height:200%;font-size:16px;max-width:none;";this.b.appendChild(this.h);this.c.appendChild(this.f);this.a.appendChild(this.b);this.a.appendChild(this.c)} function t(a,b){a.a.style.cssText="max-width:none;min-width:20px;min-height:20px;display:inline-block;overflow:hidden;position:absolute;width:auto;margin:0;padding:0;top:-999px;white-space:nowrap;font-synthesis:none;font:"+b+";"}function y(a){var b=a.a.offsetWidth,c=b+100;a.f.style.width=c+"px";a.c.scrollLeft=c;a.b.scrollLeft=a.b.scrollWidth+100;return a.g!==b?(a.g=b,!0):!1}function z(a,b){function c(){var a=k;y(a)&&a.a.parentNode&&b(a.g)}var k=a;l(a.b,c);l(a.c,c);y(a)};function A(a,b){var c=b||{};this.family=a;this.style=c.style||"normal";this.weight=c.weight||"normal";this.stretch=c.stretch||"normal"}var B=null,C=null,E=null,F=null;function G(){if(null===C)if(J()&&/Apple/.test(window.navigator.vendor)){var a=/AppleWebKit\/([0-9]+)(?:\.([0-9]+))(?:\.([0-9]+))/.exec(window.navigator.userAgent);C=!!a&&603>parseInt(a[1],10)}else C=!1;return C}function J(){null===F&&(F=!!document.fonts);return F} function K(){if(null===E){var a=document.createElement("div");try{a.style.font="condensed 100px sans-serif"}catch(b){}E=""!==a.style.font}return E}function L(a,b){return[a.style,a.weight,K()?a.stretch:"","100px",b].join(" ")} A.prototype.load=function(a,b){var c=this,k=a||"BESbswy",q=0,D=b||3E3,H=(new Date).getTime();return new Promise(function(a,b){if(J()&&!G()){var M=new Promise(function(a,b){function e(){(new Date).getTime()-H>=D?b():document.fonts.load(L(c,'"'+c.family+'"'),k).then(function(c){1<=c.length?a():setTimeout(e,25)},function(){b()})}e()}),N=new Promise(function(a,c){q=setTimeout(c,D)});Promise.race([N,M]).then(function(){clearTimeout(q);a(c)},function(){b(c)})}else m(function(){function u(){var b;if(b=-1!= f&&-1!=g||-1!=f&&-1!=h||-1!=g&&-1!=h)(b=f!=g&&f!=h&&g!=h)||(null===B&&(b=/AppleWebKit\/([0-9]+)(?:\.([0-9]+))/.exec(window.navigator.userAgent),B=!!b&&(536>parseInt(b[1],10)||536===parseInt(b[1],10)&&11>=parseInt(b[2],10))),b=B&&(f==v&&g==v&&h==v||f==w&&g==w&&h==w||f==x&&g==x&&h==x)),b=!b;b&&(d.parentNode&&d.parentNode.removeChild(d),clearTimeout(q),a(c))}function I(){if((new Date).getTime()-H>=D)d.parentNode&&d.parentNode.removeChild(d),b(c);else{var a=document.hidden;if(!0===a||void 0===a)f=e.a.offsetWidth, g=n.a.offsetWidth,h=p.a.offsetWidth,u();q=setTimeout(I,50)}}var e=new r(k),n=new r(k),p=new r(k),f=-1,g=-1,h=-1,v=-1,w=-1,x=-1,d=document.createElement("div");d.dir="ltr";t(e,L(c,"sans-serif"));t(n,L(c,"serif"));t(p,L(c,"monospace"));d.appendChild(e.a);d.appendChild(n.a);d.appendChild(p.a);document.body.appendChild(d);v=e.a.offsetWidth;w=n.a.offsetWidth;x=p.a.offsetWidth;I();z(e,function(a){f=a;u()});t(e,L(c,'"'+c.family+'",sans-serif'));z(n,function(a){g=a;u()});t(n,L(c,'"'+c.family+'",serif')); z(p,function(a){h=a;u()});t(p,L(c,'"'+c.family+'",monospace'))})})};"object"===typeof module?module.exports=A:(window.FontFaceObserver=A,window.FontFaceObserver.prototype.load=A.prototype.load);}()); // Load and observe Lateef var Lateef = new FontFaceObserver('Lateef', {weight: 400}); Promise.all([Lateef.load()]).then(function() { document.documentElement.classList.remove('wf-loading'); document.documentElement.classList.add('wf-lateef'); // Optimization for Repeat Views // Set a flag in localstorage so repeat views can skip processing FFO. sessionStorage.fontsLoadedLateef = true; }); // Load and observe Open Sans var os300 = new FontFaceObserver('Open Sans', {weight: 300}); var os400 = new FontFaceObserver('Open Sans', {weight: 400}); Promise.all([os300.load(), os400.load()]).then(function() { document.documentElement.classList.remove('wf-loading'); document.documentElement.classList.add('wf-opensans'); // Optimization for Repeat Views // Set a flag in localstorage so repeat views can skip processing FFO. sessionStorage.fontsLoadedOpensans = true; }); // Load and observe NEWFONT /* var NEWFONT = new FontFaceObserver('NEWFONT', {weight: 400}); Promise.all([NEWFONT.load()]).then(function() { document.documentElement.classList.remove('wf-loading'); document.documentElement.classList.add('wf-NEWFONT'); // Optimization for Repeat Views // Set a flag in localstorage so repeat views can skip processing FFO. sessionStorage.fontsLoadedNEWFONT = true; }); */ })(); </script> <script src="https://www.google.com/recaptcha/api.js" async defer></script> <script src=https://slashdot.org/country.js></script> <script src="/themes/linuxjournal/js/ada.js"></script> <script src="/themes/linuxjournal/js/accessibility.js"></script> <script>window.addEventListener('load', function() { new Accessibility(); });</script> <script src="//a.fsdn.com/con/js/sftheme/cmp2.js"></script> <link rel="stylesheet" href="//a.fsdn.com/con/css/sftheme/sandiego/cmp.css" type="text/css"> <link rel="stylesheet" href="/themes/linuxjournal/css/ccpa.css" type="text/css"> <script>window.bizx.cmp.init({ geo: window });</script> <script> function bm_trace() { (function (w,d,t) { _ml = w._ml || {}; _ml.eid = '771'; var s, cd, tag; s = d.getElementsByTagName(t)[0]; cd = new Date(); tag = d.createElement(t); tag.async = 1; tag.src = 'https://ml314.com/tag.aspx?' + cd.getDate() + cd.getMonth(); s.parentNode.insertBefore(tag, s); })(window,document,'script'); } bizx.cmp.ifConsent('', ['all', 'bombora'],bm_trace); </script> </head> <body class="path-node page-node-type-story has-glyphicons"> <a href="#main-content" class="visually-hidden focusable skip-link"> Skip to main content </a> <div class="dialog-off-canvas-main-canvas" data-off-canvas-main-canvas> <div id="wrap"> <div class="leader-wrapper" id="leader-wrapper"> </div> <div class="brand-wrapper" id="brand-wrapper"> <div class="brand container"> <div class="region region-brand"> <a class="logo navbar-btn" href="/" title="Home" rel="home"> <h1><img src="/themes/linuxjournal/images/ljlogo.png" alt="Linux Journal" /></h1> </a> <section id="block-topbannernearlogo" class="block block-block-content block-block-contenta8e75a3d-967c-416d-8944-4015d1f90a84 clearfix"> <div class="field field--name-body field--type-text-with-summary field--label-hidden field--item"><a href="https://www.windriver.com/products/elxr-pro?utm_source=ba&utm_medium=pa&utm_campaign=ba-dg-amer-awa-vsmf-eLxrProLau_lin_dis_09172024"><img alt="Windriver eLxrPro" data-entity-type="file" data-entity-uuid="8ba04368-7803-4329-a196-dc1ea780ec2a" src="/sites/default/files/inline-images/WindRiver-eLxrPro_0.png" width="728" height="90" loading="lazy" /></a></div> </section> </div> </div> <div class="header-wrapper"> <header class="navbar navbar-default container" id="navbar" role="banner"> <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#navbar-collapse"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <div id="navbar-collapse" class="navbar-collapse collapse"> <div class="region region-navigation-collapsible"> <section id="block-linuxjournal-main-menu" class="block block-system block-system-menu-blockmain clearfix navbar__menu col-xs-12 col-sm-8"> <ul class="menu menu--main nav navbar-nav"> <li class="expanded dropdown first"> <a href="/" class="dropdown-toggle" data-toggle="dropdown" data-drupal-link-system-path="<front>">Topics+ <span class="caret"></span></a> <ul class="dropdown-menu"> <li class="first"> <a href="/tag/cloud">Cloud</a> </li> <li> <a href="/tag/containers" data-drupal-link-system-path="taxonomy/term/978">Containers</a> </li> <li> <a href="/tag/desktop">Desktop</a> </li> <li> <a href="/tag/kernel" data-drupal-link-system-path="taxonomy/term/662">Kernel</a> </li> <li> <a href="/tag/mobile">Mobile</a> </li> <li> <a href="/tag/networking">Networking</a> </li> <li> <a href="/tag/privacy">Privacy</a> </li> <li> <a href="/tag/programming">Programming</a> </li> <li> <a href="/tag/security" data-drupal-link-system-path="taxonomy/term/31">Security</a> </li> <li> <a href="/tag/servers">Servers</a> </li> <li class="last"> <a href="/tag/sysadmin" data-drupal-link-system-path="taxonomy/term/21">SysAdmin</a> </li> </ul> </li> <li> <a href="/news" data-drupal-link-system-path="news">News</a> </li> <li class="last"> <a href="/books" data-drupal-link-system-path="books">eBooks</a> </li> </ul> </section> <section class="search-block-form block block-search block-search-form-block clearfix navbar__search col-xs-12 col-sm-4 col-md-4 col-md-offset-0" data-drupal-selector="search-block-form" id="block-linuxjournal-search" role="search"> <h2 class="block-title sr-only">Search</h2> <form action="/search/node" method="get" id="search-block-form" accept-charset="UTF-8"> <div class="form-item js-form-item form-type-search js-form-type-search form-item-keys js-form-item-keys form-no-label form-group"> <label for="edit-keys" class="control-label sr-only">Search</label> <div class="input-group"><input title="Enter the terms you wish to search for." data-drupal-selector="edit-keys" class="form-search form-control" placeholder="Search" type="search" id="edit-keys" name="keys" value="" size="15" maxlength="128" /><span class="input-group-btn"><button type="submit" value="Search" class="button js-form-submit form-submit btn-primary btn icon-only" name=""><span class="sr-only">Search</span><span class="icon glyphicon glyphicon-search" aria-hidden="true"></span></button></span></div> <div id="edit-keys--description" class="description help-block"> Enter the terms you wish to search for. </div> </div> <div class="form-actions form-group js-form-wrapper form-wrapper" data-drupal-selector="edit-actions" id="edit-actions"></div> </form> </section> </div> </div> <div class="navbar-header"> <div class="region region-navigation"> <section id="block-mobilenavigation" class="block block-system block-system-menu-blockmenu-mobile-navbar clearfix col-xs-12 col-md-8 col-md-offset-2 col-lg-8 col-lg-offset-0 navbar__menu navbar__mobilenavigation"> <ul class="menu menu--menu-mobile-navbar nav"> <li class="first"> <a href="/news" data-drupal-link-system-path="news">News</a> </li> <li> <a href="/popular" data-drupal-link-system-path="popular">Popular</a> </li> <li class="last"> <a href="/recent" data-drupal-link-system-path="recent">Recent</a> </li> </ul> </section> </div> </div> </header> </div> </div> <div role="main" class="main-container container js-quickedit-main-content" id="main"> <div class="row"> <section class="col-sm-12"> <div class="highlighted"> <div class="region region-highlighted"> <div data-drupal-messages-fallback class="hidden"></div> </div> </div> <a id="main-content"></a> <div class="region region-content"> <article data-history-node-id="1340426" class="row bs-2col-stacked node node--type-story node--view-mode-full"> <div class="col-sm-12 bs-region bs-region--top"> <div class="field field--name-field-tags field--type-entity-reference field--label-hidden field--items"> <div class="field--item"><div about="/tag/security" typeof="schema:Thing"> <h2><a href="/tag/security"> <div property="schema:name" class="field field--name-name field--type-string field--label-hidden field--item">Security</div> </a></h2> <span property="schema:name" content="Security" class="hidden"></span> </div> </div> <div class="field--item"><div about="/tag/heads" typeof="schema:Thing"> <h2><a href="/tag/heads"> <div property="schema:name" class="field field--name-name field--type-string field--label-hidden field--item">Heads</div> </a></h2> <span property="schema:name" content="Heads" class="hidden"></span> </div> </div> <div class="field--item"><div about="/tag/secure-boot" typeof="schema:Thing"> <h2><a href="/tag/secure-boot"> <div property="schema:name" class="field field--name-name field--type-string field--label-hidden field--item">Secure Boot</div> </a></h2> <span property="schema:name" content="Secure Boot" class="hidden"></span> </div> </div> <div class="field--item"><div about="/tag/uefi" typeof="schema:Thing"> <h2><a href="/tag/uefi"> <div property="schema:name" class="field field--name-name field--type-string field--label-hidden field--item">UEFI</div> </a></h2> <span property="schema:name" content="UEFI" class="hidden"></span> </div> </div> </div> <div class="field field--name-node-title field--type-ds field--label-hidden field--item"><h1> Tamper-Evident Boot with Heads </h1> </div> <div class="field field--name-node-author field--type-ds field--label-hidden field--item">by <a title="View user profile." href="/users/kyle-rankin" lang="" about="/users/kyle-rankin" typeof="schema:Person" property="schema:name" datatype="">Kyle Rankin</a></div> <div class="field field--name-node-post-date field--type-ds field--label-hidden field--item">on January 31, 2019</div> </div> <div class="col-sm-9 bs-region bs-region--left"> <div class="shariff" data-services="["twitter","facebook","linkedin","reddit","mail"]" data-theme="white" data-css="complete" data-orientation="horizontal" data-twitter-via="linuxjournal" data-mail-url="mailto:" data-lang="en"> </div> <div class="field field--name-body field--type-text-with-summary field--label-hidden field--item"><p><em>Learn about how the cutting-edge, free software Heads project detects BIOS and kernel tampering, all with keys under your control.</em></p> <p> <em>Disclaimer:</em> I work for Purism, and my experience with Heads began as part of supporting it on Purism's hardware. As a technical writer, I personally find ads that mask themselves as articles in technical publications disingenuous, and this article <em>in no way</em> is intended to be an advertisement for my employer. However, in writing this deep dive piece, I found that mentioning Purism was unavoidable in some places without leaving out important information about Heads—in particular, the list of overall supported hardware and an explanation of Heads' HOTP alternative to TOTP authentication, because it requires a specific piece Purism hardware. </p> <p> Some of the earliest computer viruses attacked the boot sector—that bit of code at the beginning of the hard drive in the Master Boot Record that allowed you to boot into your operating system. The reasons for this have to do with stealth and persistence. Viruses on the filesystem itself would be erased if users re-installed their operating systems, but if they didn't erase the boot sector as part of the re-install process, boot sector viruses could stick around and re-infect the operating system. </p> <p> Antivirus software vendors ultimately added the ability to scan the boot sector for known viruses, so the problem was solved, right? Unfortunately, as computers, operating systems and BIOSes became more sophisticated, so did the boot-sector attacks. Modern attacks take over before the OS is launched and infect the OS itself, so when you try to search for the attack through the OS, the OS tells you everything is okay. </p> <p> That's not to say modern defenses to this type of attack don't exist. Most modern approaches involve proprietary software that locks down the system so that it can boot only code that's signed by a vendor (typically Microsoft, Apple, Google or one of their approved third-party vendors). The downside, besides the proprietary nature of this defense, is that you are beholden to the vendor to bless whatever code you want to run, or else you have to disable this security feature completely (if you can). </p> <p> Fortunately, an alternative exists that is not only free software, but that also takes a completely different approach to boot security by alerting you to tampering instead of blocking untrusted code. This approach, Heads, can detect tampering not only in the BIOS itself but also in all of your important boot files in the /boot directory, including the kernel, initrd and even your grub config. The result is a trusted boot environment with keys fully under your own control. </p> <p> In this article, I describe some of the existing boot security approaches in more detail, along with some of their limitations, and then I describe how Heads works, and how to build and install it on your own system. </p> <span class="h3-replacement"> Why Boot Security Matters </span> <p> To understand why having a secure boot process matters so much, it's useful to understand one of the most common threats on a Linux system: rootkits. A rootkit is a piece of software attackers can use to exploit vulnerabilities in the kernel or other software on the system that has root privileges, so it can turn normal user-level access into root-level access. This ability to escalate to root privileges is important, because although in the old days, all network services ran as root, these days, servers more often run as regular users. If attackers find a flaw in a network service and exploit it so they are able to run commands locally, they will only be able to run those commands as the same user. The rootkit allows them to turn those local user privileges into root privileges, whereby they then can move on to the next step, which is installing backdoors into your system, so they can get back in later undetected. </p> <p> Although sometimes attackers will install a backdoor that just has a service listening on an obscure port all the time, kernel backdoors are preferred because once they exploit the kernel, they then can mask any attempts by your OS to detect the attack. After all, if you want to know what files are in a directory, or which processes are running, you have to ask the kernel. If you can exploit the kernel, you can hide your malicious processes or files from prying eyes. Many rootkits also will set up a kernel backdoor for attackers automatically as part of the automated attack. </p> <p> Rootkits aren't only a threat on servers; it's just that servers are accessible on the network all the time, and they run software that listens for requests. Although modern Linux desktop installs don't have any services listening on the network, there still are plenty of ways for attackers to launch code locally as your user—via the web browser is one of the most common ways, and malicious file attachments in email is another. </p> <p> The whole point of a rootkit is to make it difficult for you to detect it from the running OS, but you still always can boot the system from a live USB-based OS and examine the hard drive. Or, you could re-install the OS completely and be rid of the threat. Yet even in that case, you are relying on the BIOS to boot your live USB-based OS. Your BIOS is the first code your CPU executes when it boots. Once it loads, it detects the hardware on your system, initializes it, and then lets you boot either from an internal hard drive or perhaps from external USB or DVD media. If attackers were able to modify your BIOS, in theory, they could just re-install their backdoor in any kernel it loads and persist even with re-installing the OS or examining it from a live USB disk. </p> <p> The BIOS then becomes the root of trust for the entire rest of the system. Until you can trust it, you can't fully trust the rest of the code that executes after it. </p> <p> Next I describe some of the current approaches to secure the boot process, all of which involve executing only pre-approved code. </p> <span class="h3-replacement"> Other Boot Security Methods </span> <p> It's easier to understand how Heads works, and how it is different from the existing approaches, once you understand how the existing approaches work. The main two approaches that provide boot security on modern systems are UEFI Secure Boot and Intel Trusted Boot. </p> <p> <strong>UEFI Secure Boot</strong> </p> <p> Of all of the different approaches to secure the boot process, UEFI Secure Boot is the most popular, and it's included in just about every modern laptop and desktop you would buy. The way that Secure Boot works is that the UEFI flash chip contains certificates for Microsoft and its approved third-party vendors. UEFI boot firmware that works with Secure Boot contains a signature created by the private keys of either Microsoft or its approved vendors. Secure Boot then checks that signature against its certificates, and if the signature matches, it allows the boot firmware to execute. If the signature doesn't match or is missing, Secure Boot will not allow it to run. </p> <p> Because it was initially designed for Windows, and initially Windows was the only OS that used it, Secure Boot often is thought of as a Microsoft-only technology, and many in the FOSS community spoke out against it because of the risk that it could be used to lock out a system from loading Linux. It's true that initially you could use Secure Boot only with Windows, but Linux distribution vendors like Red Hat and Ubuntu worked with Microsoft to get a boot "shim" signed that would allow them to load GRUB and boot their OSes. </p> <p> Of course, there still are plenty of Linux distributions that haven't gotten boot shim code signed by Microsoft, including Debian. This means that if you want to install Debian on a system with Secure Boot, you first must go into your UEFI settings and disable Secure Boot entirely before you are allowed to boot the USB installer—that is, <em>if</em> your UEFI software allows you to disable Secure Boot. Some lower-cost computers these days ship with stripped-down UEFI firmware that allow only a very minimal level of configuration, and on these systems, Secure Boot often is no longer optional. </p> <p> Secure Boot <em>does</em> have a mechanism that would allow you to replace the existing vendor certificates with your own, and that might be an option for Linux users who want to use Secure Boot on systems that don't use Microsoft-signed boot firmware. The process itself is somewhat complicated though, and the end result would boot your own custom-signed code but then would lock out anything not signed with your own signatures, such as a typical USB OS installer. Again, this is an option only if you first can disable Secure Boot to load your untrusted OS and modify UEFI, or else attempt the modification from a trusted OS. </p> <p> <strong>Intel Trusted Boot</strong> </p> <p> Along with Secure Boot, modern Intel computers also have the option of a security mechanism called Intel Trusted Boot. This mechanism takes advantage of the special capabilities of the Trusted Platform Module (TPM) chip on a system. The TPM is a standalone chip available on some motherboards that can act as its own Hardware Security Module (HSM) by generating its own cryptographic keys and performing cryptographic operations on-chip independent of the system CPU. The TPM also contains Platform Configuration Registers (PCRs) that can contain measurements of executed code in the form of a chain of hashes. Generally, different PCRs are used to store measurements of different phases of the boot process. </p> <p> Intel Trusted Boot works by sending the measurements of code as it is executing over to the TPM where it is hashed and stored in a corresponding PCR. As new code is executed, it also gets hashed and combined with hashes of previous code in the PCR. The TPM allows you to seal secrets (disk decryption keys are common) within it that are unlocked only if the PCRs contain previously stored values. Combined with Secure Boot, Intel Trusted Boot allows you to detect tampering in boot-time executables. </p> <span class="h3-replacement"> Secure Boot Limitations</span> <p> Secure Boot is the main way vendors provide boot-time security on modern computers, but it has quite a few limitations. The first big limitation is also its biggest claimed feature—that it requires boot code to be signed by keys under the vendor's control. This means if you did happen to want to run custom boot code, you must work with vendors to get them to sign your binary or else replace all of their certificates with your own and run <em>only</em> your own code. </p> <p> Another limitation is that although Secure Boot ensures that you are running code that has been signed, it doesn't ensure that you are running the <em>same</em> boot code that you ran previously. An attacker who was able to get access to one of the vendor signing keys could create a boot-time executable that would pass Secure Boot protections. What would happen to existing computers if one of the Microsoft (or other vendor) signing keys were leaked or forced to be shared with a nation state? </p> <p> Secure Boot is also proprietary software, so you have to take vendors at their word that there are no backdoors within it, and you also have less visibility into what code might be signed. In addition, Secure Boot validates only executables. It can't validate your initrd files or GRUB configs—both places where attackers could add malicious changes. Ultimately, the issue with Secure Boot is that it takes control of your computer and its security out of your hands and into the hands of vendors. If you fully trust your vendor, perhaps you are fine with that trade-off, but many people would prefer to have full control over their own software and hardware. </p> <span class="h3-replacement"> Introduction to Heads </span> <p> Heads was created by Trammell Hudson to solve some of the trust issues and other limitations of Secure Boot by replacing it with a system that focuses on detecting tampering instead of blocking it. The idea with Heads is to capture a stable, trusted state in the BIOS and boot code, and then ensure that at each subsequent boot, the BIOS and boot code haven't changed. Heads is written under a free software license, so it not only can be inspected, but it also is reproducibly built, so if you were to get a pre-built Heads ROM, you also could build the same revision of Heads yourself and get the same result, thereby proving that the code wasn't tampered with at some point in the build process. </p> <p> Heads loads from within the open-source coreboot BIOS (or optionally LinuxBoot for some server platforms) and is actually its own standalone Linux kernel and runtime environment that performs tamper checks and then boots into your system kernel once everything checks out. Unlike with Secure Boot, it detects tampering using keys that are fully under your control—keys you can change at any point. </p> <span class="h3-replacement"> Hardware Support </span> <p> Because Heads relies on coreboot or LinuxBoot, its current hardware support is somewhat limited to hardware that both supports either coreboot or LinuxBoot, has a TPM, and has someone who has defined that board's configuration, including coreboot settings and other options, and submitted it to Heads. Currently, that list is pretty small: Lenovo ThinkPad X220 and X230, the Purism Librem laptop line and a handful of servers. </p> <span class="h3-replacement"> How Heads Works </span> <p> On the surface, Heads works similarly to Intel Trusted Boot in that it uses the TPM to verify measurements of itself to then unlock a secret. That's where the similarities end though, as Heads approaches boot security in a much different way, because its aim is to provide tamper <em>detection</em>, not tamper <em>proofing</em>. Heads will alert you to tampering, but it still provides you the ability to boot whatever software you want. </p> <p> You can break down the default Heads boot process into a few main phases: </p> <ul><li> The coreboot BIOS starts and loads the Heads kernel and initrd.</li> <li> As code executes, measurements are sent over to the TPM chip. </li> <li> Heads presents a TOTP/HOTP code to prove to the user that it hasn't been tampered with.</li> <li> The user selects a boot option.</li> <li> Heads checks all the files in /boot for tampering before loading the OS.</li> <li> If the files all check out, Heads boots the OS. </li></ul><p> Heads uses two different sets of keys to detect tampering. First it uses a shared secret stored in the TPM and also on either a TOTP authenticator application on your phone or on a special USB security token like the Librem Key. This shared secret is used to prove the BIOS itself hasn't been tampered with. The next set of keys is a set of trusted GPG public keys within a GPG keyring that you add to the Heads ROM. Once you know the BIOS hasn't been tampered with, you can trust that the GPG keyring it has within it hasn't been modified to add an untrusted key. Heads then uses that trusted keyring to verify all of the signatures on the files in /boot. In both cases, these are secrets that are fully under your control, and you can change them and reset signatures at any point. </p> <p> Next, let's look at more specifics of how Heads works by focusing on each of these two secrets and how they are used in their respective parts of the boot process. </p> <span class="h3-replacement"> Boot Security and the TPM </span> <p> The very first thing Heads must do is prove to you that it can be trusted and that it hasn't been tampered with. The challenge is, if it <em>has</em> been tampered with, couldn't it lie to you and tell you everything is okay? This is where the TPM comes in. When you first set up Heads, you go through a process to reset the TPM and set up a new admin password (called <em>taking ownership</em>), and then Heads will generate a random secret and store it in the TPM (called <em>sealing</em>) along with the current valid measurements it will take to unlock that secret. </p> <p> Once the secret is sealed in the TPM, Heads will convert that secret into a QR code and display it on the screen, so you can scan it with your phone to add it to your TOTP authenticator application of choice (FreeOTP is a free software option that works on Android, for instance). If you have added Librem Key support into Heads, you also can store a copy of the secret onto Purism's Librem Key USB security token. </p> <p> When Heads boots, it then sends measurements of the code it executes over to the TPM. If the BIOS has been tampered with, those measurements won't match what was there before, and the TPM will not unlock the shared secret. In that case, Heads will output an error to the screen alerting you to the problem. If the measurements do match, the TPM will unlock the shared secret, send it to Heads, and Heads will combine the secret with the current time to convert it to a six-digit TOTP code that it will display on the screen. You then can compare that code to the six-digit TOTP code in your phone's app, and if they match, you know that the secret was valid. Alternatively, if you enabled Librem Key support, you could insert the device at boot, and Heads would generate a six-digit HOTP code and send it over USB. If it matched the code the Librem Key generated on itself, the Librem Key would blink green; otherwise, it would blink red. </p> <img src="/sites/default/files/styles/max_650x650/public/u%5Buid%5D/12668f1-smaller.jpeg" width="650" height="488" alt="""" class="image-max_650x650" /><p> <em>Figure 1. The Default Heads Boot Screen</em> </p> <p> So if an attacker modifies the BIOS, the TPM will generate an error, but what if the attacker then resets the TPM with a different secret using the measurements from the tampered BIOS? Those measurements would match, and the TPM would unlock the new secret, but that secret would generate a different six-digit code from what your phone or Librem Key would generate, and you would know something suspicious was happening. Because the TPM is designed to be a tamper-proof device, you cannot extract the shared secret from it without providing valid measurements. If you reset the TPM, that secret is also erased. </p> <span class="h3-replacement"> Boot Security and GPG Keys </span> <p> Once you have verified that the BIOS is trustworthy, you can move on to booting your OS. But before Heads will boot into the OS, it first checks all of the files within the /boot partition to make sure they haven't changed from when you last signed all of them. When you first set up Heads, you add one or more public GPG keys to a keyring within the Heads runtime environment. Heads provides a mechanism not only to add GPG keys to a standalone Heads coreboot ROM file, but you also can add them to the running BIOS. In that case, Heads actually will pull down a copy of the running BIOS, modify it on the fly, and then reflash it. </p> <p> Once you have a set of trusted GPG keys in the Heads keyring, you then can sign the files within /boot with your corresponding GPG private key using the Heads GUI. Heads will create a file containing sha256sums for all of the files within /boot and then sign that file with your GPG private key and store the signature in /boot as well. This will require that you have some kind of USB security token that has OpenPGP smartcard support, and Heads will prompt you to insert your USB GPG key whenever you sign these files. </p> <p> When you tell Heads to boot into your OS, it first gets flashed into the BIOS, and it can read your GRUB config file and provide you with a boot menu based on the options in that config file. </p> <p> Also, whenever you update or add a new kernel, change an existing initrd file, or modify your GRUB config, Heads will detect the change, showing you an error at the next boot. Along with that error will be an option to re-sign all of the files in /boot, in the case that you changed the files yourself. If you didn't expect those files to change, of course, then this could be a sign of tampering! </p> <span class="h3-replacement"> Building and Installing Heads </span> <p> Heads is reproducibly built, which means that it's designed so that if multiple people were to build the same specific release of Heads with the same build options at different times on different systems, they should get the exact same binary. Because Heads runs on specific BIOS chips, it needs to cross-compile the kernel and other software for that platform, which means that in addition to building a complete Linux kernel and coreboot, you also will need to build a cross-compiler and supporting tools when you build Heads. </p> <p> Your local system also will need certain system libraries so you can build coreboot and Heads. On a Debian-based system, you can use apt to install them: </p><pre> <code> sudo apt install git build-essential bison flex m4 zlib1g-dev ↪gnat libpci-dev libusb-dev libusb-1.0-0-dev dmidecode ↪bsdiff python2.7 pv libelf-dev pkg-config cmake </code> </pre> <p> For other systems, use your packaging tool to install the equivalent packages for your platform. Once those are installed, the next step is to get the most recent Heads source code and go into the root of that build directory: </p><pre> <code> git clone https://github.com/osresearch/heads.git cd heads/ </code> </pre> <p> The next step is to pull down any binary blobs your board might need for coreboot to boot. Go to the blobs/ directory inside Heads, and see if your board has a directory represented in there. If so, <code>cd</code> to it and read the instructions for how to pull down your binary blobs for coreboot. For instance, on Librem hardware: </p><pre> <code> cd blobs/librem_skl/ ./get_blobs.sh </code> </pre> <p> Once you have gotten any blobs you may need, move back to the root of the Heads build directory. From there, you will see a boards/ directory, and within it are directories for each of the motherboards that Heads supports. Each of those boards has a corresponding configuration file inside its respective directory that set important options, such as what partition to use for /boot and for USB boot devices, what kernel options (if any) to pass along to the OS when it boots, and which init script to load into. These configuration files are already set up for the most part to work with the corresponding motherboard, but you should review the configuration file for your board and confirm in particular that the <code>CONFIG_BOOT_DEV</code> and <code>CONFIG_USB_BOOT_DEV</code> variables are pointing to the correct /boot and USB boot device, respectively. </p> <p> Once you are finished editing the configuration file, it's time to build Heads. Change back to the root of the Heads source code and set the particular board with an environment variable while running the <code>make</code> command. So for instance, to build for a ThinkPad X230, you would type: </p><pre> <code> make BOARD=x230 </code> </pre> <p> The first time you build Heads, it will take quite a long time! Just be patient as it builds GCC, coreboot, the Linux kernel and a number of other pieces of software. Subsequent builds will be a lot faster. If the build fails at some point in the process, make a note of what package it was attempting to build, and then check the corresponding build log for that software inside the logs/ directory. More often than not, if you see a build failure for a particular piece of software, it's because you are missing a development library on your system. Reviewing the log file should tell you which libraries are missing. </p> <p> Once Heads completes the build process, it will dump the corresponding coreboot ROM image into boards/<boardname>/coreboot.rom, so in the case of the above X230 example, it would be in boards/x230/coreboot.rom. You now are ready to install Heads as your BIOS by flashing that ROM image. </p> <span class="h3-replacement"> Flashing Heads </span> <p> Once you have built your Heads coreboot ROM, the next step is to flash it over the top of your existing BIOS. How you flash Heads on your computer will vary depending on the specific motherboard you have for a number of reasons. First, each laptop uses its own set of flashrom options that are specific to the BIOS chip it has on board, so you will need to reference the flashrom options appropriate for your board. Check out the initrd/bin/flash.sh script from within the Heads code base for an example script that provides flashrom options for the supported boards. Note that this script is designed to be run from within the Heads environment itself with a relatively new version of flashrom (1.0 or above). Older flash chips (like on the ThinkPad boards) should work with older flashrom versions that you should be able to install via a package on your current Linux distribution, but newer boards (like on the Purism Librem laptops) will require a newer (1.0) flashrom program. In the latter case, Purism provides instructions <a href="https://puri.sm/coreboot">here</a> to pull down and build a current flashrom. </p> <p> Another reason that flashing Heads varies for different platforms is that although you can update coreboot from within your own operating system using flashrom if it is already installed, if coreboot isn't already installed, some laptops require an initial hardware flash. For instance, unless you bought it from a special vendor, the Lenovo ThinkPad laptops come with a proprietary vendor-provided BIOS instead of coreboot, so they require an initial hardware flash to overwrite the vendor BIOS. This hardware flash means opening the laptop to expose the BIOS chip, connecting a Pomona clip to it that's attached to one of the many hardware platforms that support flashrom, such as a Raspberry Pi or Beaglebone Black. I cover these steps, including how to back up the existing BIOS, in a past Hack and / article: <a href="https://www.linuxjournal.com/content/flash-roms-raspberry-pi">"Flash ROMs with a Raspberry Pi"</a>. </p> <img src="/sites/default/files/styles/max_650x650/public/u%5Buid%5D/12668f2-smaller.jpeg" width="650" height="366" alt="""" class="image-max_650x650" /><p> <em>Figure 2. Hardware Flashing a BIOS with a Raspberry Pi</em> </p> <p> If your hardware already has coreboot installed, you should be able to install Heads purely from software by running flashrom from within the native OS. For instance, the Purism Librem laptops come with coreboot already installed (and plan to offer Heads as a pre-installed option in the near future), so you can use flashrom from within the regular operating system to flash the Heads BIOS without opening the machine. In this case, you will want to run flashrom first with the <code>-r</code> option, so it will pull down a backup of your existing BIOS to store on a USB thumb drive in case you ever want to revert back. </p> <span class="h3-replacement"> Using Heads </span> <p> Once you have flashed Heads for the first time and rebooted, Heads will guide you through the initial setup. First you'll be prompted to add at least one public GPG key to the Heads keyring, which will require that you have the public key on some sort of USB thumb drive ending in .asc. Heads will mount the USB drive and find all possible .asc files on the device and then prompt you as to which of them you want to add. Once you have added the key, Heads will reflash the BIOS and reboot. </p> <p> Once Heads reboots with GPG keys in place, it will get a TPM error, because the TPM has not yet been set up, so it will guide you through setting up a password for your TPM and creating the initial TOTP/HOTP secret. After it reboots another time, you finally should see the default Heads boot menu that lets you select between your default boot option (not yet configured) or opening an Advanced menu of options. </p> <p> If you select default boot with no default boot option set, it will detect that state and guide you through selecting a boot option. At that point, it also should detect that you have not yet signed any files in /boot, and it also will guide you through that process (you will need your USB security token containing your GPG private keys at that point). </p> <p> Once all of the files have been signed and your default boot option has been set, you should be able to treat Heads much like a regular GRUB menu—boot the computer, confirm there are no alerts and just press Enter to boot into your default OS. Note that as you update software on your underlying OS, if your package updates change or add any files to the /boot directory, you'll get an alert the next time you reboot that files may have been tampered with. If you know that this was caused by your package update and not something malicious, you can just re-sign all of the files in /boot with your private GPG key. </p> <p> You can apply updates to Heads completely within the Heads menu. Within the Advanced options menu is a submenu that allows you to flash the BIOS. Within this menu, you can insert a USB drive containing *.rom files and have Heads flash them over the top of your current Heads ROM. There are two main flashing options: flash a ROM and flash a <em>cleaned</em> ROM. The first option is pretty self-explanatory, but in the case of a cleaned ROM, Heads will flash the BIOS, but it won't copy over any existing GPG public keys or other custom changes you may have made to Heads on top of the default ROM. Use this option if you ever want to revert back to a pure factory state (or flash some other non-Heads BIOS), and otherwise use the default flashing option to copy your keyring to the updated Heads ROM. </p> <span class="h3-replacement"> Conclusion </span> <p> Although installing and using Heads is not for the faint of heart, if you have experimented with coreboot on systems in the past, it's not that much more complicated. If you want the best in boot security, the effort is definitely worth it, as you will end up with a system that can alert you you both to BIOS and kernel-level tampering but with keys completely under your control. </p> <span class="h3-replacement"> Resources</span> <ul><li> <a href="https://github.com/osresearch/heads.git">The Heads Project</a> </li> <li><a href="https://www.uefi.org">UEFI</a></li> <li> <a href="https://www.uefi.org/sites/default/files/resources/UEFI_Secure_Boot_in_Modern_Computer_Security_Solutions_2013.pdf">UEFI Secure Boot</a></li> <li><a href="https://software.intel.com/en-us/articles/intel-trusted-execution-technology">Intel Trusted Boot</a></li> <li> <a href="https://trustedcomputinggroup.org/work-groups/trusted-platform-module">TPM (Trusted Platform Module)</a></li> <li> <a href="https://en.wikipedia.org/wiki/Hardware_security_module">HSM (Hardware Security Module)</a></li> <li> <a href="https://www.coreboot.org">coreboot</a></li> <li> <a href="https://www.linuxboot.org">LinuxBoot</a></li> <li><a href="https://www.linuxjournal.com/content/foss-project-spotlight-linuxboot">"FOSS Project Spotlight: LinuxBoot" by David Hendricks, Andrea Barberio and Ron Minnich</a></li> <li><a href="https://www.lenovo.com/us/en/pc">Lenovo</a></li> <li><a href="https://puri.sm">Purism</a></li> <li><a href="https://www.linuxjournal.com/content/flash-roms-raspberry-pi">"Flash ROMs with a Raspberry Pi" by Kyle Rankin</a></li> </ul></div> <div class="field field--name-dynamic-token-fieldnode-author field--type-ds field--label-hidden field--item"><div> <img loading="lazy" src="/sites/default/files/pictures/picture-1002206.jpg" width="85" height="84" typeof="foaf:Image" class="img-responsive" /> </div> <p> Kyle Rankin is a Tech Editor and columnist at <em>Linux Journal</em> and the Chief Security Officer at Purism. He is the author of <em>Linux Hardening in Hostile Networks</em>, <em>DevOps Troubleshooting</em>, <em>The Official Ubuntu Server Book</em>, <em>Knoppix Hacks</em>, <em>Knoppix Pocket Reference</em>, <em>Linux Multimedia Hacks</em> and <em>Ubuntu Hacks</em>, and also a contributor to a number of other O'Reilly books. Rankin speaks frequently on security and open-source software including at BsidesLV, O'Reilly Security Conference, OSCON, SCALE, CactusCon, Linux World Expo and Penguicon. You can follow him at @kylerankin. </p> </div> <section class="section--disqus"> <a id="comments-link" class="btn btn-default" href="https://www.linuxjournal.com/content/tamper-evident-boot-heads#disqus_thread">Load Disqus comments</a> <div id="disqus_thread"></div> <noscript>Our discussions are <a href="https://disqus.com/?ref_noscript" rel="nofollow">powered by Disqus</a>, which require JavaScript.</noscript> </section> </div> <div class="col-sm-3 bs-region bs-region--right"> <div class="field field--name-dynamic-block-fieldnode-sidebar-image field--type-ds field--label-hidden field--item"><div class="views-element-container form-group"><div class="view view-sidebar-image view-id-sidebar_image view-display-id-block_1 js-view-dom-id-7f7ba908217006232962a486e7e7d9454a7242e5629ce61dc42ba29599b6751b"> </div> </div> </div> <div class="field field--name-dynamic-block-fieldnode-recent-content field--type-ds field--label-hidden field--item"><div class="views-element-container form-group"><div class="view view-related-content view-id-related_content view-display-id-block_2 js-view-dom-id-6064e91078e2d156a059a9d4e4ef4b3d1cda2d7ab5707f270d51c0497b5e21c5"> <div class="view-header"> <h2 class="block-title">Recent Articles</h2> </div> <div class="view-content"> <div class="views-row"><div class="views-field views-field-field-node-image"><div class="field-content"> <a href="/content/using-maxqda-qualitative-data-analysis-linux" hreflang="en"><img loading="lazy" src="/sites/default/files/styles/75x75_square/public/nodeimage/story/using-maxqda-for-qualitative-data-analysis-on-linux.jpg?itok=KFsvAt23" width="75" height="75" alt="Using MAXQDA for Qualitative Data Analysis on Linux" typeof="foaf:Image" class="img-responsive" /> </a> </div></div><div class="views-field views-field-title"><span class="field-content"><a href="/content/using-maxqda-qualitative-data-analysis-linux" hreflang="en">Using MAXQDA for Qualitative Data Analysis on Linux</a></span></div><div class="views-field views-field-uid"><span class="field-content"><a href="/users/george-whittaker" hreflang="en">George Whittaker</a></span></div></div> <div class="views-row"><div class="views-field views-field-field-node-image"><div class="field-content"> <a href="/content/haproxy-ubuntu-load-balancing-and-failover-resilient-infrastructure" hreflang="en"><img loading="lazy" src="/sites/default/files/styles/75x75_square/public/nodeimage/story/haproxy-on-ubuntu-load-balancing-and-failover-for-resilient-infrastructure.jpg?itok=2rVXWl_c" width="75" height="75" alt="HAProxy on Ubuntu: Load Balancing and Failover for Resilient Infrastructure" typeof="foaf:Image" class="img-responsive" /> </a> </div></div><div class="views-field views-field-title"><span class="field-content"><a href="/content/haproxy-ubuntu-load-balancing-and-failover-resilient-infrastructure" hreflang="en">HAProxy on Ubuntu: Load Balancing and Failover for Resilient Infrastructure</a></span></div><div class="views-field views-field-uid"><span class="field-content"><a href="/users/germansuarez" hreflang="en">german.suarez</a></span></div></div> <div class="views-row"><div class="views-field views-field-field-node-image"><div class="field-content"> <a href="/content/linux-binary-analysis-reverse-engineering-and-vulnerability-discovery" hreflang="en"><img loading="lazy" src="/sites/default/files/styles/75x75_square/public/nodeimage/story/linux-binary-analysis-for-reverse-engineering-and-vulnerability-discovery.jpg?itok=vTlNabrQ" width="75" height="75" alt="Linux Binary Analysis for Reverse Engineering and Vulnerability Discovery" typeof="foaf:Image" class="img-responsive" /> </a> </div></div><div class="views-field views-field-title"><span class="field-content"><a href="/content/linux-binary-analysis-reverse-engineering-and-vulnerability-discovery" hreflang="en">Linux Binary Analysis for Reverse Engineering and Vulnerability Discovery</a></span></div><div class="views-field views-field-uid"><span class="field-content"><a href="/users/george-whittaker" hreflang="en">George Whittaker</a></span></div></div> <div class="views-row"><div class="views-field views-field-field-node-image"><div class="field-content"> <a href="/content/debian-backup-and-recovery-solutions-safeguard-your-data-confidence" hreflang="en"><img loading="lazy" src="/sites/default/files/styles/75x75_square/public/nodeimage/story/debian-backup-and-recovery-solutions-safeguard-your-data-with-confidence.jpg?itok=LhESic3E" width="75" height="75" alt="Debian Backup and Recovery Solutions: Safeguard Your Data with Confidence" typeof="foaf:Image" class="img-responsive" /> </a> </div></div><div class="views-field views-field-title"><span class="field-content"><a href="/content/debian-backup-and-recovery-solutions-safeguard-your-data-confidence" hreflang="en">Debian Backup and Recovery Solutions: Safeguard Your Data with Confidence</a></span></div><div class="views-field views-field-uid"><span class="field-content"><a href="/users/george-whittaker" hreflang="en">George Whittaker</a></span></div></div> <div class="views-row"><div class="views-field views-field-field-node-image"><div class="field-content"> <a href="/content/installing-development-tools-debian-setting-compilers-libraries-and-ides-robust-development" hreflang="en"><img loading="lazy" src="/sites/default/files/styles/75x75_square/public/nodeimage/story/installing-development-tools-on-debian-setting-up-compilers-libraries-and-ides-for-a-robust-development-environment.jpg?itok=Aq209Ota" width="75" height="75" alt="Installing Development Tools on Debian: Setting Up Compilers, Libraries, and IDEs for a Robust Development Environment" typeof="foaf:Image" class="img-responsive" /> </a> </div></div><div class="views-field views-field-title"><span class="field-content"><a href="/content/installing-development-tools-debian-setting-compilers-libraries-and-ides-robust-development" hreflang="en">Installing Development Tools on Debian: Setting Up Compilers, Libraries, and IDEs for a Robust Development Environment</a></span></div><div class="views-field views-field-uid"><span class="field-content"><a href="/users/george-whittaker" hreflang="en">George Whittaker</a></span></div></div> <div class="views-row"><div class="views-field views-field-field-node-image"><div class="field-content"> <a href="/content/building-your-own-ubuntu-personal-cloud-step-step-guide-creating-secure-data-haven" hreflang="en"><img loading="lazy" src="/sites/default/files/styles/75x75_square/public/nodeimage/story/building-your-own-ubuntu-personal-cloud-a-step-by-step-guide-to-creating-a-secure-data-haven.jpg?itok=zYkD9lta" width="75" height="75" alt="Building Your Own Ubuntu Personal Cloud: A Step-by-Step Guide to Creating a Secure Data Haven" typeof="foaf:Image" class="img-responsive" /> </a> </div></div><div class="views-field views-field-title"><span class="field-content"><a href="/content/building-your-own-ubuntu-personal-cloud-step-step-guide-creating-secure-data-haven" hreflang="en">Building Your Own Ubuntu Personal Cloud: A Step-by-Step Guide to Creating a Secure Data Haven</a></span></div><div class="views-field views-field-uid"><span class="field-content"><a href="/users/george-whittaker" hreflang="en">George Whittaker</a></span></div></div> </div> </div> </div> </div> <div class="field field--name-dynamic-block-fieldnode-sponsors field--type-ds field--label-hidden field--item"><div class="views-element-container form-group"><div class="view view-latest-sponsor-block view-id-latest_sponsor_block view-display-id-block_1 js-view-dom-id-63fc9bfa68c260c26aa7ffb2cd603d882f3d9e091a9a39868f57da296fac1732"> <div class="view-header"> </div> <div class="view-content"> <div class="views-row"> <div class="field field--name-body field--type-text-with-summary field--label-hidden field--item"> </div> </div> </div> </div> </div> </div> </div> </article> </div> </section> </div> </div> <footer class="footer container" role="contentinfo"> <div class="region region-footer"> <section class="views-element-container block block-views block-views-blocknewsletter-promo-block-block-1 clearfix" id="block-views-block-newsletter-promo-block-block-1"> <div class="form-group"><div class="view view-newsletter-promo-block view-id-newsletter_promo_block view-display-id-block_1 js-view-dom-id-9c45aaed30e3769d5d713dabd197b28db0787dee194816b0ad6e8c3f1b52b488"> <div class="view-content"> <div class="views-row"> <div class="field field--name-body field--type-text-with-summary field--label-hidden field--item"><div class="newsletter-bottom"> <div class="col-md-9 col-sm-12 signup-form"> <!--<h3>Linux Journal Week in Review</h3> <p>Sign up to get all the good stuff delivered to your inbox every week.</p> <form id="subForm" class="js-cm-form" action="https://www.createsend.com/t/subscribeerror?description=" method="post" data-id="A61C50BEC994754B1D79C5819EC1255CDBC34DDF3E170B13E2BAC2D68C42BF424F853383C84F5BAE38EAB4CFA5C73907CAC074FF8192503AF80F3699F7FE5CE1"> <div class="flexform">--> <!--<label for="fieldEmail">Email</label> <br />--> <!--<input id="fieldEmail" name="cm-gjjtdh-gjjtdh" type="email" class="js-cm-email-input" placeholder="Enter your email. Get the newsletter." required /> <button class="js-cm-submit-button" type="submit">Sign Up</button> </div> <div> <input id="cm-privacy-consent" name="cm-privacy-consent" required type="checkbox" /> <label for="cm-privacy-consent">I give my consent to be emailed</label> <input id="cm-privacy-consent-hidden" name="cm-privacy-consent-hidden" type="hidden" value="true" /> </div> </form> <script type="text/javascript" src="https://js.createsend1.com/javascript/copypastesubscribeformlogic.js"></script> </div>--> <!--<div class="col-md-3 col-sm-12 subs-callout"> <img src="/sites/default/files/styles/large/public/2019-01/LJ294-Jan2019-Cover_0.jpg" width="100px" /> <h3>The Value of Open Source Journalism</h3> <p> Subscribe and support our coverage for technology's biggest thinkers – with up to 52% savings. </p> <strong><a href="https://www.linuxjournal.com/subscribe">Subscribe <i class="fa fa-angle-double-right" aria-hidden="true"></i></a> </strong> </div>--> </div> </div> </div> </div> </div> </div> </section> </div> <div class="footer-blocks col-sm-12"> <div class="col-md-6 col-sm-12 footer-left"> <div class="region region-footer-left"> <section id="block-connectwithusfooter" class="block block-block-content block-block-content5e722bd4-5e08-454b-8507-956089bfa661 clearfix"> <div class="field field--name-body field--type-text-with-summary field--label-hidden field--item"><div class="footer-heading">Connect With Us <span class="social-media sm-a-no-underline"><a href="https://youtube.com/linuxjournalonline" alt="Linux Journal on YouTube" aria-label="YouTube"><i class="fa fa-youtube fa-2x"></i></a><a href="https://www.facebook.com/linuxjournal/" alt="Linux Journal on Facebook" aria-label="Facebook"><i class="fa fa-facebook-f fa-2x"></i><a href="https://twitter.com/linuxjournal" alt="Linux Journal on Twitter" aria-label="Twitter"><i class="fa fa-twitter fa-2x"></i></a></span></div> <p>Linux Journal, representing 25+ years of publication, is the original magazine of the global Open Source community.</p></div> </section> <section id="block-linuxjournal-block-9" class="block block-block-content block-block-content8669793b-e217-4426-a79e-eb3c21ede127 clearfix"> <div class="field field--name-body field--type-text-with-summary field--label-hidden field--item"><div id="copyright">© 2024 Slashdot Media, LLC. All rights reserved.</div> </div> </section> <section id="block-privacyterms" class="block block-block-content block-block-contenta203b8bd-80ef-4982-bf6e-784dd7b44120 clearfix"> <div class="field field--name-body field--type-text-with-summary field--label-hidden field--item"><ul class="menu menu--footer-submenu nav" id="terms-nav"> <li><a href="https://slashdotmedia.com/privacy-statement/" rel="nofollow" target="_blank">PRIVACY POLICY</a></li> <li><a href="https://slashdotmedia.com/terms-of-use/" rel="nofollow" target="_blank">TERMS OF SERVICE</a></li> <li><a href="/sponsors">ADVERTISE</a></li> </ul></div> </section> </div> </div> <div class="col-md-4 col-sm-8 footer-middle"> <div class="region region-footer-middle"> <nav role="navigation" aria-labelledby="block-footermenucolumn2-menu" id="block-footermenucolumn2"> <h2 class="visually-hidden" id="block-footermenucolumn2-menu">Footer Menu Column 2</h2> <ul class="menu menu--footer-menu-column-2 nav"> <li class="first"> <a href="/content/masthead" data-drupal-link-system-path="node/1007727">Masthead</a> </li> <li> <a href="/author" data-drupal-link-system-path="node/1009249">Authors</a> </li> <li class="last"> <a href="/form/contact" data-drupal-link-system-path="webform/contact">Contact Us</a> </li> </ul> </nav> <nav role="navigation" aria-labelledby="block-footermenucolumn3-menu" id="block-footermenucolumn3"> <h2 class="visually-hidden" id="block-footermenucolumn3-menu">Footer Menu Column 3</h2> <ul class="menu menu--footer-menu-column-3 nav"> <li class="first"> <a href="/rss_feeds" data-drupal-link-system-path="node/1000457">RSS Feeds</a> </li> <li class="last"> <a href="/aboutus" data-drupal-link-system-path="node/1000267">About Us</a> </li> </ul> </nav> </div> </div> </div> </footer> </div> </div> <script type="application/json" data-drupal-selector="drupal-settings-json">{"path":{"baseUrl":"\/","scriptPath":null,"pathPrefix":"","currentPath":"node\/1340426","currentPathIsAdmin":false,"isFront":false,"currentLanguage":"en"},"pluralDelimiter":"\u0003","suppressDeprecationErrors":true,"bootstrap":{"forms_has_error_value_toggle":1,"popover_enabled":1,"popover_animation":1,"popover_auto_close":1,"popover_container":"body","popover_content":"","popover_delay":"0","popover_html":0,"popover_placement":"right","popover_selector":"","popover_title":"","popover_trigger":"click"},"linuxjournal":{"disqus":{"origin":"https:\/\/www.linuxjournal.com","prettyUrl":"\/content\/tamper-evident-boot-heads","fullUrl":"https:\/\/www.linuxjournal.com\/content\/tamper-evident-boot-heads","embedUrl":"https:\/\/linuxjournal.disqus.com\/embed.js","shortname":"linuxjournal"}},"statistics":{"data":{"nid":"1340426"},"url":"\/core\/modules\/statistics\/statistics.php"},"ajaxTrustedUrl":{"\/search\/node":true},"user":{"uid":0,"permissionsHash":"a7b3a803411eb9cbd5d7d374ffb326721ee8274ab5a665df8f38311e3aad858c"}}</script> <script src="/core/assets/vendor/jquery/jquery.min.js?v=3.6.3"></script> <script src="/core/assets/vendor/underscore/underscore-min.js?v=1.13.6"></script> <script src="/core/misc/polyfills/element.matches.js?v=9.5.9"></script> <script src="/core/misc/polyfills/object.assign.js?v=9.5.9"></script> <script src="/core/assets/vendor/once/once.min.js?v=1.0.1"></script> <script src="/core/assets/vendor/jquery-once/jquery.once.min.js?v=9.5.9"></script> <script src="/core/misc/drupalSettingsLoader.js?v=9.5.9"></script> <script src="/core/misc/drupal.js?v=9.5.9"></script> <script src="/core/misc/drupal.init.js?v=9.5.9"></script> <script src="/themes/contrib/bootstrap/js/drupal.bootstrap.js?smwnh4"></script> <script src="/themes/contrib/bootstrap/js/attributes.js?smwnh4"></script> <script src="/themes/contrib/bootstrap/js/theme.js?smwnh4"></script> <script src="/themes/linuxjournal/bootstrap/assets/javascripts/bootstrap/affix.js?smwnh4"></script> <script src="/themes/linuxjournal/bootstrap/assets/javascripts/bootstrap/alert.js?smwnh4"></script> <script src="/themes/linuxjournal/bootstrap/assets/javascripts/bootstrap/button.js?smwnh4"></script> <script src="/themes/linuxjournal/bootstrap/assets/javascripts/bootstrap/carousel.js?smwnh4"></script> <script src="/themes/linuxjournal/bootstrap/assets/javascripts/bootstrap/collapse.js?smwnh4"></script> <script src="/themes/linuxjournal/bootstrap/assets/javascripts/bootstrap/dropdown.js?smwnh4"></script> <script src="/themes/linuxjournal/bootstrap/assets/javascripts/bootstrap/modal.js?smwnh4"></script> <script src="/themes/linuxjournal/bootstrap/assets/javascripts/bootstrap/tooltip.js?smwnh4"></script> <script src="/themes/linuxjournal/bootstrap/assets/javascripts/bootstrap/popover.js?smwnh4"></script> <script src="/themes/linuxjournal/bootstrap/assets/javascripts/bootstrap/scrollspy.js?smwnh4"></script> <script src="/themes/linuxjournal/bootstrap/assets/javascripts/bootstrap/tab.js?smwnh4"></script> <script src="/themes/linuxjournal/bootstrap/assets/javascripts/bootstrap/transition.js?smwnh4"></script> <script src="/themes/linuxjournal/js/lj-consentmanager.js?smwnh4"></script> <script src="/modules/contrib/webform/js/webform.behaviors.js?v=9.5.9"></script> <script src="/core/misc/jquery.once.bc.js?v=9.5.9"></script> <script src="/core/misc/states.js?v=9.5.9"></script> <script src="/themes/contrib/bootstrap/js/misc/states.js?smwnh4"></script> <script src="/modules/contrib/webform/js/webform.states.js?v=9.5.9"></script> <script src="/modules/contrib/webform/modules/webform_bootstrap/js/webform_bootstrap.states.js?v=9.5.9"></script> <script src="/themes/contrib/bootstrap/js/popover.js?smwnh4"></script> <script src="/core/modules/statistics/statistics.js?v=9.5.9"></script> <script src="/themes/linuxjournal/js/lj-disqus.js?smwnh4"></script> <script src="/libraries/shariff/shariff.complete.js?v=9.5.9"></script> <!-- START EMBED --> <noscript><img src="https://api.b2c.com/api/noscript-448i7exgpyqpr9c144q.gif"></noscript> <!-- END EMBED --> <!-- Matomo --> <script type="text/javascript"> var _paq = _paq || []; function initPiwik() { _paq.push(['trackPageView']); _paq.push(['enableLinkTracking']); (function() { var u="https://analytics.linuxjournal.com/"; _paq.push(['setTrackerUrl', u+'piwik.php']); _paq.push(['setSiteId', '50']); var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0]; g.type='text/javascript'; g.async=true; g.defer=true; g.src=u+'piwik.js'; s.parentNode.insertBefore(g,s); })(); } </script> <noscript><p><img src="https://analytics.linuxjournal.com/piwik.php?idsite=50&rec=1" style="border:0;" alt="" /></p></noscript> <!-- End Matomo Code --> <div class="modal-custom overlay-custom" id="ccpa-modal" style="margin-left: 0; display: none; max-width: 100%; width: 100%"> <div id="modal-content" class="modal-content"> <div class="modal-header" id="ccpa-modal-content-destination"></div> <span class="close" id="modal-close">×</span> </div> </div> </body> </html>