github.com/Equinix-Metal/virtlet@v1.5.2-0.20210807010419-342346535dc5/pkg/nsfix/nsfix_linux.go (about) 1 // +build linux 2 3 /* 4 Copyright 2018 Mirantis 5 6 Licensed under the Apache License, Version 2.0 (the "License"); 7 you may not use this file except in compliance with the License. 8 You may obtain a copy of the License at 9 10 http://www.apache.org/licenses/LICENSE-2.0 11 12 Unless required by applicable law or agreed to in writing, software 13 distributed under the License is distributed on an "AS IS" BASIS, 14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 See the License for the specific language governing permissions and 16 limitations under the License. 17 */ 18 19 package nsfix 20 21 // Here we use cgo constructor trick to avoid threading-related problems 22 // (not being able to enter the mount namespace) 23 // when working with process uids/gids and namespaces 24 // https://github.com/golang/go/issues/8676#issuecomment-66098496 25 26 /* 27 #define _GNU_SOURCE 28 29 #include <stdlib.h> 30 #include <stdio.h> 31 #include <fcntl.h> 32 #include <sched.h> 33 #include <unistd.h> 34 #include <sys/mount.h> 35 #include <sys/types.h> 36 #include <sys/stat.h> 37 #include <linux/limits.h> 38 39 static void nsfix_perr(const char* msg) { 40 perror(msg); 41 exit(1); 42 } 43 44 static void nsfix_setns(int my_pid, int target_pid, int nstype, const char* nsname) { 45 int my_ns_inode, fd; 46 struct stat st; 47 char my_ns_path[PATH_MAX], target_ns_path[PATH_MAX]; 48 snprintf(my_ns_path, sizeof(my_ns_path), "/proc/%u/ns/%s", my_pid, nsname); 49 snprintf(target_ns_path, sizeof(target_ns_path), "/proc/%u/ns/%s", target_pid, nsname); 50 if (stat(my_ns_path, &st) < 0) { 51 nsfix_perr("stat() my ns"); 52 } 53 my_ns_inode = st.st_ino; 54 if (stat(target_ns_path, &st) < 0) { 55 nsfix_perr("stat() target ns"); 56 } 57 58 // Check if that's the same namespace 59 // (actually only critical for CLONE_NEWUSER) 60 if (my_ns_inode == st.st_ino) 61 return; 62 63 if ((fd = open(target_ns_path, O_RDONLY)) < 0) { 64 nsfix_perr("open() target ns"); 65 } 66 67 if (setns(fd, nstype) < 0) { 68 nsfix_perr("setns()"); 69 } 70 } 71 72 // This function is a high-priority constructor that will be invoked 73 // before any Go code starts. 74 __attribute__((constructor (200))) void nsfix_handle_reexec(void) { 75 int my_pid, target_pid, target_uid, target_gid; 76 char* pid_str; 77 if ((pid_str = getenv("NSFIX_NS_PID")) == NULL) 78 return; 79 80 my_pid = getpid(); 81 target_pid = atoi(pid_str); 82 83 // Other namespaces: 84 // cgroup, user - not touching 85 // pid - host pid namespace is used by virtlet 86 // net - host network is used by virtlet 87 fprintf(stderr, "nsfix reexec: pid %d: entering the namespaces of target pid %d\n", getpid(), target_pid); 88 nsfix_setns(my_pid, target_pid, CLONE_NEWNS, "mnt"); 89 nsfix_setns(my_pid, target_pid, CLONE_NEWUTS, "uts"); 90 nsfix_setns(my_pid, target_pid, CLONE_NEWIPC, "ipc"); 91 nsfix_setns(my_pid, target_pid, CLONE_NEWNET, "net"); 92 93 if (getenv("NSFIX_REMOUNT_SYS") != NULL) { 94 // remount /sys for the new netns 95 if (umount2("/sys", MNT_DETACH) < 0) 96 nsfix_perr("umount2()"); 97 if (mount("none", "/sys", "sysfs", 0, NULL) < 0) 98 nsfix_perr("mount()"); 99 } 100 101 // Permanently drop privs if asked to do so 102 if (getenv("NSFIX_DROP_PRIVS") != NULL) { 103 fprintf(stderr, "nsfix reexec: dropping privs\n"); 104 target_gid = getgid(); 105 if (setgid(target_gid ? target_gid : 65534) < 0) 106 nsfix_perr("setgid()"); 107 target_uid = getuid(); 108 if (setuid(target_uid ? target_uid : 65534) < 0) 109 nsfix_perr("setuid()"); 110 } else { 111 if (setgid(0) < 0) 112 nsfix_perr("setgid()"); 113 if (setuid(0) < 0) 114 nsfix_perr("setuid()"); 115 } 116 } 117 */ 118 import "C"