github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/cmds/boot/stboot/main.go (about)

     1  // Copyright 2018 the u-root Authors. All rights reserved
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package main
     6  
     7  import (
     8  	"crypto/sha256"
     9  	"encoding/hex"
    10  	"encoding/json"
    11  	"encoding/pem"
    12  	"flag"
    13  	"fmt"
    14  	"io/ioutil"
    15  	"log"
    16  	"net/url"
    17  	"path"
    18  	"strings"
    19  	"time"
    20  
    21  	"github.com/u-root/u-root/pkg/boot/stboot"
    22  	"github.com/u-root/u-root/pkg/recovery"
    23  )
    24  
    25  var debug = func(string, ...interface{}) {}
    26  
    27  var (
    28  	dryRun  = flag.Bool("dryrun", false, "Do everything except booting the loaded kernel")
    29  	doDebug = flag.Bool("d", false, "Print debug output")
    30  )
    31  
    32  const (
    33  	rootCACertPath          = "/root/LetsEncrypt_Authority_X3.pem"
    34  	rootCertFingerprintPath = "root/signing_rootcert.fingerprint"
    35  	entropyAvail            = "/proc/sys/kernel/random/entropy_avail"
    36  	interfaceUpTimeout      = 6 * time.Second
    37  )
    38  
    39  var banner = `
    40    _____ _______   _____   ____   ____________
    41   / ____|__   __|  |  _ \ / __ \ / __ \__   __|
    42  | (___    | |     | |_) | |  | | |  | | | |   
    43   \___ \   | |     |  _ <| |  | | |  | | | |   
    44   ____) |  | |     | |_) | |__| | |__| | | |   
    45  |_____/   |_|     |____/ \____/ \____/  |_|   
    46  
    47  `
    48  
    49  var check = `           
    50             //\\
    51  OS is     //  \\
    52  valid    //   //
    53          //   //
    54   //\\  //   //
    55  //  \\//   //
    56  \\        //
    57   \\      //
    58    \\    //
    59     \\__//
    60  `
    61  
    62  func main() {
    63  	log.SetPrefix("stboot: ")
    64  
    65  	flag.Parse()
    66  	if *doDebug {
    67  		debug = log.Printf
    68  	}
    69  	log.Print(banner)
    70  
    71  	vars, err := stboot.FindHostVarsInInitramfs()
    72  	if err != nil {
    73  		reboot("Cant find Netvars at all: %v", err)
    74  	}
    75  
    76  	if *doDebug {
    77  		str, _ := json.MarshalIndent(vars, "", "  ")
    78  		log.Printf("Host variables: %s", str)
    79  	}
    80  
    81  	if vars.HostIP != "" {
    82  		err = configureStaticNetwork(vars)
    83  	} else {
    84  		err = configureDHCPNetwork()
    85  	}
    86  
    87  	if err != nil {
    88  		reboot("Can not set up IO: %v", err)
    89  	}
    90  
    91  	err = validateSystemTime()
    92  	if err != nil {
    93  		reboot("%v", err)
    94  	}
    95  
    96  	ballPath := path.Join("root/", stboot.BallName)
    97  	url, err := url.Parse(vars.BootstrapURL)
    98  	if err != nil {
    99  		reboot("Invalid bootstrap URL: %v", err)
   100  	}
   101  	url.Path = path.Join(url.Path, stboot.BallName)
   102  	err = downloadFromHTTPS(url.String(), ballPath)
   103  	if err != nil {
   104  		reboot("Downloading bootball failed: %v", err)
   105  	}
   106  
   107  	ball, err := stboot.BootBallFromArchive(ballPath)
   108  	if err != nil {
   109  		reboot("Cannot open bootball: %v", err)
   110  	}
   111  
   112  	fp, err := ioutil.ReadFile(rootCertFingerprintPath)
   113  	if err != nil {
   114  		reboot("Cannot read fingerprint: %v", err)
   115  	}
   116  
   117  	if *doDebug {
   118  		log.Print("Fingerprint of boot ball's root certificate:")
   119  		log.Print(string(fp))
   120  	}
   121  	if !matchFingerprint(ball.RootCertPEM, string(fp)) {
   122  		reboot("Root certificate of boot ball does not match expacted fingerprint %v", err)
   123  	}
   124  
   125  	// Just choose the first Bootconfig for now
   126  	log.Printf("Pick the first boot configuration")
   127  	var index = 0
   128  	bc, err := ball.GetBootConfigByIndex(index)
   129  	if err != nil {
   130  		reboot("Cannot get boot configuration %d: %v", index, err)
   131  	}
   132  
   133  	if *doDebug {
   134  		str, _ := json.MarshalIndent(*bc, "", "  ")
   135  		log.Printf("Bootconfig (ID: %s): %s", bc.ID(), str)
   136  	}
   137  
   138  	n, valid, err := ball.VerifyBootconfigByID(bc.ID())
   139  	if err != nil {
   140  		reboot("Error verifying bootconfig %d: %v", index, err)
   141  	}
   142  	if valid < vars.MinimalSignaturesMatch {
   143  		reboot("Did not found enough valid signatures: %d found, %d valid, %d required", n, valid, vars.MinimalSignaturesMatch)
   144  	}
   145  
   146  	if *doDebug {
   147  		reboot("Signatures: %d found, %d valid, %d required", n, valid, vars.MinimalSignaturesMatch)
   148  	}
   149  
   150  	log.Printf("Bootconfig '%s' passed verification", bc.Name)
   151  	log.Print(check)
   152  
   153  	if *dryRun {
   154  		debug("Dryrun mode: will not boot")
   155  		return
   156  	}
   157  
   158  	log.Println("Starting up new kernel.")
   159  
   160  	if err := bc.Boot(); err != nil {
   161  		log.Printf("Failed to boot kernel %s: %v", bc.Kernel, err)
   162  	}
   163  	// if we reach this point, no boot configuration succeeded
   164  	reboot("No boot configuration succeeded")
   165  }
   166  
   167  // matchFingerprint returns true if fingerprintHex matches the SHA256
   168  // hash calculated from pem decoded certPEM.
   169  func matchFingerprint(certPEM []byte, fingerprintHex string) bool {
   170  	block, _ := pem.Decode(certPEM)
   171  	fp := sha256.Sum256(block.Bytes)
   172  	str := hex.EncodeToString(fp[:])
   173  	str = strings.TrimSpace(str)
   174  
   175  	fingerprintHex = strings.TrimSpace(fingerprintHex)
   176  
   177  	return str == fingerprintHex
   178  }
   179  
   180  //reboot trys to reboot the system in an infinity loop
   181  func reboot(format string, v ...interface{}) {
   182  	for {
   183  		recover := recovery.SecureRecoverer{
   184  			Reboot:   true,
   185  			Debug:    true,
   186  			RandWait: true,
   187  		}
   188  		err := recover.Recover(fmt.Sprintf(format, v...))
   189  		if err != nil {
   190  			continue
   191  		}
   192  	}
   193  }