Cho phép quá trình không root liên kết với cổng 80 và 443?


104

Có thể điều chỉnh một tham số kernel để cho phép chương trình userland liên kết với cổng 80 và 443 không?

Lý do tôi hỏi là tôi nghĩ thật ngu ngốc khi cho phép một quy trình đặc quyền để mở một ổ cắm và lắng nghe. Bất cứ điều gì mở ổ cắm và lắng nghe đều có rủi ro cao và các ứng dụng rủi ro cao không nên chạy dưới quyền root.

Thay vào đó, tôi cố gắng tìm hiểu quá trình không đặc quyền nào đang lắng nghe trên cổng 80 thay vì cố gắng loại bỏ phần mềm độc hại vùi lấp với các đặc quyền gốc.


1
Xem serverfault.com/questions/268099stackoverflow.com/questions/413807 . Câu trả lời ngắn gọn là không.
Sami Laine

10
Câu trả lời dài tho là có .. vì vậy loại câu trả lời ngắn cũng nên có.
BT

4
Câu trả lời ngắn gọn có.
Jason C

Câu trả lời:


163

Tôi không chắc những câu trả lời và bình luận khác ở đây đang đề cập đến. Điều này là có thể khá dễ dàng. Có hai tùy chọn, cả hai đều cho phép truy cập vào các cổng được đánh số thấp mà không cần phải nâng quá trình lên root:

Tùy chọn 1: Sử dụng CAP_NET_BIND_SERVICEđể cấp quyền truy cập cổng được đánh số thấp cho một quy trình:

Với điều này, bạn có thể cấp quyền truy cập vĩnh viễn vào một nhị phân cụ thể để liên kết với các cổng được đánh số thấp thông qua setcaplệnh:

sudo setcap CAP_NET_BIND_SERVICE=+eip /path/to/binary

Để biết thêm chi tiết về phần e / i / p, xem cap_from_text.

Sau khi làm điều này, /path/to/binarysẽ có thể liên kết với các cổng được đánh số thấp. Lưu ý rằng bạn phải sử dụng setcaptrên chính nhị phân chứ không phải là một liên kết tượng trưng.

Tùy chọn 2: Sử dụng authbindđể cấp quyền truy cập một lần, với kiểm soát người dùng / nhóm / cổng tốt hơn:

Công cụ authbind( trang man ) tồn tại chính xác cho việc này.

  1. Cài đặt authbindbằng trình quản lý gói yêu thích của bạn.

  2. Định cấu hình nó để cấp quyền truy cập vào các cổng có liên quan, ví dụ: cho phép 80 và 443 từ 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. Bây giờ 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
    

Có những mặt thăng trầm của cả hai điều trên. Tùy chọn 1 cấp quyền tin cậy 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. Tùy chọn 2 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 nhưng, AFAIK, chỉ hỗ trợ IPv4.


Có thực sự cần sự rwxcho phép?
matt

Để hoàn nguyên thao tác trong Tùy chọn 1, bạn có chạy lại lệnh bằng cách sử dụng -pintead của +eipkhông?
eugene1832

5
Xin lưu ý rằng, với setcap, nếu bạn ghi đè lên tệp thực thi mà bạn cấp đặc quyền cho (ví dụ: thực hiện xây dựng lại) thì nó sẽ mất trạng thái cổng đặc quyền và bạn phải cung cấp lại đặc quyền đó: |
rogerdpack

1
Một cái gì đó mà tôi đã phải nghịch ngợm; Tôi đã cố gắng chạy một dịch vụ sysv, chạy một ruby ​​thực thi sử dụng ruby. Bạn cần phải cung cấp cho các setcapphép trên version- thực thi ruby cụ thể , ví dụ như/usr/bin/ruby1.9.1
Christian Rondeau

3
Tôi có nghi ngờ rằng chmoding đến 777 các byporttập tin là ý tưởng tốt nhất. Tôi đã thấy cho phép hoán vị từ 500đến 744. Tôi sẽ bị mắc kẹt với một trong những hạn chế nhất làm việc cho bạn.
Pere

28

Dale Hagglund là tại chỗ. Vì vậy, tôi sẽ nói điều tương tự nhưng theo một cách khác, với một số chi tiết và ví dụ cụ thể. ☺

Điều đúng đắn cần làm trong thế giới Unix và Linux là:

  • để có một chương trình nhỏ, đơn giản, dễ nghe, chạy như siêu người dùng và liên kết ổ cắm nghe;
  • để có một chương trình nhỏ, đơn giản, dễ kiểm toán khác, bỏ các đặc quyền, được sinh ra bởi chương trình đầu tiên;
  • để có được dịch vụ, trong một chương trình thứ ba riêng biệt , chạy trong tài khoản không phải là siêu người dùng và chuỗi được tải bởi chương trình thứ hai, hy vọng đơn giản sẽ kế thừa một bộ mô tả tệp mở cho ổ cắm.

Bạn có ý tưởng sai về nơi có nguy cơ cao. Nguy cơ cao nằm ở việc đọc từ mạng và hành động theo những gì được đọc không phải trong các hành động đơn giản là mở ổ cắm, ràng buộc nó với một cổng và gọi listen(). Đó là một phần của dịch vụ truyền thông thực tế có rủi ro cao. Các phần mở, bind()listen(), và thậm chí (ở một mức độ nào đó) phần đó accepts()không phải là rủi ro cao và có thể được chạy dưới sự bảo trợ của siêu người dùng. Họ không sử dụng và hành động (ngoại trừ địa chỉ IP nguồn trong accept()trường hợp) dữ liệu nằm dưới sự kiểm soát của những người lạ không tin cậy qua mạng.

Có nhiều cách để làm việc này.

inetd

Như Dale Hagglund nói, "siêu máy chủ mạng" cũ inetdthực hiện điều này. Tài khoản mà quy trình dịch vụ được chạy là một trong những cột trong inetd.conf. Nó không tách phần nghe và phần đặc quyền thả thành hai chương trình riêng biệt, nhỏ và dễ nghe, nhưng nó tách mã dịch vụ chính thành một chương trình riêng biệt, exec()trong một quy trình dịch vụ mà nó sinh ra với một bộ mô tả tệp mở cho ổ cắm.

Khó khăn của kiểm toán không phải là vấn đề lớn, vì người ta chỉ phải kiểm toán một chương trình. inetdVấn đề lớn của không phải là kiểm toán quá nhiều mà là nó không cung cấp kiểm soát dịch vụ thời gian chạy chi tiết đơn giản, so với các công cụ gần đây.

UCSPI-TCP và daemontools

Các gói UCSPI-TCPdaemontools của Daniel J. Bernstein được thiết kế để thực hiện việc này cùng nhau. Người ta có thể thay thế sử dụng bộ công cụ mã hóa daemontools tương đương phần lớn của Bruce Guenter .

Chương trình để mở bộ mô tả tệp ổ cắm và liên kết với cổng cục bộ đặc quyền là tcpservertừ UCSPI-TCP. Nó làm cả listen()accept().

tcpserversau đó sinh ra một chương trình dịch vụ tự hủy bỏ quyền root (vì giao thức được phục vụ bao gồm bắt đầu là siêu người dùng và sau đó "đăng nhập", ví dụ như trường hợp FTP hoặc daemon SSH) hoặc setuidgidlà một trình nền SSH chương trình nhỏ độc lập và dễ kiểm tra, chỉ bỏ các đặc quyền và sau đó tải chuỗi cho chương trình dịch vụ phù hợp (không có phần nào chạy với các đặc quyền siêu người dùng, như trường hợp, như, nói, qmail-smtpd).

Do đó, một runtập lệnh dịch vụ sẽ là ví dụ (tập lệnh này cho dummyidentd để cung cấp dịch vụ IDENT null):

#!/bin/sh -e
exec 2>&1
exec \
tcpserver 0 113 \
setuidgid nobody \
dummyidentd.pl

quà vặt

Gói nosh của tôi được thiết kế để làm điều này. Nó có một setuidgidtiện ích nhỏ , giống như những người khác. Một điểm khác biệt nhỏ là nó có thể sử dụng được với systemdcác dịch vụ "LISTEN_FDS" kiểu cũng như với các dịch vụ UCSPI-TCP, vì vậy tcpserverchương trình truyền thống được thay thế bằng hai chương trình riêng biệt: tcp-socket-listentcp-socket-accept.

Một lần nữa, các tiện ích đơn mục đích sinh sản và chuỗi tải nhau. Một điều thú vị của thiết kế là người ta có thể bỏ các đặc quyền siêu người dùng sau listen()nhưng thậm chí trước đó accept(). Đây là một runkịch bản cho qmail-smtpdđiều đó thực sự làm chính xác điều đó:

#!/bin/nosh
fdmove -c 2 1
clearenv --keep-path --keep-locale
envdir env/
softlimit -m 70000000
tcp-socket-listen --combine4and6 --backlog 2 ::0 smtp
setuidgid qmaild
sh -c 'exec \
tcp-socket-accept -v -l "${LOCAL:-0}" -c "${MAXSMTPD:-1}" \
ucspi-socket-rules-check \
qmail-smtpd \
'

Các chương trình chạy dưới sự bảo hộ của superuser là những công cụ chuỗi nạp dịch vụ-agnostic nhỏ fdmove, clearenv, envdir, softlimit, tcp-socket-listen, và setuidgid. Tại điểm shđược bắt đầu, ổ cắm được mở và ràng buộc với smtpcổng và quá trình không còn có đặc quyền siêu người dùng.

s6, s6-mạng và thực thi

Các gói mạng s6s6 của Laurent Bercot được thiết kế để thực hiện việc này cùng nhau. Các lệnh có cấu trúc rất giống với các lệnh và UCSPI-TCP. daemontools

runcác kịch bản sẽ giống nhau, ngoại trừ việc thay thế s6-tcpservercho tcpservers6-setuidgidcho setuidgid. Tuy nhiên, người ta cũng có thể chọn để sử dụng M. Bercot của execline công cụ cùng một lúc.

Dưới đây là ví dụ về dịch vụ FTP, được sửa đổi nhẹ từ bản gốc của Wayne Marshall , sử dụng chương trình execline, s6, s6 và chương trình máy chủ FTP từ publicfile :

#!/command/execlineb -PW
multisubstitute {
    define CONLIMIT 41
    define FTP_ARCHIVE "/var/public/ftp"
}
fdmove -c 2 1
s6-envuidgid pubftp 
s6-softlimit -o25 -d250000 
s6-tcpserver -vDRH -l0 -b50 -c ${CONLIMIT} -B '220 Features: a p .' 0 21 
ftpd ${FTP_ARCHIVE}

ipsvd

Ipsvd của Gerrit Pape là một bộ công cụ khác chạy dọc theo cùng dòng với mạng ucspi-tcp và s6. Các công cụ này chpsttcpsvdlần này, nhưng chúng làm điều tương tự, và mã rủi ro cao thực hiện việc đọc, xử lý và viết các thứ được gửi qua mạng bởi các khách hàng không tin cậy vẫn nằm trong một chương trình riêng biệt.

Đây là ví dụ của M. Pape về việc chạy fnordtrong một runkịch bản:

#!/bin/sh
exec 2>&1
cd /public/10.0.5.4
exec \
chpst -m300000 -Uwwwuser \
tcpsvd -v 10.0.5.4 443 sslio -v -unobody -//etc/fnord/jail -C./cert.pem \
fnord

systemd

systemd, hệ thống giám sát và khởi tạo dịch vụ mới có thể được tìm thấy trong một số bản phân phối Linux, được dự định để làm những gì inetdcó thể làm . Tuy nhiên, nó không sử dụng một bộ các chương trình nhỏ khép kín. Người ta phải kiểm toán systemdtoàn bộ, thật không may.

Với systemdmột người tạo các tệp cấu hình để xác định ổ cắm systemdnghe và dịch vụ systemdbắt đầu. Tệp "đơn vị" dịch vụ có các cài đặt cho phép người ta kiểm soát rất nhiều quá trình dịch vụ, bao gồm cả những gì người dùng chạy.

Với người dùng đó được đặt là không phải là siêu người dùng, systemdthực hiện tất cả công việc mở ổ cắm, liên kết nó với một cổng và gọi listen()(và, nếu được yêu cầu accept()) trong quy trình số 1 với tư cách là siêu người dùng và quy trình dịch vụ mà nó xử lý sinh sản chạy mà không có đặc quyền siêu người dùng.


2
Cảm ơn cho những lời khen. Đây là một bộ sưu tập lớn lời khuyên cụ thể. +1.
Dale Hagglund

11

Tôi có một cách tiếp cận khá khác nhau. Tôi muốn sử dụng cổng 80 cho máy chủ node.js. Tôi không thể làm điều đó vì Node.js đã được cài đặt cho người dùng không phải sudo. Tôi đã cố gắng sử dụng symlink, nhưng nó không hiệu quả với tôi.

Sau đó, tôi đã biết rằng tôi có thể chuyển tiếp các kết nối từ một cổng sang một cổng khác. Vì vậy, tôi đã khởi động máy chủ trên cổng 3000 và thiết lập một cổng chuyển tiếp từ cổng 80 sang cổng 3000.

Liên kết này cung cấp các lệnh thực tế có thể được sử dụng để làm điều này. Đây là các lệnh -

localhost / loopback

sudo iptables -t nat -I OUTPUT -p tcp -d 127.0.0.1 --dport 80 -j REDIRECT --to-ports 3000

bên ngoài

sudo iptables -t nat -I PREROUTING -p tcp --dport 80 -j REDIRECT --to-ports 3000

Tôi đã sử dụng lệnh thứ hai và nó đã làm việc cho tôi. Vì vậy, tôi nghĩ rằng đây là một nền tảng trung gian để không cho phép người dùng xử lý truy cập trực tiếp vào các cổng thấp hơn, nhưng cho phép họ truy cập bằng cách sử dụng chuyển tiếp cổng.


6
+1 để suy nghĩ kỹ lưỡng
Richard Wiseman

4

Bản năng của bạn là hoàn toàn chính xác: đó là một ý tưởng tồi khi có một chương trình phức tạp lớn chạy như root, bởi vì sự phức tạp của chúng khiến chúng khó tin tưởng.

Nhưng, đó cũng là một ý tưởng tồi khi cho phép người dùng thường xuyên liên kết với các cổng đặc quyền, bởi vì các cổng như vậy thường đại diện cho các dịch vụ hệ thống quan trọng.

Cách tiếp cận tiêu chuẩn để giải quyết mâu thuẫn rõ ràng này là phân tách đặc quyền . Ý tưởng cơ bản là tách chương trình của bạn thành hai (hoặc nhiều) phần, mỗi phần thực hiện một phần được xác định rõ ràng của ứng dụng tổng thể và giao tiếp bằng các giao diện giới hạn đơn giản.

Trong ví dụ bạn đưa ra, bạn muốn tách chương trình của mình thành hai phần. Một cái chạy như root và mở và liên kết với ổ cắm đặc quyền, và sau đó chuyển nó bằng cách nào đó sang phần khác, chạy như một người dùng thông thường.

Hai cách chính để đạt được sự tách biệt này.

  1. Một chương trình duy nhất bắt đầu như root. Điều đầu tiên nó làm là tạo ra các ổ cắm cần thiết, theo cách đơn giản và hạn chế nhất có thể. Sau đó, nó bỏ các đặc quyền, nghĩa là, nó tự chuyển đổi thành một quy trình chế độ người dùng thông thường và thực hiện tất cả các công việc khác. Bỏ các đặc quyền một cách chính xác là khó khăn, vì vậy hãy dành thời gian để nghiên cứu đúng cách để làm điều đó.

  2. Một cặp chương trình giao tiếp qua cặp ổ cắm được tạo bởi quy trình cha. Một chương trình trình điều khiển không đặc quyền nhận các đối số ban đầu và có lẽ thực hiện một số xác thực đối số cơ bản. Nó tạo ra cặp ổ cắm được kết nối thông qua socket socket (), sau đó rẽ nhánh và thực thi hai chương trình khác sẽ thực hiện công việc thực sự và giao tiếp thông qua cặp socket. Một trong số này là đặc quyền và sẽ tạo ra ổ cắm máy chủ, và bất kỳ hoạt động đặc quyền nào khác, và cái còn lại sẽ thực hiện ứng dụng phức tạp hơn và do đó ít đáng tin cậy hơn.

[1] http://en.m.wikipedia.org/wiki/Priv đặc biệt_separation


Những gì đề xuất của bạn không được coi là thực hành tốt nhất. Bạn có thể nhìn vào inetd, có thể nghe trên một ổ cắm đặc quyền và sau đó trao ổ cắm đó cho một chương trình không có đặc quyền.
Dale Hagglund

3

Giải pháp đơn giản nhất: xóa tất cả các cổng đặc quyền trên linux

Hoạt động trên Ubuntu / debian:

#save configuration permanently
echo 'net.ipv4.ip_unprivileged_port_start=0' > /etc/sysctl.d/50-unprivileged-ports.conf
#apply conf
sysctl --system

(hoạt động tốt cho VirtualBox với tài khoản không root)

Bây giờ, hãy cẩn thận về bảo mật vì tất cả người dùng có thể liên kết tất cả các cổng!


Thật khéo léo. Một nit nhỏ: cấu hình mở 80 và 443, nhưng nó cũng mở tất cả các cổng khác. Quyền thư giãn trên các cổng khác có thể không được mong muốn.
jww
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.