루트가 아닌 프로세스가 Linux의 "권한있는"포트에 바인딩 할 수있는 방법이 있습니까?


389

저 외에 다른 사용자가 없을 때 개발 상자 에이 제한이 있다는 것은 매우 성가신 일입니다.

표준 해결 방법을 알고 있지만 아무도 내가 원하는 것을 정확하게 수행하지 않습니다.

  1. authbind (데비안 테스트 버전 1.0, IPv4 만 지원)
  2. iptables REDIRECT 대상을 사용하여 낮은 포트를 높은 포트로 재 지정합니다 ( "nat"테이블은 iptables의 IPv6 버전 인 ip6tables에 대해 아직 구현되지 않았습니다)
  3. sudo (루트로 실행하는 것은 피하려고하는 것입니다)
  4. SELinux (또는 유사) (이것은 단지 내 dev 상자입니다. 많은 추가 복잡성을 도입하고 싶지 않습니다.)

sysctl루트가 아닌 프로세스가 Linux에서 "권한이있는"포트 (1024보다 작은 포트)에 바인딩 할 수 있는 간단한 변수가 있습니까?

편집 : 경우에 따라 기능사용 하여이 작업을 수행 할 수 있습니다 .


5
내 경험상, 이것을 시도하는 한 가지 이유는 Apache (또는 lighttpd)를 사용하지 않고 웹 서버를 작성하는 것입니다.
S.Lott


15
이 경우 IPv6를 사용하고 있었기 때문에 "일반적인"해결 방법 (authbind 및 iptables REDIRECT) 중 일부가 작동하지 않는 이유가 있습니다.
Jason Creighton


1
몇 가지 방법이 있습니다. 비 루트 프로세스가 포트 80 및 443에 바인드 허용을 참조하십시오 . 슈퍼 유저.
jww

답변:


392

기능 시스템과 CAP_NET_BIND_SERVICE기능 을 지적한 사람들 덕분 입니다. 최근 커널이 있다면, 이것을 루트를 사용하지 않고 낮은 포트를 바인딩하는 서비스로 시작하는 것이 가능합니다. 짧은 대답은 당신이하는 것입니다 :

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

그런 다음 언제든지 program실행될 수 CAP_NET_BIND_SERVICE있습니다. setcap데비안 패키지에 libcap2-bin있습니다.

이제주의 사항 :

  1. 최소한 2.6.24 커널이 필요합니다
  2. 파일이 스크립트 인 경우 작동하지 않습니다. (즉, #! 행을 사용하여 인터프리터를 시작하십시오). 이 경우 이해하는 한 해석기 실행 기능 자체에 기능을 적용해야합니다. 통역사를 사용하는 모든 프로그램에는 기능이 있기 때문에 보안 악몽입니다. 이 문제를 해결할 수있는 깨끗하고 쉬운 방법을 찾을 수 없었습니다.
  3. Linux는 또는 program과 같은 상승 된 권한이 있는 모든 LD_LIBRARY_PATH를 비활성화 합니다. 따라서 자체를 사용하는 경우 포트 전달과 같은 다른 옵션을 조사해야 할 수도 있습니다.setcapsuidprogram.../lib/

자원:

참고 : RHEL은 먼저 이것을 v6에 추가했습니다 .


3
스크립트에 대한 부분 해결 방법 : 인터프리터 사본 (예 : bash)을 작성하고 기능을 제공하지만 사본이 필요한 사용자에게만 액세스를 제한하십시오. 물론 이러한 사용자는 신뢰할 수 있어야하지만 어쨌든 스크립트를 변경할 수 있습니다.
Erich Kitzmueller

3
위에서 언급 한 데비안 (바이너리) 패키지 외에도 개발자 사이트는 friedhoff.org/posixfilecaps.html 관련 논문 / 프레젠테이션 / 등입니다.
RandomNickName42

1
suse SLES 11.1에서 커널 매개 변수 file_caps = 1을 추가하여 grub menu.lst를 추가해야 작동했습니다.
Shay

2
예 @ C.Ross /usr/bin/java는 시스템에서 실행중인 모든 Java 응용 프로그램에 적용되어야 할 것이므로 기능을 열어야하기 때문입니다. 사용자별로 너무 나쁜 기능을 설정할 수도 없습니다.
joeytwiddle

5
재부팅시에도 setcap 설정이 유지됩니까? 그렇지 않은 경우 시스템 시작 중에 실행되도록이 규칙을 배치 할 표준 위치가 있습니까? 가 /etc/security/capability.conf어떤 도움 데비안 / 우분투에?
joeytwiddle

34

포트 리디렉션을 수행 할 수 있습니다. 이것이 Linux 상자에서 실행되는 Silverlight 정책 서버에 수행하는 작업입니다.

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

5
불행히도 이것은 라우팅 된 연결에 대해서만 작동합니다 (예 : 로컬 시스템에서).
zbyszek

@joeytwiddle 서버에서 실행 (시작)하는 스크립트에 있다고 생각하지만 잘못되었을 수 있습니다. 나도 알고 싶습니다 : P
Camilo Martin

@CamiloMartin 다시 한번 살펴보면, 질문에서 링크 된 데비안 문서 는 방화벽 스크립트에 배치하거나에서 생성하는 것이 좋습니다 /etc/network/if-up.d/firewall.
joeytwiddle

8
@zbyszek 추가 iptables 규칙을 사용하여 로컬 컴퓨터 연결에 사용할 수 있습니다. stackoverflow.com/a/31795603/1356953
00500005

이전 커널에서는 IPv6에서 지원되지 않는 것 같습니다 . 그러나 분명히 그것은 ip6tables v1.4.18 및 Linux 커널 v3.8에서 지원됩니다.
Craig McQueen

31

표준 방법은 루트로 시작하도록 "setuid"로 설정 한 다음 포트에 바인딩 되 자마자 연결을 수락하기 전에 루트 권한을 버리는 것입니다. Apache와 INN의 소스 코드에서 그 좋은 예를 볼 수 있습니다. Lighttpd가 또 다른 좋은 예라고 들었습니다.

또 다른 예는 파이프를 통해 통신하는 여러 데몬을 사용하는 Postfix이며, 그 중 하나 또는 두 개 (바이트 허용 또는 방출 제외)는 루트로 실행되고 나머지는 낮은 권한으로 실행됩니다.


1
흥미롭게도, 이것은 CAP_SETUID가 설정되지 않은 최신 버전의 Linux (아마도 우분투)에서는 작동하지 않습니다. 따라서 setuid가 필요한 경우 어쨌든이 기능을 설정해야합니다.
Matt

루트 권한을 삭제하는 것이 올바른 방법입니다. init / upstart / systemd 서비스를 시작한 경우 setuid를 설정할 필요는 없지만
Michael Hampton

2
프로그램이 C # (Mono), Java, Python과 같은 인터프리터 언어 또는 바이트 코드 인터프리터로 작성된 경우에는 어렵습니다. (펄은 펄 binfmt_misc과 'C'플래그 를 통해이 작업을 수행 한 것으로
Craig McQueen

바이트를 방출하는 것을 제외하고는 거의 수행하지 않습니다.
Ryan

바이너리 setuid를 설정하면 루트 프로세스로 실행됩니다. 그러나 질문은 바이너리 프로세스를 루트 프로세스로 실행하지 않고 달성하는 방법을 묻습니다. 이 솔루션은 다른 질문, 즉 "비 권한 사용자가 프로세스를 권한있는 포트에 바인딩 할 수있는 방법"에 대한 해답이지만 IMHO?
Michael Beer

24

또는 커널을 패치하고 검사를 제거하십시오.

(마지막 수단 옵션, 권장하지 않음).

에서 net/ipv4/af_inet.c읽은 두 줄을 제거하십시오.

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

커널은 더 이상 권한있는 포트를 확인하지 않습니다.


22
여러 가지 이유로 매우 나쁜 생각.
Adam Lassek

24
이것은 나쁜 생각입니다. 그러나 실제로 작동합니다. 그리고 확실히 나를 웃게했다.
Oto Brglez

23
물론 나쁜 생각입니다. 내가 마지막 수단을 말한 이유입니다. 오픈 소스의 요점은 원하는 방식으로 작동하지 않는 경우입니다.
Joshua

18
왜 당신이 다운 보트를 받았는지 잘 모르겠습니다. 악성 코드 작성자는 어떤 포트에서 수신 대기하는지 신경 쓰지 않으며 1024보다 큰 포트를 여는 것이 행복합니다. 모든 포트에 권한이 필요하거나 포트에 권한이 필요하지 않은 것 같습니다. 루트가 1024 미만의 포트를 열어야한다는 것은 루트로 실행되는 고위험 응용 프로그램을 의미합니다. 그것은 정말 바보 같은 생각입니다. 아마도 여기에 뭔가
빠졌을

9
당시에는 포트 <1024가 UNIX에서 UNIX 프로토콜로 사용되어 다른 쪽 끝에서 실행되는 코드가 루트로 실행되고 있음을 증명했습니다. 이것은 일반적인 보안 기능을 가진 일련의 UNIX 서버에서 합리적으로 잘 작동했습니다.
Joshua

22

예를 들어 포트 80이 앱에 3000으로 바인딩되도록 로컬 SSH 터널을 설정할 수 있습니다.

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

이는 스크립트 서버 작업과 매우 간단하다는 이점이 있습니다.


1
내 개인적인 마음에 드는 것. 매우 쉽게 켜고 끌 수 있으며 시스템 전체를 변경할 필요가 없습니다. 좋은 생각이야!
Dan Passaro

이것은 놀랍습니다. 가장 간단한 답변입니다.
michaelsnowden

1
나는 이것이 성능이 비싸지 않은 상황에서 적당히 비싸고 좋은 생각은 아니지만 그것을 테스트하지 않으면 확신 할 수 없습니다. SSH 암호화 비용은 0이 아닙니다.
lahwran

3
이것은 ssh 오버 헤드없이 netcat으로 수행 할 수 있습니다.sudo nc -l 80 | nc localhost 3000
Bugster

1
동일한 컴퓨터의 다른 포트로 데이터를 이동하는 경우에만 암호화 + 암호 해독합니까? 또한 UDP 트래픽에는 작동하지 않습니다.
Daniel F

19

2017 년 업데이트 :

authbind 사용


CAP_NET_BIND_SERVICE 또는 사용자 정의 커널보다 훨씬 낫습니다.

  • CAP_NET_BIND_SERVICE는 바이너리에 트러스트를 부여하지만 포트 별 액세스를 제어하지는 않습니다.
  • Authbind는 사용자 / 그룹에 신뢰를 부여하고 포트 별 액세스를 제어하며 IPv4 및 IPv6을 모두 지원합니다 (IPv6 지원은 최근 추가됨).

    1. 설치: apt-get install 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
      

커널 해킹에 대한 Joshua의 멋진 추천 (= 당신이하는 일을 알지 못한다면 권장하지 않음) :

먼저 여기에 게시했습니다 .

단순한. 정상적인 커널이나 오래된 커널로는 그렇지 않습니다.
다른 사람들이 지적했듯이 iptables는 포트를 전달할 수 있습니다.
다른 사람들이 지적한 것처럼 CAP_NET_BIND_SERVICE도 작업을 수행 할 수 있습니다.
물론 CAP_NET_BIND_SERVICE는 스크립트에서 프로그램을 시작하면 실패합니다. 쉘 인터프리터에 제한을 설정하지 않으면 서비스를 루트로 실행할 수 있습니다.
예를 들어 Java의 경우이를 적용해야합니다. 자바 JVM으로

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

분명히 이것은 모든 Java 프로그램이 시스템 포트를 바인딩 할 수 있음을 의미합니다.
모노 /.NET 용 Dito

또한 xinetd가 최고의 아이디어는 아니라고 확신합니다.
그러나 두 방법 모두 해킹이므로 제한을 해제하여 한계를 높이 지 않는 이유는 무엇입니까?
아무도 당신이 정상적인 커널을 실행해야한다고 말하지 않았으므로, 당신은 당신 자신을 실행할 수 있습니다.

최신 커널 (또는 현재 가지고있는 커널)의 소스를 다운로드하기 만하면됩니다. 이후에 다음으로 이동합니다.

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

이 라인을 찾으십시오

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

로 변경

#define PROT_SOCK 0

안전하지 않은 ssh 상황을 원하지 않으면 다음과 같이 변경하십시오. #define PROT_SOCK 24

일반적으로 http의 경우 79, 포트 25에서 SMTP를 사용할 때 24와 같이 필요한 가장 낮은 설정을 사용합니다.

그게 다야
커널을 컴파일하고 설치하십시오.
재부팅하십시오.
완료-바보 같은 한계는 사라졌고 스크립트에서도 작동합니다.

커널을 컴파일하는 방법은 다음과 같습니다.

https://help.ubuntu.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 ..

간단히 말해서, 보안을 유지하려면 iptables를 사용하고,이 제한이 다시는 귀찮게하지 않도록하려면 커널을 컴파일하십시오.


downvote에 대한 이유가 있습니까? 루트 증오 또는 커널 컴파일 명령이 작동하지 않습니까?
Stefan Steiger

4
커널을 수정하면 향후 업데이트가 훨씬 더 어려워집니다. 커널을 정기적으로 업데이트하기에는 너무 게으르다는 것을 알고 있기 때문에 나는 이것을하지 않을 것입니다. 이론적으로는 가능하지만 많은 상황에서 실행 가능한 옵션은 아닙니다.
kritzikratzi

아마도 더 안전한 방법은 chown login : chmod 544 / etc / authbind / byport / 23에 의해 chmod 777 / etc / authbind / byport / 80을 chown login : group / etc / authbind / byport / 23으로 변경하는 것입니다.
Jérôme B

18

파일 기능은 패키지 업데이트 후 중단 될 수 있으므로 이상적이지 않습니다.

이상적인 솔루션 IMHO는 상속 가능한 CAP_NET_BIND_SERVICE집합 으로 셸을 만드는 기능이어야합니다 .

이 작업을 수행하는 다소 복잡한 방법이 있습니다.

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

capsh이 유틸리티는 데비안 / 우분투 배포판의 libcap2-bin 패키지에 있습니다. 진행중인 작업은 다음과 같습니다.

  • sg유효 그룹 ID를 데몬 사용자의 유효 그룹 ID로 변경합니다. 이것은 capshGID를 변경하지 않고 그대로두고 필요하지 않기 때문에 필요 합니다.
  • 비트 'UID 변경시 기능 유지'를 설정합니다.
  • UID를 다음으로 변경 $DAEMONUSER
  • --keep=1상속 가능한 것을 제외하고 모든 캡을 삭제합니다 (이 시점에서 모든 캡은 여전히 ​​존재하므로 ).cap_net_bind_service
  • 명령을 실행합니다 ( '-'은 구분 기호입니다)

결과는 지정된 사용자 및 그룹과 cap_net_bind_service권한을 가진 프로세스입니다 .

예를 들어 ejabberd시작 스크립트 의 줄은 다음과 같습니다.

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

그리고 실제로 작동하지 않습니다. 사용자 ID 스위치에서 캡이 유지되지 않습니다. 루트로 실행하려는 경우 작동하지만 모든 기능이 삭제되었습니다.
Cyberax

시도하면 "바이너리 파일을 실행할 수 없습니다"라는 메시지가 나타납니다. 사실, 또는 옵션을 사용할 때 "이진 파일을 실행할 수 없습니다" 이외의 다른 capsh작업을 수행 할 수 없습니다 . 내가 무엇을 놓치고 있는지 궁금합니다. --==
Craig McQueen 1

@CraigMcQueen, 이후의 모든 항목 --이에 전달 /bin/bash되었으므로으로 시도해 볼 수 있습니다 -c 'your command'. 아아, 나는 @Cyberax와 같은 문제를 겪고있는 것 같습니다. 왜냐하면 시도 할 때 "권한이 거부되었습니다" bind.
Amir

파일 기능을 설정하지 않고 (또는 루트로 실행)이 작업을 수행하려면 이 Unix.SE answer에 설명 된대로 주변 기능 을 사용해야 합니다. 당신은 또한 사용할 수있는 대신에 (또는 어느 세트 , 및 ) : --gidsg--user--uid--gid--groupssudo 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

어떤 이유로 sysctl net.ipv4.ip_unprivileged_port_start를 필요한 값으로 낮추는 것에 대해서는 아무도 언급하지 않았습니다. 예 : 앱을 443 포트에 바인딩해야합니다.

sysctl net.ipv4.ip_unprivileged_port_start=443

권한이없는 사용자는 이제 다른 권한있는 포트 (444-1024)에 바인딩 할 수 있습니다. 그러나 다른 포트를 차단하여 iptables로이 문제를 쉽게 해결할 수 있습니다.

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

다른 방법과 비교 이 방법:

  • 응용 프로그램이 전혀 설정되지 않았기 때문에 (실제로 기능이 있기 때문에) CAP_NET_BIND_SERVICE / setuid를 설정하는 것보다 (IMO) 훨씬 안전합니다. 예를 들어, 기능이 활성화 된 응용 프로그램의 코어 덤프를 캡처하려면 sysctl fs.suid_dumpable을 변경해야합니다 (이로 인해 다른 잠재적 인 보안 문제가 발생할 수 있음). 또한 CAP / suid가 설정되면 / proc / PID 디렉토리가 루트에 의해 소유됩니다. 루트가 아닌 사용자는 실행중인 프로세스에 대한 완전한 정보 / 제어 권한이 없습니다. 예를 들어, 사용자는 / proc / PID / fd / (netstat -aptn | grep를 통해 어떤 연결이 응용 프로그램에 속하는지 판별 할 수 없습니다) PID).
  • 보안상의 단점이 있습니다. 어떤 이유로 앱 (또는 포트 443-1024를 사용하는 모든 앱)이 다운 된 경우 다른 앱이 포트를 사용할 수 있습니다. 그러나이 문제는 CAP / suid (예 : java / nodejs와 같은 인터프리터에서 설정 한 경우) 및 iptables-redirect에도 적용될 수 있습니다. systemd-socket 방법을 사용하여이 문제를 배제하십시오. authbind 메소드를 사용하여 특수 사용자 바인딩 만 허용하십시오.
  • 새 버전의 응용 프로그램을 배포 할 때마다 CAP / suid를 설정할 필요가 없습니다.
  • systemd-socket 방법과 같은 응용 프로그램 지원 / 수정이 필요하지 않습니다.
  • 커널 재 구축이 필요하지 않습니다 (실행중인 버전이이 sysctl 설정을 지원하는 경우)
  • authbind / privbind 메소드와 같은 LD_PRELOAD를 수행하지 않으면 성능, 보안, 동작에 영향을 줄 수 있습니다 (테스트되지 않았습니까?). 나머지 인증에서는 실제로 유연하고 안전한 방법입니다.
  • 주소 변환, 연결 상태 추적 등이 필요하지 않으므로 iptables REDIRECT / DNAT 방법을 과도하게 수행합니다. 이는 고부하 시스템에서만 볼 수 있습니다.

상황에 따라 sysctl, CAP, authbind 및 iptables-redirect 중에서 선택합니다. 그리고 이것은 우리에게 많은 옵션이 있다는 점에서 좋습니다.


2
큰 답변 주셔서 감사합니다! 이 기능은 2017 년 4 월 Linux 4.11에서 처음 등장한 것으로 보이 므로 2009 년에이 질문을 한 적이 없었습니다. 또한 빠른 테스트를 수행했으며 "ipv4"가 sysctl 이름 인 경우에도 IPV6에서도 작동하는 것 같습니다.
Jason Creighton 1

15

다른 두 가지 간단한 가능성 :

"낮은 포트에 바인딩하고 데몬을 제어하는 ​​데몬"에 대한 오래된 (유행 할 수없는) 솔루션이 있습니다. 이를 inetd (또는 xinetd)라고합니다. 단점은 다음과 같습니다.

  • 데몬은 stdin / stdout에서 대화해야합니다 (데몬을 제어하지 않으면-소스가없는 경우-아마도 일부 서비스에 inetd-compatibility 플래그가있을 수 있지만 showtopper 일 수 있습니다)
  • 모든 데몬마다 새로운 데몬 프로세스가 발생합니다.
  • 그것은 체인에 하나의 추가 링크입니다

장점 :

  • 이전 UNIX에서 사용 가능
  • sysadmin이 구성을 설정 한 후에는 개발에 착수해야합니다 (데몬을 다시 빌드 할 때 setcap 기능이 손실 될 수 있습니까? 그런 다음 관리자에게 다시 문의하십시오. " ")
  • 데몬은 그 네트워킹에 대해 걱정할 필요가 없으며 stdin / stdout에서 이야기하면됩니다.
  • 요청에 따라 데몬을 루트가 아닌 사용자로 실행하도록 구성 할 수 있습니다.

또 다른 대안 : 권한있는 포트에서 대상 데몬을 실행할 수있는 임의의 높은 번호의 포트 까지 해킹 된 프록시 (netcat 또는 더 강력한 것 ). (Netcat은 분명히 프로덕션 솔루션이 아니라 "단지 내 개발 상자"입니다.). 이 방법으로 네트워크 가능 서버 버전을 계속 사용할 수 있고, 부팅시 프록시를 시작하기 위해 루트 / 스도 만 필요하며, 복잡하고 잠재적으로 취약한 기능에 의존하지 않습니다.


1
좋은 제안. 어떤 이유로 나는 inetd를 사용하는 것을 생각조차하지 않았습니다. 문제의 서비스가 UDP 기반이기 때문에 TCP 서비스보다 약간 더 복잡합니다. setcap을 사용하는 솔루션이 있지만이 생각을해야합니다.
Jason Creighton

두 번째 옵션의 경우, inetd를 사용하여 프록시를 시작할 수도 있습니다 ... (하지만 IPTables는 오버 헤드가 낮을 것입니다 ...)
Gert van den Berg

14

내 "표준 해결 방법"은 socat을 사용자 공간 리디렉터로 사용합니다.

socat tcp6-listen:80,fork tcp6:8080

이것이 확장되지 않으며 포크는 비싸지 만 socat이 작동하는 방식입니다.


13

Linux는 "이 응용 프로그램은 루트로 실행"보다 더 세분화 된 권한을 지원 하는 기능 을 지원 합니다 . 이러한 기능 중 하나는 CAP_NET_BIND_SERVICE권한있는 포트 (<1024)에 바인딩하는 것입니다.

불행히도 응용 프로그램을 계속 제공하면서 루트가 아닌 응용 프로그램을 실행하기 위해 응용 프로그램을 악용하는 방법을 모르겠습니다 CAP_NET_BIND_SERVICE(아마도을 사용 setcap하지만 기존 솔루션이 있어야합니다).


스크립트 또는 개별 JVM / 모노 프로그램에서는 작동하지 않습니다.
Stefan Steiger

13

나는 이것이 오래된 질문이라는 것을 알고 있지만, 최근 (> = 4.3) 커널로 마침내 이것에 대한 좋은 대답이 있습니다.

빠른 대답은 git에서 최신 (출시되지 않은) libcap 버전의 복사본을 가져 와서 컴파일하는 것입니다. 결과 progs/capsh바이너리를 어딘가에 복사하십시오 ( /usr/local/bin좋은 선택). 그런 다음 루트로 프로그램을 시작하십시오.

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

우리는

  • 사용자를 전환 할 때 현재 기능 세트를 유지하려고합니다.
  • 사용자 및 그룹을 'your-service-user-name'으로 전환
  • cap_net_bind_service상속 및 주변 세트에 기능 추가
  • 분기 bash -c 'your-command'(이후 capsh의 인수로 bash를 자동으로 시작 하므로 --)

여기 후드 아래에 많은 일이 있습니다.

먼저 루트로 실행 중이므로 기본적으로 모든 기능을 사용할 수 있습니다. 여기에는 setuidsetgidsyscalls로 uid & gid를 전환하는 기능이 포함되어 있습니다. 그러나 일반적으로 프로그램 이이 작업을 수행하면 기능 세트가 손실됩니다. 이렇게하면 루트를 삭제하는 오래된 방법이 setuid여전히 작동합니다. 이 --keep=1플래그는 syscall capsh을 발행하도록 지시 하여 prctl(PR_SET_KEEPCAPS)사용자를 변경할 때 기능이 삭제되지 않도록합니다. 에 의한 실제 사용자 변경은 및 을 실행 capsh하는 --user플래그 와 함께 발생합니다 .setuidsetgid

우리가 해결해야 할 다음 문제는 exec자녀 를 쫓아가는 방식으로 기능을 설정하는 방법입니다 . 기능 시스템은 항상 '상속 된'기능 세트를 가졌으며, 이는 "execve (2)에 걸쳐 보존 된 기능 세트"[ capability (7) ]입니다. 이것이 문제를 해결하는 것처럼 들리지만 ( cap_net_bind_service기능을 상속받은 것으로 설정 했습니까?) 실제로 권한있는 프로세스에만 적용됩니다. 우리는 이미 사용자를 --user플래그로 변경했기 때문에 더 이상 권한이 없습니다 .

새로운 주변 기능 세트는이 문제를 해결합니다. "특권이없는 프로그램의 execve (2)에 걸쳐 유지되는 기능 세트입니다." exec가 서버 프로그램 cap_net_bind_service일 때 앰비언트 세트 를 넣으면 capsh, 우리 프로그램은이 기능을 상속하고 리스너를 낮은 포트에 바인딩 할 수 있습니다.

자세한 내용은 기능 매뉴얼 페이지를 참조하십시오. 에서 자세히 설명합니다. 실행 capsh을 통해하는 것은 strace매우 유익!


--addamb플래그는 libcap 2.26에서 도입되었습니다. 내 시스템에는 2.25가 있었고 소스에서 빌드해야했습니다.
ngreen

12

TLDR : "답변"에 대해 (이것이 보듯이)이 대답에서 >> TLDR << 부분으로 이동하십시오.

좋아, 나는 (이번에)이 질문에 대한 답을 알아 냈으며, 나의 대답은 또한 "최고라고 생각한 다른 대답 (여기 및 트위터 모두) 을 홍보하는 것에 대해 사과하는 방법입니다. ", 그러나 그것을 시도한 후, 나는 그것에 대해 착각했다는 것을 발견했다. 내 실수에서 배우십시오. 실제로 직접 시도하기 전에는 무언가를 홍보하지 마십시오!

다시, 나는 여기에 모든 답변을 검토했습니다. 나는 그중 일부 를 시도 했다 (그리고 나는 단순히 해결책을 좋아하지 않기 때문에 다른 것을 시도하지 않기로 선택했다). 나는 솔루션을 사용하는 것이라고 생각 systemd그와 Capabilities=CapabilitiesBindingSet=설정. 한동안 이것과 씨름 한 후, 나는 이것이 해결책이 아니라는 것을 알았습니다 .

기능은 루트 프로세스를 제한하기위한 것입니다!

OP가 현명하게 언급했듯이 항상 가능한 한 모든 데몬에 대해 피하는 것이 가장 좋습니다.

당신은 옵션과 관련된 기능 사용할 수 없습니다 User=Group=systemd기능이 있기 때문에, 유닛 파일을 항상 할 때 리셋 execev(또는 어떤 기능이)라고합니다. 즉, systemd포크를 펌핑하고 퍼마를 떨어 뜨리면 기능이 재설정됩니다. 이 문제를 해결할 방법이 없으며 커널의 모든 바인딩 논리는 기능이 아닌 uid = 0을 기본으로합니다. 즉, 기능이이 질문에 대한 정답 일 가능성은 거의 없습니다 (적어도 언제든지). 우연히도 setcap다른 사람들이 언급했듯이 해결책은 아닙니다. 그것은 나를 위해 작동하지 않았고 스크립트와 잘 작동하지 않으며 파일이 변경 될 때마다 스크립트가 재설정됩니다.

나는 빈약 한 방어에서 James의 iptables 제안 (OP도 언급)이 "두 번째 최선의 해결책"이라고 진술했습니다 (현재 삭제 한 의견에서 ). :-피

>> TLDR <<

해결책은 다음 과 같은 systemd즉시 iptables명령 과 결합 하는 것입니다 (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

여기서 우리는 다음을 달성합니다.

  • 데몬은 5333에서 수신 대기하지만 다음으로 인해 53에서 연결이 성공적으로 승인됩니다. iptables
  • 단위 파일 자체에 명령을 포함시켜 사람들의 두통을 줄일 수 있습니다. systemd데몬이 실행되지 않을 때 제거하도록 방화벽 규칙을 정리합니다.
  • 우리는 절대로 루트로 실행되지 않으며 systemd, 데몬이 손상되어 설정되어 있어도 권한 상승을 불가능하게합니다 (적어도 주장) uid=0.

iptables불행히도 여전히 추악하고 사용하기 어려운 유틸리티입니다. 예를 들어, 데몬이 수신 대기 eth0:0하지 않는 eth0경우 명령이 약간 다릅니다 .


2
고대 리눅스 배포판 eth0:0이 없다면 아무도 더 이상 이전 스타일의 별칭을 사용해서는 안됩니다 . 그들은 몇 년 동안 사용되지 않아 결국 사라질 것입니다.
Michael Hampton

1
OpenVZ를 의미한다고 생각합니다. (SolusVM은 제어판입니다.) 물론 OpenVZ는 많은 일을 잘못하고 네트워킹은 그중 하나 일뿐입니다.
Michael Hampton

1
아냐, 나는 SolusVM을 의미한다. / etc / network / interfaces에서 :# Generated by SolusVM
Greg Slepak

1
여전히 OpenVZ가 아닙니다 ... 최소한 사용하지 않거나 VPS도 아닙니다.
Greg Slepak

7
systemd의 기능 기능을 지적 해 주셔서 감사합니다. 그러나 systemd가 바이너리를 직접 시작할 때 (스크립트가 아닌) 복잡한 iptable이 필요하지 않습니다. AmbientCapabilities=CAP_NET_BIND_SERVICE와 함께 설정하면 완벽하게 작동했습니다 User=.
Ruud

11

systemd 는 특정 기능을 가진 데몬을 시작하는 옵션이있는 sysvinit 교체입니다. systemd.exec (5) 맨 페이지의 옵션 Capabilities =, CapabilityBoundingSet =


2
이전 에이 답변을 권장했지만 시도한 후에는 그렇지 않습니다. 아직도 사용하는 대안에 대한 내 대답 을 참조하십시오 systemd.
Greg Slepak

10

포트 리다이렉션이 우리에게 가장 합리적 이었지만 우리는 애플리케이션이 다시 라우팅해야하는 로컬 URL을 해결할 수있는 문제가 발생했습니다. ( 신디 를 의미 함 ).

로컬 컴퓨터의 URL에 액세스 할 때 리디렉션 될 수도 있습니다.

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

현대 리눅스는 지원합니다 /sbin/sysctl -w net.ipv4.ip_unprivileged_port_start=0.


8

systemd를 사용하면 사전 활성화 된 소켓을 허용하도록 서비스를 약간 수정하면됩니다.

나중에 systemd socket activate를 사용할 수 있습니다 .

기능, iptables 또는 기타 트릭이 필요하지 않습니다.

이것은 간단한 파이썬 http 서버의 예제에서 관련 시스템 파일의 내용입니다.

파일 httpd-true.service

[Unit]
Description=Httpd true 

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

PrivateTmp=yes

파일 httpd-true.socket

[Unit]
Description=HTTPD true

[Socket]
ListenStream=80

[Install]
WantedBy=default.target

7

시작시 :

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

그런 다음 전달할 포트에 바인딩 할 수 있습니다.


않는 --to-port존재 하는가? (복수) man iptables만 언급합니다 --to-ports.
Abdull

ive는 약간의 차이점을 알아 차리고 뛰어 다니고 있습니다
James

이을 결합하는 방법 은 이 답변 을 참조하십시오 systemd.
Greg Slepak


3

'djb way'도 있습니다. 이 방법을 사용하여 tcpserver의 모든 포트에서 루트로 실행중인 프로세스를 시작할 수 있으며 프로세스가 시작된 직후 지정한 사용자에게 프로세스 제어를 전달합니다.

#!/bin/sh

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

자세한 내용은 다음을 참조하십시오 : http://thedjbway.b0llix.net/daemontools/uidgid.html


1

OP는 개발 / 테스트 일 뿐이므로 매끄러운 솔루션보다 적은 도움이 될 수 있습니다.

setcap을 스크립트의 통역사에서 사용하여 스크립트에 기능을 부여 할 수 있습니다. 글로벌 인터프리터 바이너리의 setcaps가 허용되지 않으면 바이너리의 로컬 사본 (모든 사용자가 할 수 있음)을 작성하고이 사본에서 setcap의 루트를 가져 오십시오. Python2는 (적어도) 스크립트 개발 트리에서 인터프리터의 로컬 사본과 올바르게 작동합니다. 루트 사용자가 사용자가 액세스 할 수있는 기능을 제어 할 수 있도록 suid가 필요하지 않습니다.

인터프리터에 대한 시스템 전체 업데이트를 추적해야하는 경우 다음과 같은 쉘 스크립트를 사용하여 스크립트를 실행하십시오.

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

iptables PREROUTING REDIRECT 방법을 시도했습니다. 이전 커널에서는 이러한 유형의 규칙 이 IPv6에 대해 지원되지 않은 것 같습니다 . 그러나 분명히 ip6tables v1.4.18 및 Linux 커널 v3.8에서 지원됩니다.

또한 PREROUTING REDIRECT가 시스템 내에서 시작된 연결에서 작동하지 않는 것을 발견했습니다. 로컬 시스템에서 연결에 대해 작업하려면 OUTPUT 규칙도 추가하십시오. iptables 포트 리디렉션이 localhost에서 작동하지 않음을 참조하십시오 . 예를 들면 다음과 같습니다.

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

또한 PREROUTING REDIRECT가 전달 된 패킷에도 영향을 미친다 는 것을 알았습니다 . 즉, 기기가 인터페이스간에 패킷을 전달하는 경우 (예 : 이더넷 네트워크에 연결된 Wi-Fi 액세스 포인트 역할을하는 경우) iptables 규칙은 연결된 클라이언트의 인터넷 연결에 대한 연결을 포착하여 리디렉션합니다. 기계. 그것은 내가 원하는 것이 아닙니다. 기계 자체로 향한 연결 만 리디렉션하고 싶었습니다. 추가하여 상자에 지정된 패킷에만 영향을 줄 수 있음을 알았습니다 -m addrtype --dst-type LOCAL. 예를 들면 다음과 같습니다.

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

또 다른 가능성은 TCP 포트 전달을 사용하는 것입니다. 예 socat:

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

그러나이 방법의 한 가지 단점은 포트 8080에서 수신 대기하는 응용 프로그램은 들어오는 연결의 소스 주소를 알지 못한다는 것입니다 (예 : 로깅 또는 기타 식별 목적).


0

2015 년 9 월 답변 :

ip6tables는 이제 IPV6 NAT를 지원합니다 : http://www.netfilter.org/projects/iptables/files/changes-iptables-1.4.17.txt

커널 3.7 이상이 필요합니다

증명:

[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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.