Có cách nào để các tiến trình không root liên kết với các cổng đặc quyền của Wikipedia trên Linux không?


389

Thật khó chịu khi có giới hạn này trong hộp phát triển của tôi, khi không có bất kỳ người dùng nào khác ngoài tôi.

Tôi biết các cách giải quyết tiêu chuẩn , nhưng không ai trong số họ làm chính xác những gì tôi muốn:

  1. authbind (Phiên bản trong thử nghiệm Debian, 1.0, chỉ hỗ trợ IPv4)
  2. Sử dụng mục tiêu GIẢM GIÁ iptables để chuyển hướng một cổng thấp sang cổng cao (bảng "nat" chưa được triển khai cho ip6tables, phiên bản IPv6 của iptables)
  3. sudo (Chạy bằng root là điều tôi đang cố tránh)
  4. SELinux (hoặc tương tự). (Đây chỉ là hộp dev của tôi, tôi không muốn giới thiệu nhiều sự phức tạp thêm.)

Có một số sysctlbiến đơn giản để cho phép các quy trình không root liên kết với các cổng "đặc quyền" (các cổng nhỏ hơn 1024) trên Linux, hay tôi không gặp may?

EDIT: Trong một số trường hợp, bạn có thể sử dụng các khả năng để làm điều này.


5
Theo kinh nghiệm của tôi, một lý do để thử điều này là để viết một máy chủ web thay vì sử dụng Apache (hoặc lighttpd).
S.Lott

Tôi đã thêm công cụ setcap vào câu trả lời của mình cho stackoverflow
Paul Tomblin

15
Bởi vì trong trường hợp này, tôi đã sử dụng IPv6, đó là lý do tại sao một số cách giải quyết "thông thường" (authbind và iptables REDIRECT) không hoạt động với tôi.
Jason Creighton


1
Có một vài cách để làm điều đó. Xem Cho phép quá trình không root để liên kết với cổng 80 và 443? trên Siêu người dùng.
jww

Câu trả lời:


392

Được rồi, cảm ơn những người đã chỉ ra hệ thống CAP_NET_BIND_SERVICEnăng lực và khả năng. Nếu bạn có một kernel gần đây, thực sự có thể sử dụng điều này để bắt đầu một dịch vụ là không root nhưng liên kết các cổng thấp. Câu trả lời ngắn gọn là bạn làm:

setcap 'cap_net_bind_service=+ep' /path/to/program

Và sau đó bất cứ lúc nào programđược thực hiện sau đó nó sẽ có CAP_NET_BIND_SERVICEkhả năng. setcaplà trong gói debian libcap2-bin.

Bây giờ hãy cẩn thận:

  1. Bạn sẽ cần ít nhất một kernel 2.6.24
  2. Điều này sẽ không hoạt động nếu tập tin của bạn là một tập lệnh. (nghĩa là sử dụng dòng #! để khởi chạy trình thông dịch). Trong trường hợp này, theo như tôi hiểu, bạn sẽ phải áp dụng khả năng cho chính trình thông dịch thực thi, tất nhiên đó là một cơn ác mộng về bảo mật, vì bất kỳ chương trình nào sử dụng trình thông dịch đó đều có khả năng. Tôi không thể tìm thấy bất kỳ cách dễ dàng, sạch sẽ nào để khắc phục vấn đề này.
  3. Linux sẽ vô hiệu hóa LD_LIBRARY_PATH trên bất kỳ programđặc quyền nào có đặc quyền nâng cao như setcaphoặc suid. Vì vậy, nếu bạn programsử dụng riêng của mình .../lib/, bạn có thể phải xem xét một tùy chọn khác như chuyển tiếp cổng.

Tài nguyên:

Lưu ý: Lần đầu tiên RHEL thêm điều này vào v6 .


3
Giải pháp một phần cho các tập lệnh: tạo một bản sao của trình thông dịch (ví dụ bash), cung cấp cho nó khả năng nhưng hạn chế quyền truy cập vào bản sao cho những người dùng cần nó. Tất nhiên, những người dùng đó phải được tin cậy, nhưng dù sao họ cũng có thể thay đổi tập lệnh.
Erich Kitzmueller

3
Ngoài gói debian (nhị phân) đã nói ở trên, trang web của nhà phát triển là Friedhoff.org/poseixfilecaps.html các bài báo / bài thuyết trình liên quan / v.v ...
RandomNickName42

1
trên suse SLES 11.1 Tôi đã phải thêm kernel param file_caps = 1 vào grub menu.lst để nó hoạt động.
Shay

2
Có @ C.Ross vì nó sẽ phải được áp dụng /usr/bin/javavà sau đó sẽ mở khả năng cho bất kỳ ứng dụng java nào đang chạy trên hệ thống. Khả năng quá xấu cũng không thể được đặt cho mỗi người dùng.
joeytwiddle

5
Cài đặt setcap có tồn tại trên các lần khởi động lại không; Nếu không có một nơi tiêu chuẩn để đặt quy tắc này để nó chạy trong khi khởi động hệ thống? Là /etc/security/capability.conftrên Debian / Ubuntu bất kỳ sự giúp đỡ?
joeytwiddle

34

Bạn có thể làm một chuyển hướng cổng. Đây là những gì tôi làm cho máy chủ chính sách Silverlight chạy trên hộp Linux

iptables -A PREROUTING -t nat -i eth0 -p tcp --dport 943 -j REDIRECT --to-port 1300

5
Thật không may, điều này sẽ chỉ hoạt động cho các kết nối được định tuyến, tức là không phải từ máy cục bộ.
zbyszek

@joeytwiddle Tôi nghĩ rằng trong kịch bản chạy (bắt đầu) máy chủ của bạn, nhưng tôi có thể sai. Tôi cũng muốn biết: P
Camilo Martin

@CamiloMartin Nhìn lại, tài liệu Debian được liên kết từ câu hỏi khuyên bạn nên đặt nó trong tập lệnh tường lửa của bạn hoặc tạo một tài liệu tại /etc/network/if-up.d/firewall.
joeytwiddle

8
@zbyszek Điều này có thể hoạt động cho các kết nối máy cục bộ, với quy tắc iptables bổ sung: stackoverflow.com/a/31795603/1356953
00500005

Trong các nhân cũ hơn, có vẻ như điều này không được hỗ trợ cho IPv6 . Nhưng rõ ràng nó được hỗ trợ trong ip6tables v1.4.18 và Linux kernel v3.8.
Craig McQueen

31

Cách tiêu chuẩn là làm cho chúng "setuid" để chúng khởi động như root, và sau đó chúng vứt bỏ đặc quyền gốc đó ngay khi chúng bị ràng buộc với cổng nhưng trước khi chúng bắt đầu chấp nhận kết nối với nó. Bạn có thể thấy các ví dụ hay về điều đó trong mã nguồn cho Apache và INN. Tôi đã nói rằng Lighttpd là một ví dụ điển hình khác.

Một ví dụ khác là Postfix, sử dụng nhiều trình tiện ích giao tiếp qua các đường ống và chỉ một hoặc hai trong số chúng (rất ít ngoại trừ chấp nhận hoặc phát ra byte) chạy dưới quyền root và phần còn lại chạy ở đặc quyền thấp hơn.


1
Thật thú vị, điều này không hoạt động trong các phiên bản Linux gần đây (có thể chỉ là Ubuntu) mà không có cài đặt CAP_SETUID. Vì vậy, nếu bạn cần setuid, bạn sẽ phải thiết lập khả năng này.
Matt

Bỏ quyền riêng tư là cách đúng đắn để làm điều này. Mặc dù không cần thiết phải thiết lập setuid nếu bạn có init / upstart / systemd bắt đầu dịch vụ.
Michael Hampton

2
Điều này là khó khăn nếu chương trình được viết bằng ngôn ngữ phiên dịch hoặc trình thông dịch mã byte như C # (Mono), Java, Python. (Rõ ràng Perl đã thực hiện nó thông qua binfmt_miscvà cờ 'C' của nó; tôi không chắc chắn về người khác.)
Craig McQueen

rất ít ngoại trừ phát ra byte, điều đó không làm được rất ít.
Ryan

Nếu bạn đặt một setuid nhị phân, nó sẽ chạy như một tiến trình gốc. Câu hỏi, tuy nhiên, hỏi làm thế nào để thực hiện nó mà không chạy nhị phân của bạn như là quá trình root. Giải pháp này là câu trả lời cho một câu hỏi khác, cụ thể là: "Làm thế nào một người dùng không có quyền riêng tư có thể có một quy trình liên kết với một cổng riêng tư", nhưng đây không phải là câu hỏi được đặt ra, IMHO?
Bia Michael

24

Hoặc vá kernel của bạn và loại bỏ kiểm tra.

(Lựa chọn cuối cùng, không nên dùng).

Trong net/ipv4/af_inet.c, loại bỏ hai dòng đọc

      if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE))
              goto out;

và kernel sẽ không kiểm tra các cổng đặc quyền nữa.


22
Một ý tưởng rất xấu cho nhiều lý do.
Adam Lassek

24
Đây là ý tưởng tồi. Nhưng thực sự sẽ làm việc. Và chắc chắn làm tôi cười.
Oto Brglez

23
Tất nhiên đó là một ý tưởng tồi. Đó là lý do tại sao tôi nói cuối cùng. Điểm quan trọng của nguồn mở là nếu nó không hoạt động theo cách bạn muốn, bạn có thể thay đổi nó.
Joshua

18
Tôi không chắc tại sao bạn lại bị đánh giá thấp. Người viết phần mềm độc hại không quan tâm đến việc họ nghe cổng nào và họ rất vui khi mở một cổng lớn hơn 1024. Đối với tôi, tất cả các cổng nên yêu cầu đặc quyền hoặc không có cổng nào cần đặc quyền. Và yêu cầu root để mở một cổng dưới 1024 chỉ có nghĩa là bạn có một ứng dụng rủi ro cao đang chạy như root. Đó dường như là một ý tưởng thực sự ngu ngốc đối với tôi. Có lẽ tôi đang thiếu một cái gì đó ở đây ...
jww 2/214

9
Trước đây, cổng <1024 đã được sử dụng trong các giao thức UNIX sang UNIX để chứng minh mã chạy ở đầu bên kia đang chạy dưới quyền root. Điều này hoạt động hợp lý tốt cho một tập hợp các máy chủ UNIX có bảo mật chung.
Joshua

22

Bạn có thể thiết lập đường hầm SSH cục bộ, ví dụ: nếu bạn muốn cổng 80 đánh vào ứng dụng của bạn bị ràng buộc với 3000:

sudo ssh $USERNAME@localhost -L 80:localhost:3000 -N

Điều này có lợi thế là làm việc với các máy chủ script và rất đơn giản.


1
Yêu thích cá nhân của tôi. Rất dễ dàng để bật và tắt và không yêu cầu thay đổi toàn hệ thống. Ý tưởng tuyệt vời!
Dan Passaro

Thật đáng kinh ngạc. Cho đến nay câu trả lời đơn giản nhất.
michaelsnowden

1
Tôi hy vọng nó sẽ có giá vừa phải và không phải là ý tưởng hay trong bất kỳ tình huống ràng buộc nào về hiệu năng, nhưng tôi không thể chắc chắn nếu không thử nghiệm nó. Mã hóa SSH có chi phí khác nhau.
lahwran

3
Điều này có thể được thực hiện với netcat không có ssh trên đầu:sudo nc -l 80 | nc localhost 3000
Bugster

1
Bạn đang mã hóa + giải mã chỉ để di chuyển dữ liệu sang một cổng khác trên cùng một máy? Ngoài ra, điều này không hoạt động cho lưu lượng UDP.
Daniel F

19

Cập nhật 2017:

Sử dụng xác thực


Tốt hơn nhiều so với CAP_NET_BIND_SERVICE hoặc hạt nhân tùy chỉnh.

  • CAP_NET_BIND_SERVICE cấp ủy thác cho nhị phân nhưng không cung cấp quyền kiểm soát truy cập trên mỗi cổng.
  • Authbind cấp niềm tin cho người dùng / nhóm và cung cấp quyền kiểm soát truy cập trên mỗi cổng và hỗ trợ cả IPv4 và IPv6 (hỗ trợ IPv6 đã được thêm vào cuối).

    1. Tải về: apt-get install authbind

    2. Định cấu hình quyền truy cập vào các cổng có liên quan, ví dụ 80 và 443 cho tất cả người dùng và nhóm:

      sudo touch / etc / authbind / byport / 80
      sudo touch / etc / authbind / byport / 443
      sudo chmod 777 / etc / authbind / byport / 80
      sudo chmod 777 / etc / authbind / byport / 443

    3. Thực hiện lệnh của bạn thông qua authbind
      (tùy chọn chỉ định --deephoặc các đối số khác, xem trang man):

      authbind --deep /path/to/binary command line args
      

      ví dụ

      authbind --deep java -jar SomeServer.jar
      

Theo dõi khuyến nghị tuyệt vời của Joshua (= không được đề xuất trừ khi bạn biết bạn làm gì) để hack kernel:

Lần đầu tiên tôi đăng nó ở đây .

Đơn giản. Với một nhân bình thường hoặc cũ, bạn không.
Như được chỉ ra bởi những người khác, iptables có thể chuyển tiếp một cổng.
Như những người khác đã chỉ ra, CAP_NET_BIND_SERVICE cũng có thể thực hiện công việc.
Tất nhiên, CAP_NET_BIND_SERVICE sẽ thất bại nếu bạn khởi chạy chương trình của mình từ một tập lệnh, trừ khi bạn đặt giới hạn trên trình thông dịch shell, điều này là vô nghĩa, bạn cũng có thể chạy dịch vụ của mình dưới dạng root ...
ví dụ: đối với Java, bạn phải áp dụng nó đến JVM JAVA

sudo /sbin/setcap 'cap_net_bind_service=ep' /usr/lib/jvm/java-8-openjdk/jre/bin/java

Rõ ràng, điều đó có nghĩa là bất kỳ chương trình Java nào cũng có thể liên kết các cổng hệ thống.
Dito cho mono / .NET.

Tôi cũng khá chắc chắn xinetd không phải là ý tưởng hay nhất.
Nhưng vì cả hai phương pháp đều là hack, tại sao không nâng giới hạn bằng cách nâng giới hạn?
Không ai nói bạn phải chạy kernel bình thường, vì vậy bạn chỉ có thể chạy kernel của riêng mình.

Bạn chỉ cần tải xuống nguồn cho kernel mới nhất (hoặc giống như bạn hiện có). Sau đó, bạn đi đến:

/usr/src/linux-<version_number>/include/net/sock.h:

Có bạn tìm dòng này

/* Sockets 0-1023 can't be bound to unless you are superuser */
#define PROT_SOCK       1024

và thay đổi nó thành

#define PROT_SOCK 0

nếu bạn không muốn có một tình huống ssh không an toàn, bạn hãy thay đổi nó thành: #define PROT_SOCK 24

Nói chung, tôi sẽ sử dụng cài đặt thấp nhất mà bạn cần, ví dụ 79 cho http hoặc 24 khi sử dụng SMTP trên cổng 25.

Đó là tất cả.
Biên dịch kernel và cài đặt nó.
Khởi động lại.
Đã kết thúc - giới hạn ngu ngốc đó là ĐÁNH GIÁ, và điều đó cũng hoạt động đối với các tập lệnh.

Đây là cách bạn biên dịch kernel:

https://help.ubfox.com/community/Kernel/Compile

# You can get the kernel-source via package linux-source, no manual download required
apt-get install linux-source fakeroot

mkdir ~/src
cd ~/src
tar xjvf /usr/src/linux-source-<version>.tar.bz2
cd linux-source-<version>

# Apply the changes to PROT_SOCK define in /include/net/sock.h

# Copy the kernel config file you are currently using
cp -vi /boot/config-`uname -r` .config

# Install ncurses libary, if you want to run menuconfig
apt-get install libncurses5 libncurses5-dev

# Run menuconfig (optional)
make menuconfig

# Define the number of threads you wanna use when compiling (should be <number CPU cores> - 1), e.g. for quad-core
export CONCURRENCY_LEVEL=3
# Now compile the custom kernel
fakeroot make-kpkg --initrd --append-to-version=custom kernel-image kernel-headers

# And wait a long long time

cd ..

Tóm lại, hãy sử dụng iptables nếu bạn muốn giữ an toàn, biên dịch kernel nếu bạn muốn chắc chắn rằng hạn chế này không bao giờ làm phiền bạn nữa.


Bất kỳ lý do cho các downvote? Root-ghét, hoặc các hướng dẫn biên dịch kernel không hoạt động?
Stefan Steiger

4
Sửa đổi kernel làm cho các cập nhật trong tương lai đau đớn hơn nhiều. Tôi sẽ không làm điều này, vì tôi biết rằng tôi sẽ quá lười biếng để tiếp tục cập nhật kernel của mình thường xuyên. Về mặt lý thuyết thì thật tuyệt, nhưng đó không phải là một lựa chọn khả thi trong nhiều tình huống.
kritzikratzi 6/07/2015

Có lẽ cách an toàn hơn để thực hiện là thay đổi chmod 777 / etc / authbind / byport / 80 bằng chmod 544 / etc / authbind / byport / 23 so với đăng nhập chown: group / etc / authbind / byport / 23
Jérôme B

18

Khả năng tệp không lý tưởng, vì chúng có thể bị hỏng sau khi cập nhật gói.

Giải pháp lý tưởng, IMHO, nên là một khả năng tạo ra một trình bao với CAP_NET_BIND_SERVICEtập hợp kế thừa .

Đây là một cách hơi phức tạp để làm điều này:

sg $DAEMONUSER "capsh --keep=1 --uid=`id -u $DAEMONUSER` \
     --caps='cap_net_bind_service+pei' -- \
     YOUR_COMMAND_GOES_HERE"

capshtiện ích có thể được tìm thấy trong gói libcap2-bin trong bản phân phối Debian / Ubuntu. Đây là những gì đang diễn ra:

  • sgthay đổi ID nhóm hiệu quả thành ID của người dùng daemon. Điều này là cần thiết bởi vì capshGID không thay đổi và chúng tôi chắc chắn không muốn nó.
  • Đặt bit 'giữ khả năng thay đổi UID'.
  • Thay đổi UID thành $DAEMONUSER
  • Giảm tất cả các mũ (tại thời điểm này, tất cả các mũ vẫn còn hiện diện vì --keep=1), ngoại trừ kế thừacap_net_bind_service
  • Thực thi lệnh của bạn ('-' là dấu phân cách)

Kết quả là một quá trình với người dùng và nhóm được chỉ định và các cap_net_bind_serviceđặc quyền.

Ví dụ, một dòng từ ejabberdtập lệnh khởi động:

sg $EJABBERDUSER "capsh --keep=1 --uid=`id -u $EJABBERDUSER` --caps='cap_net_bind_service+pei' -- $EJABBERD --noshell -detached"

Và nó thực sự KHÔNG hoạt động. Mũ không được bảo quản qua chuyển đổi ID người dùng. Nó sẽ hoạt động nếu bạn muốn chạy bằng root, nhưng với tất cả các khả năng bị loại bỏ.
Cyberax

Nếu tôi thử điều đó, tôi nhận được "không thể thực thi tệp nhị phân". Trên thực tế, tôi không thể capshlàm bất cứ điều gì ngoài "không thể thực thi tệp nhị phân" khi sử dụng --hoặc ==tùy chọn. Tôi tự hỏi những gì tôi đang thiếu.
Craig McQueen

@CraigMcQueen, mọi thứ sau đó --được truyền lại /bin/bash, vì vậy bạn có thể muốn thử -c 'your command'. Than ôi, tôi dường như đang gặp vấn đề tương tự như @Cyberax, vì tôi bị "từ chối cấp phép" khi cố gắng bind.
Amir

Để làm việc này mà không cần thiết lập các khả năng của tệp (hoặc chạy dưới quyền root), bạn sẽ cần sử dụng các khả năng xung quanh như được mô tả trong câu trả lời Unix.SE này . Bạn cũng có thể sử dụng --gidthay vì sg(hoặc --usermà bộ --uid, --gid--groups): sudo capsh --caps='cap_net_bind_service+eip cap_setpcap,cap_setuid,cap_setgid+ep' --keep=1 --user="$service_user" --addamb=cap_net_bind_service -- -c 'exec $service $service_args'
Kevinoid

18

Vì một số lý do, không ai đề cập đến việc hạ thấp sysctl net.ipv4.ip_unpriv đặc biệt_port_start đến giá trị bạn cần. Ví dụ: Chúng tôi cần liên kết ứng dụng của mình với cổng 443.

sysctl net.ipv4.ip_unprivileged_port_start=443

Một số người có thể nói, có một vấn đề bảo mật tiềm ẩn: người dùng không có đặc quyền hiện có thể liên kết với các cổng đặc quyền khác (444-1024). Nhưng bạn có thể giải quyết vấn đề này một cách dễ dàng bằng iptables, bằng cách chặn các cổng khác:

iptables -I INPUT -p tcp --dport 444:1024 -j DROP
iptables -I INPUT -p udp --dport 444:1024 -j DROP

So sánh với các phương pháp khác. Phương pháp này:

  • từ một số điểm là (IMO) thậm chí còn an toàn hơn so với cài đặt CAP_NET_BIND_SERVICE / setuid, vì một ứng dụng hoàn toàn không được thiết lập, thậm chí một phần (khả năng thực sự là). Ví dụ: để bắt được một ứng dụng hỗ trợ khả năng, bạn sẽ cần thay đổi sysctl fs.suid_dumpable (dẫn đến một vấn đề bảo mật tiềm ẩn khác) Ngoài ra, khi CAP / suid được đặt, thư mục / Proc / PID được sở hữu bởi root, vì vậy Người dùng không phải root của bạn sẽ không có đầy đủ thông tin / kiểm soát quá trình đang chạy, ví dụ, người dùng sẽ không thể (trong trường hợp thông thường) để xác định kết nối nào thuộc về ứng dụng thông qua / Proc / PID / fd / (netstat -aptn | grep PID).
  • có nhược điểm bảo mật: trong khi ứng dụng của bạn (hoặc bất kỳ ứng dụng nào sử dụng cổng 443-1024) không hoạt động vì một số lý do, ứng dụng khác có thể mất cổng. Nhưng vấn đề này cũng có thể được áp dụng cho CAP / suid (trong trường hợp bạn đặt nó trên trình thông dịch, ví dụ java / nodejs) và iptables-redirect. Sử dụng phương pháp systemd-socket để loại trừ vấn đề này. Sử dụng phương thức authbind để chỉ cho phép ràng buộc người dùng đặc biệt.
  • không yêu cầu thiết lập CAP / suid mỗi khi bạn triển khai phiên bản ứng dụng mới.
  • không yêu cầu hỗ trợ / sửa đổi ứng dụng, như phương thức systemd-socket.
  • không yêu cầu xây dựng lại kernel (nếu phiên bản đang chạy hỗ trợ cài đặt sysctl này)
  • không làm LD_PRELOAD như phương thức authbind / privatebind, điều này có khả năng ảnh hưởng đến hiệu suất, bảo mật, hành vi (nó chưa được thử nghiệm). Trong phần còn lại authbind là phương pháp thực sự linh hoạt và an toàn.
  • thực hiện quá mức phương pháp iptables REDIRECT / DNAT, vì nó không yêu cầu dịch địa chỉ, theo dõi trạng thái kết nối, v.v ... Điều này chỉ đáng chú ý trên các hệ thống tải cao.

Tùy thuộc vào tình huống, tôi sẽ chọn giữa sysctl, CAP, authbind và iptables-redirect. Và điều này thật tuyệt khi chúng ta có rất nhiều lựa chọn.


2
Cảm ơn câu trả lời tuyệt vời! Có vẻ như chức năng này xuất hiện lần đầu tiên trong Linux 4.11 vào tháng 4 năm 2017 , vì vậy nó không xuất hiện vào năm 2009 khi tôi hỏi câu hỏi này lần đầu tiên. Tôi cũng đã thực hiện một thử nghiệm nhanh và dường như nó cũng hoạt động với IPV6, mặc dù "ipv4" có trong tên sysctl.
Jason Creighton

15

Hai khả năng đơn giản khác:

Có một giải pháp cũ (không hợp thời) cho "một daemon liên kết trên một cổng thấp và điều khiển tay với daemon của bạn". Nó được gọi là inetd (hoặc xinetd). Nhược điểm là:

  • daemon của bạn cần nói chuyện trên stdin / stdout (nếu bạn không điều khiển daemon - nếu bạn không có nguồn - thì đây có lẽ là một showstopper, mặc dù một số dịch vụ có thể có cờ tương thích inetd)
  • một tiến trình daemon mới được chia rẽ cho mọi kết nối
  • đó là một liên kết bổ sung trong chuỗi

Ưu điểm:

  • có sẵn trên mọi UNIX cũ
  • một khi sysadmin của bạn đã thiết lập cấu hình, bạn sẽ tiếp tục phát triển (khi bạn xây dựng lại trình nền của mình, bạn có thể mất khả năng setcap không? Và sau đó bạn sẽ phải quay lại quản trị viên của mình ", thưa ngài .. . ")
  • daemon không phải lo lắng về công cụ kết nối mạng đó, chỉ cần nói về stdin / stdout
  • có thể cấu hình để thực thi trình nền của bạn với tư cách là người dùng không root, theo yêu cầu

Một cách khác: proxy bị hack (netcat hoặc thậm chí là thứ gì đó mạnh mẽ hơn ) từ cổng đặc quyền đến một số cổng được đánh số cao tùy ý nơi bạn có thể chạy trình nền đích của mình. (Netcat rõ ràng không phải là một giải pháp sản xuất, nhưng "chỉ là hộp dev của tôi", phải không?). Bằng cách này, bạn có thể tiếp tục sử dụng phiên bản máy chủ có khả năng kết nối mạng, chỉ cần root / sudo để khởi động proxy (khi khởi động), sẽ không phụ thuộc vào các khả năng phức tạp / có khả năng dễ hỏng.


1
Gợi ý tốt. Vì một số lý do, tôi thậm chí không nghĩ đến việc sử dụng inetd. Ngoại trừ dịch vụ được đề cập là dựa trên UDP, do đó, nó hơi phức tạp hơn các dịch vụ TCP. Tôi có một giải pháp hoạt động ngay bây giờ với setcap, nhưng tôi sẽ phải suy nghĩ điều này.
Jason Creighton

Đối với tùy chọn thứ hai, bạn thậm chí có thể bắt đầu proxy bằng inetd ... (Nhưng IPTables có khả năng thấp hơn ...)
Gert van den Berg

14

"Cách giải quyết chuẩn" của tôi sử dụng socat làm công cụ chuyển hướng không gian người dùng:

socat tcp6-listen:80,fork tcp6:8080

Coi chừng việc này không có quy mô, rèn là tốn kém nhưng đó là cách hoạt động của socat.


13

Linux hỗ trợ các khả năng để hỗ trợ các quyền chi tiết hơn là "ứng dụng này được chạy dưới quyền root". Một trong những khả năng CAP_NET_BIND_SERVICEđó là liên kết với một cổng đặc quyền (<1024).

Thật không may, tôi không biết cách khai thác ứng dụng đó để chạy ứng dụng không phải là root trong khi vẫn cung cấp cho nó CAP_NET_BIND_SERVICE(có thể sử dụng setcap, nhưng chắc chắn sẽ là một giải pháp hiện có cho việc này).


Không hoạt động cho các tập lệnh hoặc các chương trình JVM / mono riêng lẻ.
Stefan Steiger

13

Tôi biết đây là một câu hỏi cũ, nhưng bây giờ với các hạt nhân gần đây (> = 4.3) cuối cùng cũng có một câu trả lời tốt cho điều này - khả năng xung quanh.

Câu trả lời nhanh là lấy một bản sao của phiên bản libcap mới nhất (chưa được phát hành) từ git và biên dịch nó. Sao chép progs/capshnhị phân kết quả ở đâu đó ( /usr/local/binlà một lựa chọn tốt). Sau đó, với quyền root, hãy bắt đầu chương trình của bạn với

/usr/local/bin/capsh --keep=1 --user='your-service-user-name' \
    --inh='cap_net_bind_service' --addamb='cap_net_bind_service' \ 
    -- -c 'your-program'

Theo thứ tự, chúng tôi

  • Tuyên bố rằng khi chúng tôi chuyển đổi người dùng, chúng tôi muốn giữ các bộ khả năng hiện tại của mình
  • Chuyển người dùng & nhóm thành 'tên dịch vụ của bạn'
  • Thêm cap_net_bind_servicekhả năng cho các bộ kế thừa và môi trường xung quanh
  • Ngã ba bash -c 'your-command'(vì capshtự động bắt đầu bash với các đối số sau --)

Có rất nhiều thứ đang diễn ra dưới mui xe ở đây.

Đầu tiên, chúng tôi đang chạy với quyền root, vì vậy theo mặc định, chúng tôi có được một bộ đầy đủ các khả năng. Bao gồm trong điều này là khả năng chuyển đổi uid & gid với setuidvà các tòa nhà cao tầng setgid. Tuy nhiên, thông thường khi một chương trình thực hiện điều này, nó sẽ mất tập hợp các khả năng của nó - điều này là do cách bỏ root cũ mà setuidvẫn hoạt động. Các --keep=1lá cờ nói capshđến hành prctl(PR_SET_KEEPCAPS)syscall, giúp chặn đứng sự giảm khả năng khi thay đổi người dùng. Sự thay đổi thực tế của người dùng bằng cách capshxảy ra với --usercờ, chạy setuidsetgid.

Vấn đề tiếp theo chúng ta cần giải quyết là làm thế nào để thiết lập các khả năng theo cách tiếp tục theo đuổi execcon cái chúng ta. Hệ thống các khả năng luôn có một bộ khả năng 'được kế thừa', đó là "một bộ các khả năng được bảo tồn trong một lần thực thi (2)" [ khả năng (7) ]. Mặc dù điều này nghe có vẻ như giải quyết được vấn đề của chúng tôi (chỉ cần đặt cap_net_bind_servicekhả năng kế thừa, phải không?), Điều này thực sự chỉ áp dụng cho các quy trình đặc quyền - và quy trình của chúng tôi không còn đặc quyền nữa, vì chúng tôi đã thay đổi người dùng (với --usercờ).

Bộ khả năng môi trường mới hoạt động xung quanh vấn đề này - đó là "một bộ các khả năng được bảo toàn trên một chương trình thực thi (2) của một chương trình không có đặc quyền." Bằng cách đặt cap_net_bind_servicetrong môi trường xung quanh, khi capshchương trình máy chủ của chúng tôi thực hiện, chương trình của chúng tôi sẽ kế thừa khả năng này và có thể liên kết người nghe với các cổng thấp.

Nếu bạn muốn tìm hiểu thêm, trang hướng dẫn về khả năng giải thích điều này rất chi tiết. Chạy capshqua stracecũng rất nhiều thông tin!


Các --addamblá cờ đã được giới thiệu với libcap 2,26. Hệ thống của tôi có 2,25 và tôi phải xây dựng từ nguồn.
ngreen

12

TLDR: Đối với "câu trả lời" (như tôi thấy), hãy nhảy xuống phần >> TLDR << trong câu trả lời này.

OK, tôi đã tìm ra nó (lần này là thật), câu trả lời cho câu hỏi này và câu trả lời này của tôi cũng là một cách xin lỗi vì đã quảng bá một câu trả lời khác (cả ở đây và trên twitter) mà tôi nghĩ là "tốt nhất ", Nhưng sau khi thử nó, phát hiện ra rằng tôi đã nhầm về điều đó. Học hỏi từ những đứa trẻ sai lầm của tôi: đừng quảng bá một cái gì đó cho đến khi bạn thực sự thử nó!

Một lần nữa, tôi đã xem lại tất cả các câu trả lời ở đây. Tôi đã thử một vài trong số chúng (và chọn không thử những thứ khác vì đơn giản là tôi không thích các giải pháp). Tôi nghĩ rằng giải pháp là sử dụng systemdvới nó Capabilities=CapabilitiesBindingSet=cài đặt. Sau khi vật lộn với điều này một thời gian, tôi phát hiện ra rằng đây không phải là giải pháp vì:

Khả năng là nhằm hạn chế các quá trình root!

Như OP đã khôn ngoan tuyên bố, tốt nhất là luôn tránh điều đó (đối với tất cả các trình tiện ích của bạn nếu có thể!).

Bạn không thể sử dụng các tùy chọn liên quan đến Khả năng với User=Group=trong systemdcác tệp đơn vị, vì các khả năng LUÔN được đặt lại khi execev(hoặc bất cứ chức năng nào được gọi). Nói cách khác, khi systemdrẽ và giảm uốn, các khả năng được đặt lại. Không có cách nào khác, và tất cả logic ràng buộc trong kernel là cơ bản xung quanh uid = 0, không phải khả năng. Điều này có nghĩa là không chắc rằng Khả năng sẽ là câu trả lời đúng cho câu hỏi này (ít nhất là bất cứ lúc nào sớm). Ngẫu nhiên, setcapnhư những người khác đã đề cập, không phải là một giải pháp. Nó không hoạt động với tôi, nó không hoạt động tốt với các tập lệnh và chúng được thiết lập lại bất cứ khi nào tập tin thay đổi.

Để bảo vệ ít ỏi của mình, tôi đã tuyên bố (trong bình luận tôi đã xóa), rằng đề xuất iptables của James (mà OP cũng đề cập), là "giải pháp tốt nhất thứ 2". :-P

>> TLDR <<

Giải pháp là kết hợp systemdvới các iptableslệnh đang hoạt động, như thế này ( lấy từ DNSChain ):

[Unit]
Description=dnschain
After=network.target
Wants=namecoin.service

[Service]
ExecStart=/usr/local/bin/dnschain
Environment=DNSCHAIN_SYSD_VER=0.0.1
PermissionsStartOnly=true
ExecStartPre=/sbin/sysctl -w net.ipv4.ip_forward=1
ExecStartPre=-/sbin/iptables -D INPUT -p udp --dport 5333 -j ACCEPT
ExecStartPre=-/sbin/iptables -t nat -D PREROUTING -p udp --dport 53 -j REDIRECT --to-ports 5333
ExecStartPre=/sbin/iptables -A INPUT -p udp --dport 5333 -j ACCEPT
ExecStartPre=/sbin/iptables -t nat -A PREROUTING -p udp --dport 53 -j REDIRECT --to-ports 5333
ExecStopPost=/sbin/iptables -D INPUT -p udp --dport 5333 -j ACCEPT
ExecStopPost=/sbin/iptables -t nat -D PREROUTING -p udp --dport 53 -j REDIRECT --to-ports 5333
User=dns
Group=dns
Restart=always
RestartSec=5
WorkingDirectory=/home/dns
PrivateTmp=true
NoNewPrivileges=true
ReadOnlyDirectories=/etc

# Unfortunately, capabilities are basically worthless because they're designed to restrict root daemons. Instead, we use iptables to listen on privileged ports.
# Capabilities=cap_net_bind_service+pei
# SecureBits=keep-caps

[Install]
WantedBy=multi-user.target

Ở đây chúng tôi thực hiện như sau:

  • Trình nền nghe trên 5333, nhưng các kết nối được chấp nhận thành công vào ngày 53 nhờ vào iptables
  • Chúng tôi có thể bao gồm các lệnh trong tệp đơn vị, và do đó chúng tôi tiết kiệm được đau đầu của mọi người. systemdlàm sạch các quy tắc tường lửa cho chúng tôi, đảm bảo xóa chúng khi daemon không chạy.
  • Chúng tôi không bao giờ chạy với quyền root và chúng tôi không thể leo thang đặc quyền (ít nhất là systemdyêu cầu), ngay cả khi daemon bị xâm phạm và thiết lập uid=0.

iptablesThật không may, vẫn là một tiện ích khá xấu xí và khó sử dụng. Nếu daemon đang nghe eth0:0thay vì eth0, ví dụ, các lệnh hơi khác nhau .


2
Không ai nên sử dụng các bí danh kiểu cũ như thế eth0:0nữa trừ khi chúng có bản phân phối Linux thực sự cổ xưa . Họ đã bị từ chối trong nhiều năm và cuối cùng sẽ biến mất.
Michael Hampton

1
Tôi nghĩ bạn có nghĩa là OpenVZ. (SolusVM là bảng điều khiển.) Và vâng, OpenVZ đã làm rất nhiều điều sai, mạng chỉ là một trong số đó.
Michael Hampton

1
Không, ý tôi là SolusVM. Từ / etc / mạng / giao diện:# Generated by SolusVM
Greg Slepak

1
Vẫn không phải OpenVZ ... Ít nhất là tôi không sử dụng nó, VPS của tôi cũng vậy.
Greg Slepak

7
Cảm ơn bạn đã chỉ ra tính năng khả năng của systemd. Tuy nhiên, không cần iptables phức tạp khi systemd khởi động nhị phân trực tiếp (không phải tập lệnh). Thiết lập AmbientCapabilities=CAP_NET_BIND_SERVICElàm việc hoàn toàn tốt cho tôi kết hợp với User=.
Ruud

11

systemd là một thay thế sysvinit có tùy chọn để khởi chạy một daemon với các khả năng cụ thể. Tùy chọn Khả năng =, CapabilityBoundingset = trong mand systemd.exec (5) .


2
Trước đây tôi đã đề xuất câu trả lời này, nhưng sau khi thử nó, bây giờ thì không. Xem câu trả lời của tôi cho một sự thay thế vẫn còn sử dụng systemd.
Greg Slepak

10

Chuyển hướng cổng có ý nghĩa nhất đối với chúng tôi, nhưng chúng tôi gặp phải một vấn đề trong đó ứng dụng của chúng tôi sẽ giải quyết một url cục bộ cũng cần được định tuyến lại; (có nghĩa là bạn shindig ).

Điều này cũng sẽ cho phép bạn được chuyển hướng khi truy cập url trên máy cục bộ.

iptables -A PREROUTING -t nat -p tcp --dport 80 -j REDIRECT --to-port 8080
iptables -A OUTPUT -t nat -p tcp --dport 80 -j REDIRECT --to-port 8080

9

Hỗ trợ Linux hiện đại /sbin/sysctl -w net.ipv4.ip_unprivileged_port_start=0.


8

Với systemd, bạn chỉ cần sửa đổi một chút dịch vụ của mình để chấp nhận các socket bị vô hiệu hóa.

Sau này bạn có thể sử dụng kích hoạt ổ cắm systemd .

Không có khả năng, iptables hoặc các thủ thuật khác là cần thiết.

Đây là nội dung của các tệp systemd có liên quan từ ví dụ này về máy chủ http python đơn giản

Tập tin httpd-true.service

[Unit]
Description=Httpd true 

[Service]
ExecStart=/usr/local/bin/httpd-true
User=subsonic

PrivateTmp=yes

Tập tin httpd-true.socket

[Unit]
Description=HTTPD true

[Socket]
ListenStream=80

[Install]
WantedBy=default.target

7

Lúc khởi động:

iptables -A PREROUTING -t nat -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 8080

Sau đó, bạn có thể liên kết với cổng bạn chuyển tiếp.


--to-porttồn tại không? man iptableschỉ đề cập --to-ports(số nhiều).
Abdull

Tôi đã nhận thấy một số khác biệt và tôi đã nhảy xung quanh
James

Xem câu trả lời này để biết cách kết hợp nó với systemd.
Greg Slepak


3

Ngoài ra còn có "cách djb". Bạn có thể sử dụng phương pháp này để bắt đầu quá trình của mình khi root chạy trên bất kỳ cổng nào trong tcpserver, sau đó nó sẽ trao quyền điều khiển quá trình cho người dùng mà bạn chỉ định ngay sau khi quá trình bắt đầu.

#!/bin/sh

UID=$(id -u username)
GID=$(id -g username)
exec tcpserver -u "${UID}" -g "${GID}" -RHl0 0 port /path/to/binary &

Để biết thêm thông tin, hãy xem: http://thedjbway.b0llix.net/daemontools/uidgid.html


1

Vì OP chỉ là phát triển / thử nghiệm, nên các giải pháp nhỏ hơn có thể hữu ích:

setcap có thể được sử dụng trên trình thông dịch của tập lệnh để cấp khả năng cho tập lệnh. Nếu setcaps trên nhị phân trình thông dịch toàn cầu không được chấp nhận, hãy tạo một bản sao cục bộ của nhị phân (bất kỳ người dùng nào cũng có thể) và lấy root để setcap trên bản sao này. Python2 (ít nhất) hoạt động đúng với một bản sao cục bộ của trình thông dịch trong cây phát triển tập lệnh của bạn. Không cần suid để người dùng root có thể kiểm soát những khả năng mà người dùng có quyền truy cập.

Nếu bạn cần theo dõi các bản cập nhật toàn hệ thống cho trình thông dịch, hãy sử dụng tập lệnh shell như sau để chạy tập lệnh của bạn:

#!/bin/sh
#
#  Watch for updates to the Python2 interpreter

PRG=python_net_raw
PRG_ORIG=/usr/bin/python2.7

cmp $PRG_ORIG $PRG || {
    echo ""
    echo "***** $PRG_ORIG has been updated *****"
    echo "Run the following commands to refresh $PRG:"
    echo ""
    echo "    $ cp $PRG_ORIG $PRG"
    echo "    # setcap cap_net_raw+ep $PRG"
    echo ""
    exit
}

./$PRG $*

1

Tôi đã thử phương pháp iptables PREROUTING REDIRECT. Trong các nhân cũ hơn, có vẻ như loại quy tắc này không được hỗ trợ cho IPv6 . Nhưng rõ ràng nó hiện được hỗ trợ trong ip6tables v1.4.18 và Linux kernel v3.8.

Tôi cũng nhận thấy rằng PREROUTING REDIRECT không hoạt động đối với các kết nối được khởi tạo trong máy. Để làm việc cho các kết hợp từ máy cục bộ, hãy thêm quy tắc OUTPUT - xem chuyển hướng cổng iptables không hoạt động cho localhost . Ví dụ:

iptables -t nat -I OUTPUT -o lo -p tcp --dport 80 -j REDIRECT --to-port 8080

Tôi cũng thấy rằng PREROUTING REDIRECT cũng ảnh hưởng đến các gói được chuyển tiếp . Nghĩa là, nếu máy cũng chuyển tiếp các gói giữa các giao diện (ví dụ: nếu nó hoạt động như một điểm truy cập Wi-Fi được kết nối với mạng Ethernet), thì quy tắc iptables cũng sẽ bắt các kết nối của máy khách được kết nối với các điểm đến Internet và chuyển hướng chúng đến máy. Đó không phải là điều tôi muốn, tôi chỉ muốn chuyển hướng các kết nối được hướng đến chính máy. Tôi thấy tôi có thể làm cho nó chỉ ảnh hưởng đến các gói được gửi đến hộp, bằng cách thêm -m addrtype --dst-type LOCAL. Ví dụ:

iptables -A PREROUTING -t nat -p tcp --dport 80 -m addrtype --dst-type LOCAL -j REDIRECT --to-port 8080

Một khả năng khác là sử dụng chuyển tiếp cổng TCP. Ví dụ: sử dụng socat:

socat TCP4-LISTEN:www,reuseaddr,fork TCP4:localhost:8080

Tuy nhiên, một nhược điểm của phương pháp đó là, ứng dụng đang nghe trên cổng 8080 sau đó không biết địa chỉ nguồn của các kết nối đến (ví dụ để ghi nhật ký hoặc các mục đích nhận dạng khác).


0

Trả lời vào 2015 / tháng 9:

ip6tables hiện hỗ trợ IPV6 NAT: http://www.netfilter.org/projects/iptables/files/changes-iptables-1.4.17.txt

Bạn sẽ cần kernel 3.7+

Bằng chứng:

[09:09:23] root@X:~ ip6tables -t nat -vnL
Chain PREROUTING (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination
    0     0 REDIRECT   tcp      eth0   *       ::/0                 ::/0                 tcp dpt:80 redir ports 8080
    0     0 REDIRECT   tcp      eth0   *       ::/0                 ::/0                 tcp dpt:443 redir ports 1443

Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination

Chain OUTPUT (policy ACCEPT 6148 packets, 534K bytes)
 pkts bytes target     prot opt in     out     source               destination

Chain POSTROUTING (policy ACCEPT 6148 packets, 534K bytes)
 pkts bytes target     prot opt in     out     source               destination
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.