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"