1 module symmetry.linux.namespace; 2 import symmetry.sildoc; 3 4 version(Posix): 5 6 extern(C) @nogc nothrow int setns(int fd, int nstype); 7 8 import symmetry.linux.process : CloneFlag, posixCloneFlags; 9 import symmetry.linux.file : fdopen; 10 import symmetry.linux.cgroups: ChildConfig; 11 import symmetry.linux.mount : fnMount, unmount; 12 import core.sys.linux.mount : MountType, UmountFlag; 13 14 private auto system(string s) 15 { 16 import std.process: executeShell; 17 return executeShell("sudo " ~ s); 18 } 19 20 void setNamespace(string path, CloneFlag[] cloneFlags) 21 { 22 import core.sys.posix.fcntl: O_RDONLY; 23 if (path.length ==0) 24 return; 25 auto flags = posixCloneFlags(cloneFlags); 26 setNamespace_(path,flags); 27 } 28 29 version(Posix) 30 void setNamespace_(string path, int flags) 31 { 32 import core.sys.posix.fcntl: O_RDONLY; 33 if (path.length ==0) 34 return; 35 auto fd = fdopen(path, O_RDONLY); 36 setns(fd,flags); 37 } 38 39 void createNetworkNamespace(string name) 40 { 41 import std.format : format; 42 import std.exception : enforce; 43 deleteNetworkNamespace(name); 44 auto ret = system(format!"ip netns add %s"(name)); 45 enforce(ret.status ==0, ret.output); 46 } 47 48 void deleteNetworkNamespace(string name) 49 { 50 import std.format : format; 51 import std.exception : enforce; 52 auto ret = system(format!"ip netns del %s"(name)); 53 } 54 55 void createVethPair(string name, string peerName) 56 { 57 import std.format : format; 58 import std.exception : enforce; 59 auto ret = system(format!"ip link add %s type veth peer name %s"(name, peerName)); 60 enforce(ret.status ==0, ret.output); 61 } 62 63 void deleteVethPair(string name) 64 { 65 import std.format : format; 66 import std.exception : enforce; 67 auto ret = system(format!"ip link delete %s"(name)); 68 } 69 70 void addVethPairToNamespace(string peerName, string namespace) 71 { 72 import std.exception : enforce; 73 import std.format : format; 74 auto ret = system(format!"ip link set %s netns %s"(peerName, namespace)); 75 enforce(ret.status ==0, ret.output); 76 } 77 78 void addAddressesIP4(string addresses, string deviceName, string namespace = null) 79 { 80 import std.exception : enforce; 81 import std.format : format; 82 auto ns = (namespace.length > 0) ? "-n " ~ namespace ~ " ": ""; 83 auto ret = system(format!"ip %saddr add %s dev %s"(ns,addresses,deviceName)); 84 enforce(ret.status ==0, ret.output); 85 } 86 87 void addAddressesIP4Peer(string nameSpace, string addresses, string deviceName) 88 { 89 import std.exception : enforce; 90 import std.format : format; 91 auto ret = system(format!"ip netns exec %s ip addr add %s dev %s"(nameSpace,addresses,deviceName)); 92 enforce(ret.status ==0, ret.output); 93 } 94 95 // return addresses in JSON format 96 string showAddresses(string nameSpace = null) 97 { 98 import std.exception : enforce; 99 import std.format : format; 100 auto ns = (nameSpace.length > 0) ? "-n " ~ nameSpace ~ " ": ""; 101 auto ret = system(format!"ip -j %saddr show"(ns)); 102 enforce(ret.status ==0, ret.output); 103 return ret.output; 104 } 105 106 void setLinkUpPeer(string nameSpace, string deviceName) 107 { 108 import std.exception : enforce; 109 import std.format : format; 110 auto ret = system(format!"ip netns exec %s ip link set %s up"(nameSpace, deviceName)); 111 enforce(ret.status ==0, ret.output); 112 ret = system("dhcpcd"); 113 enforce(ret.status ==0, ret.output); 114 } 115 116 void setLinkUp(string deviceName, string nameSpace = null) 117 { 118 import std.exception : enforce; 119 import std.format : format; 120 auto ns = (nameSpace.length > 0) ? "-n " ~ nameSpace ~ " ": ""; 121 auto ret = system(format!"ip %slink set %s up"(ns,deviceName)); 122 enforce(ret.status ==0, ret.output); 123 } 124 125 // return link info in JSON format 126 string showLink(string nameSpace = null) 127 { 128 import std.exception : enforce; 129 import std.format : format; 130 auto ns = (nameSpace.length > 0) ? "-n " ~ nameSpace ~ " ": ""; 131 auto ret = system(format!"ip -j %slink show"(ns)); 132 enforce(ret.status ==0, ret.output); 133 return ret.output; 134 } 135 136 // return route info in JSON format 137 string showRoute(string nameSpace = null) 138 { 139 import std.exception : enforce; 140 import std.format : format; 141 auto ns = (nameSpace.length > 0) ? "-n " ~ nameSpace ~ " ": ""; 142 auto ret = system(format!"ip -j %sroute show"(ns)); 143 enforce(ret.status ==0, ret.output); 144 return ret.output; 145 } 146 147 // return route get info in JSON format 148 string getRoute(string destinationIP, string nameSpace = null) 149 { 150 import std.exception : enforce; 151 import std.format : format; 152 auto ns = (nameSpace.length > 0) ? "-n " ~ nameSpace ~ " ": ""; 153 auto ret = system(format!"ip -j %sroute get %s"(ns,destinationIP)); 154 enforce(ret.status ==0, ret.output); 155 return ret.output; 156 } 157 158 // return route get info in JSON format 159 string addRoute(string route, string device, string nameSpace = null) 160 { 161 import std.exception : enforce; 162 import std.format : format; 163 auto ns = (nameSpace.length > 0) ? "-n " ~ nameSpace ~ " ": ""; 164 auto ret = system(format!"ip -j %sroute add %s dev %s"(ns,route,device)); 165 enforce(ret.status ==0, ret.output); 166 return ret.output; 167 } 168 169 string executeNamespace(string nameSpace, string cmd) 170 { 171 import std.exception : enforce; 172 import std.format : format; 173 auto ret = system(format!"ip netns exec %s %s"(nameSpace,cmd)); 174 enforce(ret.status ==0, ret.output); 175 return ret.output; 176 } 177 178 void addDefaultRoutePeer(string nameSpace, string defaultGateway) 179 { 180 import std.exception : enforce; 181 import std.format : format; 182 auto ret = system(format!"ip netns exec %s ip route add default via %s"(nameSpace,defaultGateway)); 183 enforce(ret.status ==0, ret.output); 184 } 185 186 void setHostForwarding(bool enableForwarding = true) 187 { 188 import std.file : write; 189 // Enable IP-forwarding. 190 write("/proc/sys/net/ipv4/ip_forward",enableForwarding ? "1\n" : "0\n"); 191 } 192 193 194 void allowForwarding(string hostDeviceName, string clientDeviceName) 195 { 196 import std.exception : enforce; 197 import std.format : format; 198 199 // Allow forwarding between eth0 and v-eth1. 200 auto ret = system(format!"iptables -A FORWARD -i %s -o %s -j ACCEPT"(hostDeviceName, clientDeviceName)); 201 enforce(ret.status ==0, ret.output); 202 ret = system(format!"iptables -A FORWARD -o %s -i %s -j ACCEPT"(hostDeviceName,clientDeviceName)); 203 enforce(ret.status ==0, ret.output); 204 } 205 206 void flushNatRules() 207 { 208 import std.exception : enforce; 209 import std.file : write; 210 import std.format : format; 211 // Flush nat rules. 212 auto ret = system("iptables -t nat -F"); 213 enforce(ret.status ==0, ret.output); 214 } 215 216 void enableMasquerading(string peerAddress, string interfaceName) 217 { 218 import std.exception : enforce; 219 import std.file : write; 220 import std.format : format; 221 // Enable masquerading of 10.200.1.0. 222 auto ret = system(format!"iptables -t nat -A POSTROUTING -s %s/255.255.255.0 -o %s -j MASQUERADE"(peerAddress,interfaceName)); 223 enforce(ret.status ==0, ret.output); 224 } 225 226 void setPolicyDropDefault() 227 { 228 import std.exception : enforce; 229 import std.file : write; 230 import std.format : format; 231 // set policy DROP by default 232 auto ret = system("iptables -P FORWARD DROP"); 233 enforce(ret.status ==0, ret.output); 234 } 235 236 void flushForwardRules() 237 { 238 import std.exception : enforce; 239 import std.file : write; 240 import std.format : format; 241 // set policy DROP by default 242 auto ret = system("iptables -F FORWARD"); 243 enforce(ret.status ==0, ret.output); 244 } 245 246 @SILdoc("Share internet access between host and namespace") 247 void setHostForwarding(string hostDeviceName, string clientDeviceName, string address) 248 { 249 import std.exception : enforce; 250 import std.file : write; 251 import std.format : format; 252 253 setHostForwarding(true); 254 setPolicyDropDefault(); 255 flushForwardRules(); 256 flushNatRules(); 257 enableMasquerading(address,hostDeviceName); 258 allowForwarding(hostDeviceName,clientDeviceName); 259 } 260 261 262 void containNetwork(string namespace, string hostDeviceName, string deviceName, string peerName, string addresses = "10.200.1.1/24") 263 { 264 import std.exception : enforce; 265 import std.format : format; 266 import std..string : lastIndexOf; 267 268 createNetworkNamespace(namespace); 269 createVethPair(deviceName,peerName); 270 addVethPairToNamespace(peerName,namespace); 271 addAddressesIP4(addresses,deviceName); 272 setLinkUp(deviceName); 273 addAddressesIP4Peer(namespace,addresses,peerName); 274 setLinkUpPeer(namespace,peerName); 275 setLinkUpPeer(namespace,"lo"); 276 addDefaultRoutePeer(namespace,"10.200.1.1"); 277 auto address = addresses[0..addresses.lastIndexOf(".")] ~ ".0"; 278 setHostForwarding(hostDeviceName, deviceName, address); 279 } 280 281 void mounts(ChildConfig config) 282 { 283 import std.exception : enforce; 284 import std.file : tempDir, rmdir,chdir; 285 import std.path : baseName; 286 import std.format : format; 287 import std.stdio: stderr, writeln, writefln; 288 289 stderr.writeln("=> remounting everyting with MS_PRIVATE..."); 290 fnMount(null,"/",null, [MountType.rec, MountType.private_], null); 291 stderr.writeln("=> making a temp directory and a bind mount there..."); 292 auto mountDir = tempDir(); 293 enforce(mountDir != ".", "failed making a temp directory"); 294 fnMount(config.mountDir,mountDir,null,[MountType.bind, MountType.private_], null); 295 auto innerMountDir = format!"%s/oldroot"(mountDir); 296 stderr.writeln("done"); 297 stderr.writeln("=> pivoting root"); 298 auto oldRootDir = format!"%s/"(innerMountDir.baseName()); 299 auto oldRoot = oldRootDir; 300 chdir("/"); 301 unmount(oldRoot,[UmountFlag.detach]); 302 rmdir(oldRoot); 303 stderr.writefln("done"); 304 } 305 306 307 void teardownChroot(ChildConfig childConfig) 308 { 309 import std.format : format; 310 foreach(m; [ "proc", "sys", "sys/firmware/efi/efivars", "dev/pts", "dev/shm", "dev", "run", "tmp"]) 311 { 312 unmount(format!"%s/%s"(childConfig.mountDir,m)); 313 } 314 } 315 316 void setupChroot(ChildConfig childConfig) 317 { 318 import std.format : format; 319 fnMount("proc",format!"%s/proc"(childConfig.mountDir), "proc",[MountType.noSUID,MountType.noExec, MountType.noDev]); 320 fnMount("sys",format!"%s/sys"(childConfig.mountDir), "sysfs",[MountType.noSUID,MountType.noExec, MountType.noDev,MountType.readOnly]); 321 try 322 { 323 fnMount("efivars",format!"%s/sys/firmware/efi/efivars"(childConfig.mountDir), "efivars",[MountType.noSUID,MountType.noExec, MountType.noDev]); 324 } 325 catch(Exception e) 326 { 327 } 328 329 fnMount("udev",format!"%s/dev"(childConfig.mountDir), "devtmpfs",[MountType.noSUID],"mode=0755"); 330 fnMount("devpts",format!"%s/dev/pts"(childConfig.mountDir), "devpts",[MountType.noSUID,MountType.noExec],"mode=0620,gid=5"); 331 fnMount("shm",format!"%s/dev/shm"(childConfig.mountDir), "tmpfs",[MountType.noSUID,MountType.noDev],"mode=1777"); 332 fnMount("/run",format!"%s/run"(childConfig.mountDir), "--bind",[]); 333 fnMount("tmp",format!"%s/tmp"(childConfig.mountDir), "tmpfs",[MountType.strictAccessTime,MountType.noDev,MountType.noSUID],"mode=1777"); 334 }