github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/cmds/boot/stboot/network.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 "context" 9 "crypto/tls" 10 "crypto/x509" 11 "encoding/pem" 12 "errors" 13 "fmt" 14 "io" 15 "io/ioutil" 16 "log" 17 "net" 18 "net/http" 19 "os" 20 "strconv" 21 "strings" 22 "time" 23 24 "github.com/u-root/u-root/pkg/boot/stboot" 25 "github.com/u-root/u-root/pkg/dhclient" 26 "github.com/vishvananda/netlink" 27 ) 28 29 func configureStaticNetwork(vars stboot.HostVars) error { 30 log.Printf("Setup network configuration with IP: " + vars.HostIP) 31 addr, err := netlink.ParseAddr(vars.HostIP) 32 if err != nil { 33 return fmt.Errorf("Error parsing HostIP string to CIDR format address: %v", err) 34 } 35 36 link, err := findNetworkInterface() 37 if err != nil { 38 return err 39 } 40 41 if err = netlink.AddrAdd(link, addr); err != nil { 42 return fmt.Errorf("Error adding address: %v", err) 43 } 44 45 if err = netlink.LinkSetUp(link); err != nil { 46 return fmt.Errorf("Error bringing up interface: %v", err) 47 } 48 49 gateway, err := netlink.ParseAddr(vars.DefaultGateway) 50 if err != nil { 51 return fmt.Errorf("Error parsing GatewayIP string to CIDR format address: %v", err) 52 } 53 54 r := &netlink.Route{LinkIndex: link.Attrs().Index, Gw: gateway.IPNet.IP} 55 if err = netlink.RouteAdd(r); err != nil { 56 return fmt.Errorf("Error setting default gateway: %v", err) 57 } 58 59 return nil 60 } 61 62 func configureDHCPNetwork() error { 63 log.Printf("Trying to configure network configuration dynamically...") 64 65 link, err := findNetworkInterface() 66 if err != nil { 67 return err 68 } 69 70 var links []netlink.Link 71 links = append(links, link) 72 73 var level dhclient.LogLevel 74 if *doDebug { 75 level = 1 76 } else { 77 level = 0 78 } 79 config := dhclient.Config{ 80 Timeout: interfaceUpTimeout, 81 Retries: 4, 82 LogLevel: level, 83 } 84 85 r := dhclient.SendRequests(context.TODO(), links, true, false, config, 30*time.Second) 86 for result := range r { 87 if result.Err == nil { 88 return result.Lease.Configure() 89 } else if *doDebug { 90 log.Printf("dhcp response error: %v", result.Err) 91 } 92 } 93 return errors.New("no valid DHCP configuration recieved") 94 } 95 96 func findNetworkInterface() (netlink.Link, error) { 97 ifaces, err := net.Interfaces() 98 if err != nil { 99 return nil, err 100 } 101 102 if len(ifaces) == 0 { 103 return nil, errors.New("No network interface found") 104 } 105 106 var ifnames []string 107 for _, iface := range ifaces { 108 if *doDebug { 109 log.Printf("Found interface %s", iface.Name) 110 log.Printf(" MTU: %d Hardware Addr: %s", iface.MTU, iface.HardwareAddr.String()) 111 log.Printf(" Flags: %v", iface.Flags) 112 } 113 ifnames = append(ifnames, iface.Name) 114 // skip loopback 115 if iface.Flags&net.FlagLoopback != 0 || iface.HardwareAddr.String() == "" { 116 continue 117 } 118 log.Printf("Try using %s", iface.Name) 119 link, err := netlink.LinkByName(iface.Name) 120 if err == nil { 121 return link, nil 122 } 123 log.Print(err) 124 } 125 126 return nil, fmt.Errorf("Could not find a non-loopback network interface with hardware address in any of %v", ifnames) 127 } 128 129 func downloadFromHTTPS(url string, destination string) error { 130 roots := x509.NewCertPool() 131 if err := loadHTTPSCertificate(roots); err != nil { 132 return fmt.Errorf("Failed to load root certificate: %v", err) 133 } 134 135 // setup https client 136 client := http.Client{ 137 Transport: (&http.Transport{ 138 Proxy: http.ProxyFromEnvironment, 139 DialContext: (&net.Dialer{ 140 Timeout: 30 * time.Second, 141 KeepAlive: 30 * time.Second, 142 DualStack: true, 143 }).DialContext, 144 MaxIdleConns: 100, 145 IdleConnTimeout: 90 * time.Second, 146 TLSHandshakeTimeout: 10 * time.Second, 147 ExpectContinueTimeout: 1 * time.Second, 148 TLSClientConfig: (&tls.Config{ 149 RootCAs: roots, 150 }), 151 }), 152 } 153 154 // check available kernel entropy 155 e, err := ioutil.ReadFile(entropyAvail) 156 if err != nil { 157 return fmt.Errorf("Cannot evaluate entropy, %v", err) 158 } 159 es := strings.TrimSpace(string(e)) 160 entr, err := strconv.Atoi(es) 161 if err != nil { 162 return fmt.Errorf("Cannot evaluate entropy, %v", err) 163 } 164 log.Printf("Available kernel entropy: %d", entr) 165 if entr < 128 { 166 log.Print("WARNING: low entropy!") 167 log.Printf("%s : %d", entropyAvail, entr) 168 } 169 // get remote boot bundle 170 log.Print("Downloading bootball ...") 171 resp, err := client.Get(url) 172 if err != nil { 173 return err 174 } 175 defer resp.Body.Close() 176 if resp.StatusCode != 200 { 177 return fmt.Errorf("non-200 HTTP status: %d", resp.StatusCode) 178 } 179 f, err := os.Create(destination) 180 if err != nil { 181 return fmt.Errorf("failed create boot config file: %v", err) 182 } 183 defer f.Close() 184 185 _, err = io.Copy(f, resp.Body) 186 if err != nil { 187 return fmt.Errorf("failed to save bootball: %v", err) 188 } 189 190 return nil 191 } 192 193 // loadHTTPSCertificate loads the certificate needed 194 // for HTTPS and verifies it. 195 func loadHTTPSCertificate(roots *x509.CertPool) error { 196 log.Printf("Load %s as CA certificate", rootCACertPath) 197 rootCertBytes, err := ioutil.ReadFile(rootCACertPath) 198 if err != nil { 199 return err 200 } 201 rootCertPem, _ := pem.Decode(rootCertBytes) 202 if rootCertPem.Type != "CERTIFICATE" { 203 return fmt.Errorf("Failed decoding certificate: Certificate is of the wrong type. PEM Type is: %s", rootCertPem.Type) 204 } 205 ok := roots.AppendCertsFromPEM([]byte(rootCertBytes)) 206 if !ok { 207 return fmt.Errorf("Error parsing CA root certificate") 208 } 209 return nil 210 }