TYPE3
[iec.git] / src / type3_AndroidCloud / anbox-master / src / anbox / container / lxc_container.cpp
1 /*
2  * Copyright (C) 2016 Simon Fels <morphis@gravedo.de>
3  *
4  * This program is free software: you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License version 3, as published
6  * by the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranties of
10  * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11  * PURPOSE.  See the GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program.  If not, see <http://www.gnu.org/licenses/>.
15  *
16  */
17
18 #include "anbox/android/ip_config_builder.h"
19 #include "anbox/common/binder_device_allocator.h"
20 #include "anbox/common/binder_device.h"
21 #include "anbox/container/lxc_container.h"
22 #include "anbox/system_configuration.h"
23 #include "anbox/logger.h"
24 #include "anbox/utils.h"
25
26 #include <map>
27 #include <stdexcept>
28 #include <fstream>
29 #include <sstream>
30
31 #include <boost/filesystem.hpp>
32 #include <boost/throw_exception.hpp>
33
34 #include <sys/capability.h>
35 #include <sys/prctl.h>
36 #include <sys/types.h>
37 #include <sys/wait.h>
38 #include <sys/stat.h>
39
40 #include <unistd.h>
41
42 namespace fs = boost::filesystem;
43
44 namespace {
45 constexpr unsigned int unprivileged_uid{100000};
46 constexpr unsigned int android_system_uid{1000};
47 constexpr const char *default_container_ip_address{"192.168.250.2"};
48 constexpr const std::uint32_t default_container_ip_prefix_length{24};
49 constexpr const char *default_host_ip_address{"192.168.250.1"};
50 constexpr const char *default_dns_server{"8.8.8.8"};
51 constexpr int num_needed_binders{1};
52
53 #ifdef ENABLE_LXC2_SUPPORT
54 constexpr const char *lxc_config_idmap_key{"lxc.id_map"};
55 constexpr const char *lxc_config_net_type_key{"lxc.network.type"};
56 constexpr const char *lxc_config_net_flags_key{"lxc.network.flags"};
57 constexpr const char *lxc_config_net_link_key{"lxc.network.link"};
58 constexpr const char *lxc_config_pty_max_key{"lxc.pts"};
59 constexpr const char *lxc_config_tty_max_key{"lxc.tty"};
60 constexpr const char *lxc_config_uts_name_key{"lxc.utsname"};
61 constexpr const char *lxc_config_tty_dir_key{"lxc.devttydir"};
62 constexpr const char *lxc_config_init_cmd_key{"lxc.init_cmd"};
63 constexpr const char *lxc_config_rootfs_path_key{"lxc.rootfs"};
64 constexpr const char *lxc_config_log_level_key{"lxc.loglevel"};
65 constexpr const char *lxc_config_log_file_key{"lxc.logfile"};
66 constexpr const char *lxc_config_apparmor_profile_key{"lxc.aa_profile"};
67 #else
68 constexpr const char *lxc_config_idmap_key{"lxc.idmap"};
69 constexpr const char *lxc_config_net_type_key{"lxc.net.0.type"};
70 constexpr const char *lxc_config_net_flags_key{"lxc.net.0.flags"};
71 constexpr const char *lxc_config_net_link_key{"lxc.net.0.link"};
72 constexpr const char *lxc_config_pty_max_key{"lxc.pty.max"};
73 constexpr const char *lxc_config_tty_max_key{"lxc.tty.max"};
74 constexpr const char *lxc_config_uts_name_key{"lxc.uts.name"};
75 constexpr const char *lxc_config_tty_dir_key{"lxc.tty.dir"};
76 constexpr const char *lxc_config_init_cmd_key{"lxc.init.cmd"};
77 constexpr const char *lxc_config_rootfs_path_key{"lxc.rootfs.path"};
78 constexpr const char *lxc_config_log_level_key{"lxc.log.level"};
79 constexpr const char *lxc_config_log_file_key{"lxc.log.file"};
80 constexpr const char *lxc_config_apparmor_profile_key{"lxc.apparmor.profile"};
81 #endif
82
83 constexpr int device_major(dev_t dev) {
84   return int(((dev >> 8) & 0xfff) | ((dev >> 32) & (0xfffff000)));
85 }
86
87 constexpr int device_minor(dev_t dev) {
88   return int((dev & 0xff) | ((dev >> 12) & (0xffffff00)));
89 }
90 } // namespace
91
92 namespace anbox {
93 namespace container {
94 LxcContainer::LxcContainer(bool privileged,
95                            bool rootfs_overlay,
96                            const std::string& container_network_address,
97                            const std::string &container_network_gateway,
98                            const std::vector<std::string> &container_network_dns_servers,
99                            const network::Credentials &creds)
100     : state_(State::inactive),
101       container_(nullptr),
102       privileged_(privileged),
103       rootfs_overlay_(rootfs_overlay),
104       container_network_address_(container_network_address),
105       container_network_gateway_(container_network_gateway),
106       container_network_dns_servers_(container_network_dns_servers),
107       creds_(creds) {
108   utils::ensure_paths({
109       SystemConfiguration::instance().container_config_dir(),
110       SystemConfiguration::instance().container_state_dir(),
111       SystemConfiguration::instance().log_dir(),
112   });
113 }
114
115 LxcContainer::~LxcContainer() {
116   stop();
117   if (container_)
118     lxc_container_put(container_);
119 }
120
121 void LxcContainer::setup_id_map() {
122   const auto base_id = unprivileged_uid;
123   const auto max_id = 100000;
124
125   set_config_item(lxc_config_idmap_key, utils::string_format("u 0 %d %d", base_id, android_system_uid - 1));
126   set_config_item(lxc_config_idmap_key, utils::string_format("g 0 %d %d", base_id, android_system_uid - 1));
127
128   // We need to bind the user id for the one running the client side
129   // process as he is the owner of various socket files we bind mount
130   // into the container.
131   set_config_item(lxc_config_idmap_key, utils::string_format("u %d %d 1", android_system_uid, creds_.uid()));
132   set_config_item(lxc_config_idmap_key, utils::string_format("g %d %d 1", android_system_uid, creds_.gid()));
133
134   set_config_item(lxc_config_idmap_key, utils::string_format("u %d %d %d", android_system_uid + 1,
135                                                      base_id + android_system_uid + 1,
136                                                      max_id - creds_.uid() - 1));
137   set_config_item(lxc_config_idmap_key, utils::string_format("g %d %d %d", android_system_uid + 1,
138                                                      base_id + android_system_uid + 1,
139                                                      max_id - creds_.gid() - 1));
140 }
141
142 void LxcContainer::setup_network() {
143   if (!fs::exists("/sys/class/net/anbox0")) {
144     WARNING("Anbox bridge interface 'anbox0' doesn't exist. Network functionality will not be available");
145     return;
146   }
147
148   set_config_item(lxc_config_net_type_key, "veth");
149   set_config_item(lxc_config_net_flags_key, "up");
150   set_config_item(lxc_config_net_link_key, "anbox0");
151
152   // Instead of relying on DHCP we will give Android a static IP configuration
153   // for the virtual ethernet interface LXC creates for us. This will be bridged
154   // to the host and will allows us to have reliable network connectivity and
155   // not depend on any other system service.
156
157   android::IpConfigBuilder ip_conf;
158   ip_conf.set_version(android::IpConfigBuilder::Version::Version2);
159   ip_conf.set_assignment(android::IpConfigBuilder::Assignment::Static);
160
161   std::string address = default_container_ip_address;
162   std::uint32_t ip_prefix_length = default_container_ip_prefix_length;
163   if (!container_network_address_.empty()) {
164     auto tokens = utils::string_split(container_network_address_, '/');
165     if (tokens.size() == 1 || tokens.size() == 2)
166       address = tokens[0];
167     if (tokens.size() == 2)
168       ip_prefix_length = atoi(tokens[1].c_str());
169   }
170   ip_conf.set_link_address(address, ip_prefix_length);
171
172   std::string gateway = default_host_ip_address;
173   if (!container_network_gateway_.empty())
174     gateway = container_network_gateway_;
175   ip_conf.set_gateway(gateway);
176
177   if (container_network_dns_servers_.size() > 0)
178     ip_conf.set_dns_servers(container_network_dns_servers_);
179   else
180     ip_conf.set_dns_servers({default_dns_server});
181
182   ip_conf.set_id(0);
183
184   std::vector<std::uint8_t> buffer(512);
185   common::BinaryWriter writer(buffer.begin(), buffer.end());
186   const auto size = ip_conf.write(writer);
187
188   const auto data_ethernet_path = fs::path("data") / "misc" / "ethernet";
189   const auto ip_conf_dir = SystemConfiguration::instance().data_dir() / data_ethernet_path;
190   if (!fs::exists(ip_conf_dir))
191     fs::create_directories(ip_conf_dir);
192
193   // We have to walk through the created directory hierachy now and
194   // ensure the permissions are set correctly. Otherwise the Android
195   // system will fail to boot as it isn't allowed to write anything
196   // into these directories. As previous versions of Anbox which were
197   // published to our users did this incorrectly we need to check on
198   // every startup if those directories are still owned by root and
199   // if they are we move them over to the unprivileged user.
200   auto path = SystemConfiguration::instance().data_dir();
201   for (auto iter = data_ethernet_path.begin(); iter != data_ethernet_path.end(); iter++) {
202     path /= *iter;
203
204     struct stat st;
205     if (stat(path.c_str(), &st) < 0) {
206       WARNING("Cannot retrieve permissions of path %s", path);
207       continue;
208     }
209
210     if (st.st_uid != 0 && st.st_gid != 0)
211       continue;
212
213     if (::chown(path.c_str(), unprivileged_uid, unprivileged_uid) < 0)
214       WARNING("Failed to set owner for path '%s'", path);
215   }
216
217   const auto ip_conf_path = ip_conf_dir / "ipconfig.txt";
218   if (fs::exists(ip_conf_path))
219     fs::remove(ip_conf_path);
220
221   std::ofstream f(ip_conf_path.string(), std::ofstream::binary);
222   if (f.is_open()) {
223     f.write(reinterpret_cast<const char*>(buffer.data()), size);
224     f.close();
225   } else {
226     ERROR("Failed to write IP configuration. Network functionality will not be available.");
227   }
228 }
229
230 void LxcContainer::add_device(const std::string& device, const DeviceSpecification& spec) {
231   struct stat st;
232   const std::string *old_device_name;
233   if (!spec.old_device_name.empty())
234     old_device_name = &spec.old_device_name;
235   else
236     old_device_name = &device;
237   int r = stat(old_device_name->c_str(), &st);
238   if (r < 0) {
239     const auto msg = utils::string_format("Failed to retrieve information about device %s", device);
240     throw std::runtime_error(msg);
241   }
242
243   const auto major = device_major(st.st_rdev);
244   const auto minor = device_minor(st.st_rdev);
245   const auto mode = ((st.st_mode >> 9) << 9) | (spec.permission & ~(1 << 9));
246   const auto new_device_name = fs::basename(device);
247   const auto devices_path = fs::path(SystemConfiguration::instance().container_devices_dir());
248   const auto new_device_path = (devices_path / new_device_name).string();
249
250   const auto encoded_device_number = (minor & 0xff) | (major << 8) | ((minor & !0xff) << 12);
251   r = mknod(new_device_path.c_str(), mode, encoded_device_number);
252   if (r < 0) {
253     auto msg = utils::string_format("Failed to create node for device %s: %s",
254                                     device, strerror(errno));
255     throw std::runtime_error(msg);
256   }
257
258   auto base_uid = unprivileged_uid;
259   if (privileged_)
260     base_uid = 0;
261
262   const auto shifted_uid = base_uid + st.st_uid;
263   const auto shifted_gid = base_uid + st.st_gid;
264   r = chown(new_device_path.c_str(), shifted_uid, shifted_gid);
265   if (r < 0) {
266     auto msg = utils::string_format("Failed to change ownership of new node for %s: %s",
267                                     device, strerror(errno));
268     throw std::runtime_error(msg);
269   }
270
271   // Needed as mknod respects the umask
272   r = chmod(new_device_path.c_str(), mode);
273   if (r < 0) {
274     auto msg = utils::string_format("Failed to change mode of new node for %s: %s",
275                                     device, strerror(errno));
276     throw::std::runtime_error(msg);
277   }
278
279   auto target_path = device;
280   // Strip a leading slash as LXC doesn't like that
281   if (utils::string_starts_with(device, "/"))
282     target_path = device.substr(1, device.length() - 1);
283
284   const auto entry = utils::string_format("%s %s none bind,create=file,optional 0 0",
285                                           new_device_path, target_path);
286   set_config_item("lxc.mount.entry", entry);
287 }
288
289 bool LxcContainer::create_binder_devices(unsigned int device_count, std::vector<std::unique_ptr<common::BinderDevice>>& devices) {
290   // We will always allocate a static set of binders devices even if the container
291   // doesn't use all of them
292   for (unsigned int n = 0; n < device_count; n++) {
293     auto device = common::BinderDeviceAllocator::new_device();
294     if (!device)
295       return false;
296
297     DEBUG("Allocated binder device %s", device->path());
298     devices.push_back(std::move(device));
299   }
300
301   return true;
302 }
303
304 void LxcContainer::start(const Configuration &configuration) {
305   if (getuid() != 0)
306     throw std::runtime_error("You have to start the container as root");
307
308   if (container_ && container_->is_running(container_)) {
309     WARNING("Container already started, stopping it now");
310     container_->stop(container_);
311   }
312
313   if (!container_) {
314     const auto container_config_dir = SystemConfiguration::instance().container_config_dir();
315     DEBUG("Containers are stored in %s", container_config_dir);
316
317     // Remove container config to be be able to rewrite it
318     ::unlink(utils::string_format("%s/default/config", container_config_dir).c_str());
319
320     container_ = lxc_container_new("default", container_config_dir.c_str());
321     if (!container_)
322       throw std::runtime_error("Failed to create LXC container instance");
323
324     // If container is still running (for example after a crash) we stop it here
325     // to ensure its configuration is synchronized.
326     if (container_->is_running(container_))
327       container_->stop(container_);
328   }
329
330   // We can mount proc/sys as rw here as we will run the container unprivileged
331   // in the end
332   set_config_item("lxc.mount.auto", "proc:mixed sys:mixed cgroup:mixed");
333
334   set_config_item("lxc.autodev", "1");
335   set_config_item(lxc_config_pty_max_key, "1024");
336   set_config_item(lxc_config_tty_max_key, "0");
337   set_config_item(lxc_config_uts_name_key, "anbox");
338
339   set_config_item("lxc.group.devices.deny", "");
340   set_config_item("lxc.group.devices.allow", "");
341
342   // We can't move bind-mounts, so don't use /dev/lxc/
343   set_config_item(lxc_config_tty_dir_key, "");
344
345   set_config_item("lxc.environment", "PATH=/system/bin:/system/sbin:/system/xbin");
346
347   set_config_item(lxc_config_init_cmd_key, "/anbox-init.sh");
348
349 #ifdef ENABLE_SNAP_CONFINEMENT
350   // If we're running inside the snap environment snap-confine already created a
351   // cgroup for us we need to use as otherwise presevering a namespace wont help.
352   if (utils::is_env_set("SNAP"))
353     set_config_item("lxc.namespace.keep", "cgroup");
354 #endif
355
356   auto rootfs_path = SystemConfiguration::instance().rootfs_dir();
357   if (rootfs_overlay_)
358     rootfs_path = SystemConfiguration::instance().combined_rootfs_dir();
359
360   DEBUG("Using rootfs path %s", rootfs_path);
361   set_config_item(lxc_config_rootfs_path_key, rootfs_path);
362
363   set_config_item(lxc_config_log_level_key, "0");
364   const auto log_path = SystemConfiguration::instance().log_dir();
365   set_config_item(lxc_config_log_file_key, utils::string_format("%s/container.log", log_path).c_str());
366
367 #ifndef ENABLE_LXC2_SUPPORT
368     // Dump the console output to disk to have a chance to debug early boot problems
369     set_config_item("lxc.console.logfile", utils::string_format("%s/console.log", log_path).c_str());
370     set_config_item("lxc.console.rotate", "1");
371 #endif
372
373   setup_network();
374
375 #ifdef ENABLE_SNAP_CONFINEMENT
376   // We take the AppArmor profile snapd has defined for us as part of the
377   // anbox-support interface. The container manager itself runs within a
378   // child profile snap.anbox.container-manager//lxc too.
379   set_config_item("lxc.apparmor.profile", "snap.anbox.container-manager//container");
380 #else
381   set_config_item(lxc_config_apparmor_profile_key, "unconfined");
382 #endif
383
384   if (!privileged_)
385     setup_id_map();
386
387   auto bind_mounts = configuration.bind_mounts;
388   auto devices = configuration.devices;
389
390   // If we have binderfs support we can dynamically allocate all our devices
391   if (common::BinderDeviceAllocator::is_supported()) {
392     DEBUG("Using binderfs to allocate our own binder nodes");
393
394     std::vector<std::unique_ptr<common::BinderDevice>> binder_devices;
395     if (!create_binder_devices(num_needed_binders, binder_devices) ||
396         binder_devices.size() != num_needed_binders)
397       throw std::runtime_error("Failed to allocate necessary binder devices");
398
399     bind_mounts.insert({binder_devices[0]->path().string(), "/dev/binder"});
400     binder_devices_ = std::move(binder_devices);
401   } else {
402     DEBUG("Using static binder device /dev/binder");
403     devices.insert({"/dev/binder", { 0666 }});
404   }
405
406   for (const auto &bind_mount : bind_mounts) {
407     std::string create_type = "file";
408
409     if (fs::is_directory(bind_mount.first))
410       create_type = "dir";
411
412     auto target_path = bind_mount.second;
413     // The target path needs to be absolute and pointing to the right
414     // location inside the target rootfs as otherwise we get problems
415     // when running in confined environments like snap's.
416     if (!utils::string_starts_with(target_path, "/"))
417       target_path = std::string("/") + target_path;
418     target_path = rootfs_path + target_path;
419
420     const auto entry = utils::string_format("%s %s none bind,create=%s,optional 0 0",
421                                             bind_mount.first, target_path, create_type);
422     set_config_item("lxc.mount.entry", entry);
423   }
424
425   // Additional devices we need in our container
426   devices.insert({"/dev/console", {0600}});
427   devices.insert({"/dev/full", {0666}});
428   devices.insert({"/dev/null", {0666}});
429   devices.insert({"/dev/random", {0666}});
430   devices.insert({"/dev/tty", {0666}});
431   devices.insert({"/dev/urandom", {0666}});
432   devices.insert({"/dev/zero", {0666}});
433   devices.insert({"/dev/tun", {0660, "/dev/net/tun"}});
434   devices.insert({"/dev/ashmem", {0666}});
435
436   // Remove all left over devices from last time first before
437   // creating any new ones
438   const auto devices_dir = SystemConfiguration::instance().container_devices_dir();
439   fs::remove_all(devices_dir);
440   fs::create_directories(devices_dir);
441
442   for (const auto& device : devices)
443     add_device(device.first, device.second);
444
445   // If we have any additional properties we add them at the top of default.prop
446   // within the Android rootfs which we overlay with a bind mount.
447   if (configuration.extra_properties.size() > 0) {
448     const auto container_state_dir = SystemConfiguration::instance().container_state_dir();
449     auto old_default_prop_path = fs::path(rootfs_path) / "default.prop";
450     auto new_default_prop_path = fs::path(container_state_dir) / "default.prop";
451     auto default_prop_content = utils::read_file_if_exists_or_throw(old_default_prop_path.string());
452
453     std::ofstream default_props;
454     default_props.open(new_default_prop_path.string(), std::ios_base::out);
455     if (!default_props.is_open())
456       throw std::runtime_error("Failed to open new default properties file");
457
458     default_props << "# Properties added by Anbox" << std::endl;
459     for (const auto& prop : configuration.extra_properties)
460       default_props << prop << std::endl;
461
462     default_props << std::endl
463                   << default_prop_content << std::endl;
464
465     default_props.close();
466
467     set_config_item("lxc.mount.entry",
468                     utils::string_format("%s %s/default.prop none bind,optional,ro 0 0",
469                                          new_default_prop_path.string(), rootfs_path));
470   }
471
472   if (!container_->save_config(container_, nullptr))
473     throw std::runtime_error("Failed to save container configuration");
474
475   if (!container_->start(container_, 0, nullptr))
476     throw std::runtime_error("Failed to start container");
477
478   state_ = Container::State::running;
479
480   DEBUG("Container successfully started");
481 }
482
483 void LxcContainer::stop() {
484   if (!container_ || !container_->is_running(container_))
485     return;
486
487   if (!container_->stop(container_))
488     throw std::runtime_error("Failed to stop container");
489
490   state_ = Container::State::inactive;
491   binder_devices_.clear();
492
493   DEBUG("Container successfully stopped");
494 }
495
496 void LxcContainer::set_config_item(const std::string &key,
497                                    const std::string &value) {
498   if (!container_->set_config_item(container_, key.c_str(), value.c_str())) {
499     const auto msg = utils::string_format("Failed to set config item %s", key);
500     throw std::runtime_error(msg);
501   }
502 }
503
504 Container::State LxcContainer::state() { return state_; }
505 }  // namespace container
506 }  // namespace anbox