Không gian tên Linux

theanh

Administrator
Nhân viên

Bối cảnh​

Bắt đầu từ kernel 2.6.24, Linux hỗ trợ 6 loại không gian tên khác nhau. Không gian tên hữu ích trong việc tạo các quy trình được cô lập hơn so với phần còn lại của hệ thống, mà không cần sử dụng công nghệ ảo hóa cấp thấp đầy đủ.
  • CLONE_NEWIPC: Không gian tên IPC: Hàng đợi tin nhắn IPC và POSIX của SystemV có thể được cô lập.
  • CLONE_NEWPID: Không gian tên PID: PID được cô lập, nghĩa là PID ảo bên trong không gian tên có thể xung đột với PID bên ngoài không gian tên. PID bên trong không gian tên sẽ được ánh xạ tới các PID khác bên ngoài không gian tên. PID đầu tiên bên trong không gian tên sẽ là '1', bên ngoài không gian tên được gán cho init
  • CLONE_NEWNET: Không gian tên mạng: Mạng (/proc/net, IP, giao diện và tuyến đường) được cô lập. Các dịch vụ có thể chạy trên cùng một cổng trong không gian tên và có thể tạo giao diện ảo "trùng lặp".
  • CLONE_NEWNS: Không gian tên gắn kết. Chúng ta có khả năng cô lập các điểm gắn kết khi chúng xuất hiện với các quy trình. Sử dụng không gian tên gắn kết, chúng ta có thể đạt được chức năng tương tự như chroot() nhưng với tính bảo mật được cải thiện.
  • CLONE_NEWUTS: Không gian tên UTS. Mục đích chính của không gian tên này là cô lập tên máy chủ và tên NIS.
  • CLONE_NEWUSER: Không gian tên người dùng. Ở đây, ID người dùng và nhóm khác nhau bên trong và bên ngoài không gian tên và có thể được sao chép.


Trước tiên, chúng ta hãy xem xét cấu trúc của một chương trình C, cần thiết để chứng minh không gian tên quy trình. Nội dung sau đã được thử nghiệm trên Debian 6 và 7. Đầu tiên, chúng ta cần phân bổ một trang bộ nhớ trên ngăn xếp và đặt một con trỏ đến cuối trang bộ nhớ đó. Chúng tôi sử dụng alloca để phân bổ bộ nhớ ngăn xếp thay vì malloc sẽ phân bổ bộ nhớ trên heap.
Mã:
void *mem = alloca(sysconf(_SC_PAGESIZE)) + sysconf(_SC_PAGESIZE);
Tiếp theo, chúng tôi sử dụng clone để tạo một tiến trình con, truyền vị trí của ngăn xếp con 'mem', cũng như các cờ bắt buộc để chỉ định một không gian tên mới. Chúng tôi chỉ định 'callee' là hàm để thực thi trong không gian con:
Mã:
mypid = clone(callee, mem, SIGCHLD | CLONE_NEWIPC | CLONE_NEWPID | CLONE_NEWNS | CLONE_FILES, NULL);
Sau khi gọi clone, chúng tôi đợi tiến trình con hoàn tất trước khi chấm dứt tiến trình cha. Nếu không, luồng thực thi của cha sẽ tiếp tục và kết thúc ngay sau đó, xóa luồng con cùng với nó:
Mã:
while (waitpid(mypid, &r, 0) < 0 && errno == EINTR){ continue;}
Cuối cùng, chúng ta sẽ quay lại shell với mã thoát của con:
Mã:
if (WIFEXITED(r)){ return WEXITSTATUS(r);}return EXIT_FAILURE;
Bây giờ, hãy xem hàm callee:
Mã:
static int callee(){ int ret; mount("proc", "/proc", "proc", 0, ""); setgid(u); setgroups(0, NULL); setuid(u); ret = execl("/bin/bash", "/bin/bash", NULL); return ret;}
Tại đây, chúng ta gắn hệ thống tệp /proc, sau đó đặt uid (ID người dùng) và gid (ID nhóm) thành giá trị 'u' trước khi tạo shell /bin/bash. LXC là công cụ ảo hóa cấp hệ điều hành sử dụng cgroups và không gian tên để cô lập tài nguyên. Chúng ta hãy cùng nhau thực hiện, đặt 'u' thành 65534 là người dùng "nobody" và nhóm "nogroup" trên Debian:
Mã:
#define _GNU_SOURCE#include #include #include #include #include #include #include #include #include #include static int callee();const int u = 65534;int main(int argc, char *argv[]){ int r; pid_t mypid; void *mem = alloca(sysconf(_SC_PAGESIZE)) + sysconf(_SC_PAGESIZE); mypid = clone(callee, mem, SIGCHLD | CLONE_NEWIPC | CLONE_NEWPID | CLONE_NEWNS | CLONE_FILES, NULL); while (waitpid(mypid, &r, 0) < 0 && errno == EINTR) { continue; } if (WIFEXITED(r)) { return WEXITSTATUS(r); } return EXIT_FAILURE;}static int callee(){ int ret; mount("proc", "/proc", "proc", 0, ""); setgid(u); setgroups(0, NULL); setuid(u); ret = execl("/bin/bash", "/bin/bash", NULL); return ret;}
Để thực thi mã sẽ tạo ra lệnh sau:
Mã:
root@w:~/pen/tmp# gcc -O -o ns.c -Wall -Werror -ansi -c89 ns.croot@w:~/pen/tmp# ./nsnobody@w:~/pen/tmp$ iduid=65534(nobody) gid=65534(nogroup)nobody@w:~/pen/tmp$ ps auxwUSER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMANDnobody 1 0.0 0.0 4620 1816 điểm/1 S 21:21 0:00 /bin/bashnobody 5 0.0 0.0 2784 1064 điểm/1 R+ 21:21 0:00 ps auxwnobody@w:~/pen/tmp$
Lưu ý rằng UID và GID được đặt thành nobody và nogroup. Cụ thể, lưu ý rằng đầu ra ps đầy đủ chỉ hiển thị hai quy trình đang chạy và PID của chúng lần lượt là 1 và 5. Bây giờ, hãy chuyển sang sử dụng ip netns để làm việc với không gian tên mạng. Trước tiên, hãy xác nhận rằng không có không gian tên nào tồn tại hiện tại:
Mã:
root@w:~# ip netns listĐối tượng "netns" không xác định, hãy thử "ip help".
Trong trường hợp này, ip cần nâng cấp hoặc kernel cần nâng cấp. Giả sử bạn có kernel mới hơn 2.6.24, thì rất có thể là ip. Sau khi nâng cấp, ip netns list theo mặc định sẽ không trả về kết quả nào. Hãy thêm một không gian tên mới có tên là 'ns1':
Mã:
root@w:~# ip netns add ns1root@w:~# ip netns listns1
Đầu tiên, hãy liệt kê các giao diện hiện tại:
Mã:
root@w:~# ip link list1: lo:  mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:002: eth0:  mtu 1500 qdisc pfifo_fast state UNKNOWN mode DEFAULT qlen 1000 link/ether 00:0c:29:65:25:9e brd ff:ff:ff:ff:ff:ff
Bây giờ hãy tạo một giao diện ảo mới và thêm nó vào không gian tên mới của chúng ta. Các giao diện ảo được tạo theo cặp và được liên kết với nhau - hãy tưởng tượng một cáp chéo ảo:
Mã:
root@w:~# ip link add veth0 type veth peer name veth1root@w:~# ip link list1: lo:  mtu 65536 qdisc noqueue trạng thái KHÔNG XÁC ĐỊNH chế độ MẶC ĐỊNH liên kết/vòng lặp 00:00:00:00:00:00 brd 00:00:00:00:00:002: eth0: 
 mtu 1500 qdisc pfifo_fast trạng thái KHÔNG XÁC ĐỊNH chế độ MẶC ĐỊNH qlen 1000 liên kết/ether 00:0c:29:65:25:9e brd ff:ff:ff:ff:ff:ff3: veth1:  mtu 1500 qdisc noop trạng thái chế độ XUỐNG MẶC ĐỊNH qlen 1000 liên kết/ether d2:e9:52:18:19:ab brd ff:ff:ff:ff:ff:ff4: veth0:  mtu 1500 qdisc noop state DOWN mode DEFAULT qlen 1000 link/ether f2:f7:5e:e2:22:ac brd ff:ff:ff:ff:ff:ff
ifconfig -a giờ cũng sẽ hiển thị thêm cả veth0 và veth1.

Tuyệt, giờ thì gán giao diện mới của chúng ta vào không gian tên. Lưu ý rằng ip netns exec được sử dụng để thực thi các lệnh trong không gian tên:
Mã:
root@w:~# ip link set veth1 netns ns1root@w:~# ip netns exec ns1 ip link list1: lo:  mtu 65536 qdisc noop state DOWN mode DEFAULT link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:003: veth1: 
 mtu 1500 qdisc noop state DOWN mode DEFAULT qlen 1000 link/ether d2:e9:52:18:19:ab brd ff:ff:ff:ff:ff:ff
ifconfig -a giờ đây sẽ chỉ hiển thị veth0, vì veth1 nằm trong không gian tên ns1.

Chúng ta có muốn xóa veth0/veth1 không:
Mã:
ip netns exec ns1 ip link del veth1
Bây giờ chúng ta có thể gán địa chỉ IP 192.168.5.5/24 cho veth0 trên máy chủ của mình:
Mã:
ifconfig veth0 192.168.5.5/24
Và gán veth1 192.168.5.10/24 trong ns1:
Mã:
ip netns exec ns1 ifconfig veth1 192.168.5.10/24 up
Để thực thi ip addr list trên cả máy chủ và trong không gian tên của chúng tôi:
Mã:
root@w:~# ip addr list1: lo:  mtu 65536 qdisc noqueue state UNKNOWN link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 phạm vi máy chủ lo inet6 ::1/128 phạm vi máy chủ valid_lft mãi mãi preferred_lft mãi mãi2: eth0: 
 mtu 1500 qdisc pfifo_fast state UNKNOWN qlen 1000 liên kết/ether 00:0c:29:65:25:9e brd ff:ff:ff:ff:ff:ff inet 192.168.3.122/24 brd 192.168.3.255 phạm vi toàn cục eth0 inet6 fe80::20c:29ff:fe65:259e/64 phạm vi liên kết valid_lft mãi mãi preferred_lft mãi mãi6: veth0:  mtu 1500 qdisc pfifo_fast state UP qlen 1000 link/ether 86:b2:c7:bd:c9:11 brd ff:ff:ff:ff:ff:ff inet 192.168.5.5/24 brd 192.168.5.255 phạm vi toàn cục veth0 inet6 fe80::84b2:c7ff:febd:c911/64 phạm vi liên kết valid_lft mãi mãi preferred_lft mãi mãiroot@w:~# ip netns exec ns1 ip addr list1: lo:  mtu 65536 qdisc noop state DOWN link/loopback 00:00:00:00:00:00 brd 00:00:00:00:005: veth1:  mtu 1500 qdisc pfifo_fast state UP qlen 1000 link/ether 12:bd:b6:76:a6:eb brd ff:ff:ff:ff:ff:ff inet 192.168.5.10/24 brd 192.168.5.255 scope global veth1 inet6 fe80::10bd:b6ff:fe76:a6eb/64 scope link valid_lft forever preferred_lft forever
Để xem các bảng định tuyến bên trong và bên ngoài không gian tên:
Mã:
root@w:~# ip route listdefault via 192.168.3.1 dev eth0 proto static192.168.3.0/24 dev eth0 proto kernel scope link src 192.168.3.122192.168.5.0/24 dev veth0 proto kernel scope link src 192.168.5.5root@w:~# ip netns exec ns1 ip route list192.168.5.0/24 dev veth1 proto kernel scope link src 192.168.5.10
Cuối cùng, để kết nối giao diện vật lý và giao diện ảo, chúng ta sẽ cần một cầu nối. Hãy bắc cầu eth0 và veth0 trên máy chủ, sau đó sử dụng DHCP để lấy IP trong không gian tên ns1:
Mã:
root@w:~# brctl addbr br0root@w:~# brctl addif br0 eth0root@w:~# brctl addif br0 veth0root@w:~# ifconfig eth0 0.0.0.0root@w:~# ifconfig veth0 0.0.0.0root@w:~# dhclient br0root@w:~# ip addr list br07: br0: 
 mtu 1500 qdisc noqueue state UP link/ether 00:0c:29:65:25:9e brd ff:ff:ff:ff:ff:ff inet 192.168.3.122/24 brd 192.168.3.255 scope global br0 inet6 fe80::20c:29ff:fe65:259e/64 scope link valid_lft forever preferred_lft forever
br0 đã được gán IP là 192.168.3.122/24. Bây giờ đến không gian tên:
Mã:
root@w:~# ip netns exec ns1 dhclient veth1root@w:~# ip netns exec ns1 ip addr list1: lo:  mtu 65536 qdisc noop state DOWN link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:005: veth1: 
 mtu 1500 qdisc pfifo_fast state UP qlen 1000 link/ether 12:bd:b6:76:a6:eb brd ff:ff:ff:ff:ff:ff inet 192.168.3.248/24 brd 192.168.3.255 scope global veth1 inet6 fe80::10bd:b6ff:fe76:a6eb/64 scope link valid_lft forever preferred_lft forever
Tuyệt vời! veth1 đã được chỉ định 192.168.3.248/24

Liên kết​

IO Digital Sec
Chuyên gia tư vấn Linux
 
Back
Bên trên