github.com/Cloud-Foundations/Dominator@v0.3.4/hypervisor/metadatad/daemons.go (about) 1 package metadatad 2 3 import ( 4 "fmt" 5 "net" 6 "net/http" 7 "os/exec" 8 "runtime" 9 "strconv" 10 "time" 11 12 "github.com/Cloud-Foundations/Dominator/lib/log" 13 "github.com/Cloud-Foundations/Dominator/lib/log/prefixlogger" 14 libnet "github.com/Cloud-Foundations/Dominator/lib/net" 15 "github.com/Cloud-Foundations/Dominator/lib/wsyscall" 16 proto "github.com/Cloud-Foundations/Dominator/proto/hypervisor" 17 ) 18 19 type statusType struct { 20 namespaceFd int 21 threadId int 22 err error 23 } 24 25 func blockMetadataOnInterface(ifName string) error { 26 cmd := exec.Command("ebtables", "-t", "filter", "-A", "INPUT", 27 "-i", ifName, "-p", "ip", 28 "--ip-src", "169.254.0.0/16", "-j", "DROP") 29 if output, err := cmd.CombinedOutput(); err != nil { 30 return fmt.Errorf("error running ebtables: %s: %s", 31 err, string(output)) 32 } 33 cmd = exec.Command("ebtables", "-t", "filter", "-A", "FORWARD", 34 "-i", ifName, "-p", "ip", 35 "--ip-src", "169.254.0.0/16", "-j", "DROP") 36 if output, err := cmd.CombinedOutput(); err != nil { 37 return fmt.Errorf("error running ebtables: %s: %s", 38 err, string(output)) 39 } 40 cmd = exec.Command("ebtables", "-t", "filter", "-A", "FORWARD", 41 "-o", ifName, "-p", "ip", 42 "--ip-dst", "169.254.0.0/16", "-j", "DROP") 43 if output, err := cmd.CombinedOutput(); err != nil { 44 return fmt.Errorf("error running ebtables: %s: %s", 45 err, string(output)) 46 } 47 cmd = exec.Command("ebtables", "-t", "filter", "-A", "OUTPUT", 48 "-o", ifName, "-p", "ip", 49 "--ip-dst", "169.254.0.0/16", "-j", "DROP") 50 if output, err := cmd.CombinedOutput(); err != nil { 51 return fmt.Errorf("error running ebtables: %s: %s", 52 err, string(output)) 53 } 54 return nil 55 } 56 57 func httpServe(listener net.Listener, handler http.Handler, 58 idleTimeout time.Duration) error { 59 httpServer := &http.Server{Handler: handler, IdleTimeout: idleTimeout} 60 return httpServer.Serve(listener) 61 } 62 63 func (s *server) startServer() error { 64 cmd := exec.Command("ebtables", "-t", "filter", "-F") 65 if output, err := cmd.CombinedOutput(); err != nil { 66 return fmt.Errorf("error running ebtables: %s: %s", err, string(output)) 67 } 68 cmd = exec.Command("ebtables", "-t", "nat", "-F") 69 if output, err := cmd.CombinedOutput(); err != nil { 70 return fmt.Errorf("error running ebtables: %s: %s", err, string(output)) 71 } 72 // Block input/forwarding/output of metadata packets for all EtherNet 73 // interfaces. This ensures that metadata traffic cannot leave the host. 74 broadcastInterfaces, _, err := libnet.ListBroadcastInterfaces( 75 libnet.InterfaceTypeEtherNet, s.logger) 76 if err != nil { 77 return err 78 } 79 for _, iface := range broadcastInterfaces { 80 if err := blockMetadataOnInterface(iface.Name); err != nil { 81 return err 82 } 83 } 84 runtime.LockOSThread() 85 defer runtime.UnlockOSThread() 86 for _, bridge := range s.bridges { 87 if err := s.startServerOnBridge(bridge); err != nil { 88 return err 89 } 90 } 91 return nil 92 } 93 94 func (s *server) startServerOnBridge(bridge net.Interface) error { 95 logger := prefixlogger.New(bridge.Name+": ", s.logger) 96 startChannel := make(chan struct{}) 97 statusChannel := make(chan statusType, 1) 98 go s.createNamespace(startChannel, statusChannel, logger) 99 status := <-statusChannel 100 if status.err != nil { 101 return status.err 102 } 103 if err := createInterface(bridge, status.threadId, logger); err != nil { 104 return err 105 } 106 startChannel <- struct{}{} 107 status = <-statusChannel 108 if status.err != nil { 109 return status.err 110 } 111 subnetChannel := s.manager.MakeSubnetChannel() 112 go s.addSubnets(bridge, status.namespaceFd, subnetChannel, logger) 113 return nil 114 } 115 116 func (s *server) addSubnets(bridge net.Interface, namespaceFd int, 117 subnetChannel <-chan proto.Subnet, logger log.DebugLogger) { 118 logger.Debugf(0, "waiting for subnet updates in namespaceFD=%d\n", 119 namespaceFd) 120 if err := wsyscall.SetNetNamespace(namespaceFd); err != nil { 121 logger.Println(err) 122 return 123 } 124 for subnet := range subnetChannel { 125 addRouteForBridge(bridge, subnet, logger) 126 } 127 } 128 129 func addRouteForBridge(bridge net.Interface, subnet proto.Subnet, 130 logger log.DebugLogger) { 131 if subnet.DisableMetadata { 132 logger.Debugf(0, "metadata service disabled for subnet: %s\n", 133 subnet.Id) 134 return 135 } 136 subnetMask := net.IPMask(subnet.IpMask) 137 subnetAddr := subnet.IpGateway.Mask(subnetMask) 138 addr := subnetAddr.String() 139 mask := fmt.Sprintf("%d.%d.%d.%d", 140 subnetMask[0], subnetMask[1], subnetMask[2], subnetMask[3]) 141 cmd := exec.Command("route", "add", "-net", addr, "netmask", mask, "eth0") 142 if output, err := cmd.CombinedOutput(); err != nil { 143 logger.Printf("error adding route: for subnet: %s: %s/%s: %s: %s", 144 subnet.Id, addr, mask, err, string(output)) 145 } else { 146 logger.Debugf(0, "added route for subnet: %s: %s/%s\n", 147 subnet.Id, addr, mask) 148 } 149 } 150 151 func (s *server) createNamespace(startChannel <-chan struct{}, 152 statusChannel chan<- statusType, logger log.DebugLogger) { 153 namespaceFd, threadId, err := wsyscall.UnshareNetNamespace() 154 if err != nil { 155 statusChannel <- statusType{err: err} 156 return 157 } 158 statusChannel <- statusType{namespaceFd: namespaceFd, threadId: threadId} 159 <-startChannel 160 cmd := exec.Command("ifconfig", "eth0", "169.254.169.254", "netmask", 161 "255.255.255.255", "up") 162 if err := cmd.Run(); err != nil { 163 statusChannel <- statusType{err: err} 164 return 165 } 166 hypervisorListener, err := net.Listen("tcp", 167 fmt.Sprintf("169.254.169.254:%d", s.hypervisorPortNum)) 168 if err != nil { 169 statusChannel <- statusType{err: err} 170 return 171 } 172 metadataListener, err := net.Listen("tcp", "169.254.169.254:80") 173 if err != nil { 174 statusChannel <- statusType{err: err} 175 return 176 } 177 statusChannel <- statusType{namespaceFd: namespaceFd, threadId: threadId} 178 logger.Printf("starting metadata server in thread: %d\n", threadId) 179 go httpServe(hypervisorListener, nil, time.Second*5) 180 httpServe(metadataListener, s, time.Second*5) 181 } 182 183 func createInterface(bridge net.Interface, threadId int, 184 logger log.DebugLogger) error { 185 localName := bridge.Name + "-ll" 186 remoteName := bridge.Name + "-lr" 187 if _, err := net.InterfaceByName(localName); err == nil { 188 exec.Command("ip", "link", "delete", localName).Run() 189 } 190 cmd := exec.Command("ip", "link", "add", localName, "type", "veth", 191 "peer", "name", remoteName) 192 if output, err := cmd.CombinedOutput(); err != nil { 193 return fmt.Errorf("error creating veth for bridge: %s: %s: %s", 194 bridge.Name, err, output) 195 } 196 cmd = exec.Command("ifconfig", localName, "up") 197 if output, err := cmd.CombinedOutput(); err != nil { 198 return fmt.Errorf("error bringing up local interface: %s: %s: %s", 199 localName, err, output) 200 } 201 remoteInterface, err := net.InterfaceByName(remoteName) 202 if err != nil { 203 return err 204 } 205 cmd = exec.Command("ip", "link", "set", remoteName, "netns", 206 strconv.FormatInt(int64(threadId), 10), "name", "eth0") 207 if output, err := cmd.CombinedOutput(); err != nil { 208 return fmt.Errorf("error moving interface to namespace: %s: %s: %s", 209 remoteName, err, output) 210 } 211 cmd = exec.Command("ip", "link", "set", localName, "master", bridge.Name) 212 if output, err := cmd.CombinedOutput(); err != nil { 213 return fmt.Errorf("error adding interface: %s to bridge: %s: %s: %s", 214 localName, bridge.Name, err, output) 215 } 216 hwAddr := remoteInterface.HardwareAddr.String() 217 cmd = exec.Command("ebtables", "-t", "nat", "-A", "PREROUTING", 218 "--logical-in", bridge.Name, "-p", "ip", 219 "--ip-dst", "169.254.0.0/16", "-j", "dnat", "--to-destination", 220 hwAddr) 221 if output, err := cmd.CombinedOutput(); err != nil { 222 return fmt.Errorf( 223 "error adding ebtables dnat to: %s to bridge: %s: %s: %s", 224 hwAddr, bridge.Name, err, output) 225 } 226 logger.Printf("created veth, remote addr: %s\n", hwAddr) 227 return nil 228 }