비 루트 프로세스가 포트 80 및 443에 바인드되도록 허용합니까? 매개 변수를 조정할 수 있습니까? 내가 묻는

userland 프로그램이 포트 80 및 443에 바인딩 할 수 있도록 커널 매개 변수를 조정할 수 있습니까?

내가 묻는 이유는 권한있는 프로세스가 소켓을 열고 듣는 것이 어리석은 것이라고 생각하기 때문입니다. 소켓을 열고 듣는 것은 위험이 높으며 위험이 높은 응용 프로그램은 루트로 실행해서는 안됩니다.

루트 권한으로 잠복 한 맬웨어를 제거하려고 시도하는 대신 권한이없는 프로세스가 포트 80에서 수신 대기중인 것이 무엇인지 파악하려고합니다.



답변

다른 답변과 의견이 무엇을 말하는지 잘 모르겠습니다. 이것은 오히려 쉽게 가능합니다. 프로세스를 루트로 올리지 않고도 낮은 번호의 포트에 액세스 할 수있는 두 가지 옵션이 있습니다.

옵션 1 : CAP_NET_BIND_SERVICE프로세스에 낮은 번호의 포트 액세스 권한을 부여하는 데 사용하십시오 .

이를 통해 다음 setcap명령을 통해 번호가 낮은 포트에 바인딩하기 위해 특정 바이너리에 영구적으로 액세스 할 수 있습니다 .

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

e / i / p 부분에 대한 자세한 내용은을 참조하십시오 cap_from_text.

이 작업을 수행 한 후 /path/to/binary번호가 낮은 포트에 바인딩 할 수 있습니다. setcap심볼릭 링크가 아닌 이진 자체에 사용해야합니다 .

옵션 2 : authbind보다 나은 사용자 / 그룹 / 포트 제어를 통해 일회성 액세스 권한을 부여하는 데 사용 합니다.

이를 위해 authbind( man page ) 도구가 정확하게 존재합니다.

  1. authbind선호하는 패키지 관리자를 사용하여 설치 하십시오.

  2. 모든 사용자 및 그룹에서 80 및 443을 허용하도록 관련 포트에 대한 액세스 권한을 부여하도록 구성하십시오.

    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. 이제 다음을 통해 명령을 실행하십시오 authbind(선택적으로 지정 --deep하거나 다른 인수는 매뉴얼 페이지 참조).

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

    예 :

    authbind --deep java -jar SomeServer.jar
    

위의 두 가지 장점과 단점이 있습니다. 옵션 1은 이진에 신뢰를 부여 하지만 포트 별 액세스에 대한 제어는 제공하지 않습니다. 옵션 2는 사용자 / 그룹에 신뢰를 부여 하고 포트 별 액세스를 제어하지만 AFAIK는 IPv4 만 지원합니다.


답변

데일 하그 룬드가 등장했습니다. 그래서 나는 똑같은 말을하지만 다른 구체적인 방법과 예를 들겠습니다. ☺

유닉스와 리눅스 세계에서해야 할 올바른 일은 :

  • 수퍼 유저로서 실행되고 청취 소켓을 바인딩하는 작고 간단하며 쉽게 감사 할 수있는 프로그램이 있어야합니다.
  • 첫 번째 프로그램에 의해 생성 된 권한을 삭제하는 또 다른 작고 간단하며 쉽게 감사 할 수있는 프로그램을 갖추어야합니다.
  • 별도의 세 번째 프로그램 에서 서비스의 핵심을 수퍼 유저가 아닌 계정으로 실행하고 두 번째 프로그램에 의해로드 된 체인을 사용하여 소켓에 대한 열린 파일 설명자를 간단히 상속해야합니다.

높은 위험이 어디에 있는지 잘못 알고 있습니다. 높은 위험은 네트워크 에서 읽고 소켓을 열고 포트에 바인딩 한 다음 호출하는 간단한 동작으로 읽지 않은 내용을 처리하는 것 listen()입니다. 위험이 높은 실제 통신을 수행하는 서비스의 일부입니다. bind(), 및 listen(), 심지어는 어느 정도까지 개방 된 부분은 accepts()위험이 높지 않으며 수퍼 유저의 요구에 따라 실행될 수 있습니다. accept()네트워크를 통해 신뢰할 수없는 낯선 사람이 제어하는 ​​데이터를 사용하지 않습니다 (소스 IP 주소 제외 ).

이를 수행하는 방법에는 여러 가지가 있습니다.

inetd

데일 해글 런드 (Dale Hagglund)가 말했듯이, 오래된 “네트워크 슈퍼 서버” inetd가이를 수행합니다. 서비스 프로세스가 실행되는 계정은의 열 중 하나입니다 inetd.conf. 청취 부분과 삭제 권한 부분을 작고 쉽게 감사 할 수있는 두 개의 개별 프로그램으로 분리하지는 않지만 주 서비스 코드를 별도의 프로그램으로 분리 exec()합니다 (서비스 프로세스에서 열린 파일 디스크립터로 생성됨) 소켓.

하나의 프로그램 만 감사하면되기 때문에 감사의 어려움은 그리 큰 문제가되지 않습니다. inetd주요 문제는 너무 많은 감사가 아니라 최신 도구와 비교할 때 간단한 세분화 된 런타임 서비스 제어를 제공하지 않는 것입니다.

UCSPI-TCP 및 데몬 툴

Daniel J. Bernstein의 UCSPI-TCPdaemontools 패키지는이를 함께 수행하도록 설계되었습니다. 대안으로 Bruce Guenter의 대체로 동일한 daemontools-encore 도구 세트를 사용할 수 있습니다 .

소켓 파일 디스크립터를 열고 특권 로컬 포트에 바인드하는 프로그램 tcpserver은 UCSPI-TCP의 것입니다. 와를 모두 수행 listen()합니다 accept().

tcpserver그런 다음 루트 권한 자체를 삭제하는 서비스 프로그램을 생성합니다 (서비스중인 프로토콜이 수퍼 유저로 시작한 다음 FTP 또는 SSH 데몬의 경우와 같이 “로그온”하기 때문에) setuidgid. 자체적으로 포함 된 작고 쉽게 감사 할 수있는 프로그램으로, 권한을 떨어 뜨린 다음 서비스 프로그램에로드를 적절하게 연결합니다 (이와 같이 어떤 부분도 수퍼 유저 권한으로 실행되지 않습니다 qmail-smtpd).

따라서 서비스 run스크립트는 다음과 같습니다 (이것은 null IDENT 서비스를 제공 하기 위해 더미 ID 를 위한 것임 ).

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

으악

내 nosh 패키지 는이 작업을 수행하도록 설계되었습니다. setuidgid다른 것과 마찬가지로 작은 유틸리티가 있습니다. 하나 개 약간의 차이는 그것이로 사용할 수 있다는 것입니다 systemd전통 그래서 스타일 “LISTEN_FDS”서비스뿐만 아니라와 UCSPI – TCP 서비스 tcpserver프로그램은 두 개의 별도의 프로그램으로 대체됩니다 : tcp-socket-listentcp-socket-accept.

다시 한 번, 다목적 유틸리티가 생성되고 체인이로드됩니다. 디자인의 흥미로운 점 중 하나는 listen()심지어 그 이후에도 슈퍼 유저 권한을 떨어 뜨릴 수 있다는 것 accept()입니다. 그에 대한 run스크립트는 다음과 같습니다 qmail-smtpd.

#!/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 \
'

수퍼 유저의 후원으로 실행 프로그램은 작은 서비스에 얽매이지 체인 로딩 도구 fdmove, clearenv, envdir, softlimit, tcp-socket-listen,와 setuidgid. 점으로 sh시작, 소켓은 개방과 결합하지 smtp포트, 프로세스는 더 이상 수퍼 유저 권한을가집니다.

s6, s6-networking 및 execline

Laurent Bercot의 s6s6 네트워킹 패키지는이 작업을 함께 수행하도록 설계되었습니다. 명령은 daemontoolsUCSPI-TCP 의 명령과 구조적으로 매우 유사합니다 .

run스크립트는 s6-tcpserverfor tcpservers6-setuidgidfor를 대체하는 것을 제외하고는 거의 동일 합니다 setuidgid. 그러나 M. Bercot의 execline 툴셋을 동시에 사용하도록 선택할 수도 있습니다 .

다음은 Wayne Marshall의 원본 에서 약간 수정 된 execline, s6, s6-networking 및 publicfile 의 FTP 서버 프로그램을 사용 하는 FTP 서비스의 예입니다 .

#!/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

Gerrit Pape의 ipsvd 는 ucspi-tcp 및 s6-networking과 동일한 라인을 따라 실행되는 또 다른 툴셋입니다. 도구는 현재 chpsttcpsvd동일하지만 동일한 작업을 수행하며 신뢰할 수없는 클라이언트가 네트워크를 통해 전송 한 내용을 읽고 처리하고 작성하는 고위험 코드는 여전히 별도의 프로그램에 있습니다.

여기 M. 파페의 예를 실행하는 fnordA의 run스크립트 :

#!/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일부 리눅스 배포판에서 찾을 수있는 새로운 서비스 감독 및 초기화 시스템은 일을하기위한 것입니다 inetd할 수 있습니다 . 그러나 작은 자체 포함 된 프로그램 제품군을 사용하지 않습니다. systemd불행히도 전체 감사 를해야합니다.

systemd하나의 소켓 정의 구성 파일을 생성 systemd수신 대기하고 서비스를 systemd시작합니다. 서비스 “단위”파일에는 실행중인 사용자를 포함하여 서비스 프로세스를 크게 제어 할 수있는 설정이 있습니다.

해당 사용자가 수퍼 유저가 아닌 사용자로 설정 systemd되면 소켓을 열고 포트에 바인딩 한 다음 프로세스 # 1에서 수퍼 유저로 호출하고 listen()(필요한 경우 accept()) 작업을 수행합니다. 스폰은 수퍼 유저 권한없이 실행됩니다.


답변

나는 다른 접근법을 가지고 있습니다. node.js 서버에 포트 80을 사용하고 싶었습니다. Sudo가 아닌 사용자를 위해 Node.js가 설치되어서 할 수 없었습니다. 심볼릭 링크를 사용하려고했지만 작동하지 않았습니다.

그런 다음 한 포트에서 다른 포트로 연결을 전달할 수 있다는 것을 알게되었습니다. 그래서 포트 3000에서 서버를 시작하고 포트 80에서 포트 3000으로 포트 포워드를 설정했습니다.

이 링크 는이를 수행하는 데 사용할 수있는 실제 명령을 제공합니다. 명령은 다음과 같습니다.

로컬 호스트 / 루프백

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

외부

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

나는 두 번째 명령을 사용했고 그것은 나를 위해 일했다. 따라서 이것은 사용자 프로세스가 하위 포트에 직접 액세스하는 것을 허용하지 않지만 포트 전달을 사용하여 액세스를 제공하는 중간 근거라고 생각합니다.


답변

본능은 완전히 정확합니다. 복잡한 프로그램을 복잡하게 만들면 신뢰하기 어렵 기 때문에 복잡한 대규모 프로그램을 루트로 실행하는 것은 좋지 않습니다.

그러나 일반 사용자가 권한있는 포트에 바인딩하도록 허용하는 것도 좋지 않습니다. 이러한 포트는 일반적으로 중요한 시스템 서비스를 나타 내기 때문입니다.

이러한 명백한 모순을 해결하기위한 표준 접근 방식은 권한 분리 입니다. 기본 아이디어는 프로그램을 두 개 이상의 부분으로 분리하는 것입니다. 각 부분은 전체 응용 프로그램에서 잘 정의 된 부분을 수행하고 간단한 제한된 인터페이스로 통신합니다.

제시 한 예에서 프로그램을 두 부분으로 분리하려고합니다. 하나는 루트로 실행되어 특권 소켓에 열리고 바인딩 된 다음 일반 사용자로 실행되는 다른 부분으로 전달합니다.

이 분리를 달성하기위한이 두 가지 주요 방법.

  1. 루트로 시작하는 단일 프로그램. 가장 먼저하는 일은 가능한 한 간단하고 제한된 방식으로 필요한 소켓을 만드는 것입니다. 그런 다음 권한을 삭제합니다. 즉, 자신을 일반 사용자 모드 프로세스로 변환하고 다른 모든 작업을 수행합니다. 권한을 올바르게 삭제하는 것은 까다로울 수 있으므로 올바른 방법으로 공부하십시오.

  2. 부모 프로세스에 의해 작성된 소켓 쌍을 통해 통신하는 프로그램 쌍. 권한이없는 드라이버 프로그램은 초기 인수를 수신하고 기본 인수 유효성 검사를 수행합니다. socketpair ()를 통해 연결된 소켓 쌍을 만든 다음 실제 작업을 수행하고 소켓 쌍을 통해 통신하는 두 개의 다른 프로그램을 포크하고 실행합니다. 이 중 하나는 특권이 있으며 서버 소켓 및 기타 특권 작업을 작성하고 다른 하나는 더 복잡하고 신뢰할 수있는 응용 프로그램 실행을 수행합니다.

[1] http://en.m.wikipedia.org/wiki/Privilege_separation


답변

가장 간단한 해결책 : 리눅스에서 모든 특권 포트를 제거

우분투 / 데비안에서 작동합니다 :

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

(루트가 아닌 계정이있는 VirtualBox에 적합)

이제 모든 사용자가 모든 포트를 바인딩 할 수 있으므로 보안에주의하십시오!