2016-12-20 12 views
1

golang을 통해 사용자 정의 웹 서버를 만들고 싶습니다. 포트 80에 바인드하려면 루트가 필요합니다. 그러나 가능한 한 빨리 루트를 삭제하려고합니다. syscall.SetUid()ticket #1435에 따라 "지원되지 않음"을 반환합니다.골란 드롭 특권 (v1.7)

필자는 포트 80을 iptables를 통해 다른 경로로 재 라우팅 할 수 있지만 루트가 아닌 프로세스를 열어 내 웹 서버로 사용할 수 있습니다.

내 응용 프로그램에 대한 권한을 삭제하거나 대체 방법을 올바르게 해결하려면 어떻게합니까?

+3

선호하는 방법은 프로그램이 완전한 루트 기능을 사용하지 않고 올바른 포트에 바인딩하는 것을 허용하는 Linux 기능을 사용하는 것입니다. – JimB

+1

당신이 실제로 링크 할 수있는 문제는 가능한 여러 가지 해결 방법/솔루션 IIRC가 있습니다. – Carpetsmoker

+0

Setuid가 golang에서 예상대로 작동하지 않았습니다. 이러한 해결 방법은 행동의 일관성을 높이는 것이 었습니다. 그러나 결국 그들은 성공을 보장하지 못했습니다. 즉, setuid를 호출 한 후에도 루트가 될 수 있습니다. 그렇기 때문에이 기능을 사용하지 않도록 결정했습니다. linux-capabilities에 관해서 : 당신은 여기서 정확히 무엇을 제안하겠습니까? – user2089648

답변

1

저는 @JimB가 제안한 것을 할 것입니다. 한편

는 리눅스의 다른 트릭있다 : 당신이 생성 os/exec.Cmd 인스턴스의 SysProcAttr.Credential 분야에서 대체 자격 증명을 사용하도록 말하는 동안 /proc/self/exe을 실행하는 os/exec.Command()를 사용할 수 있습니다.

go doc os/exec.Cmd, go doc syscall.SysProcAttrgo doc syscall.Credential을 참조하십시오.

프로그램을 다시 실행하려면 생성 된 프로그램의 부모 프로그램에 표준 I/O 스트림이 연결되어 있고 필요한 모든 열린 파일도 상속되었는지 확인해야합니다. 언급 할 가치가


또 다른 alternatve 전혀 포트 (80)에 결합하려고 시도하고 거기 매달려 적절한 웹 서버가 다음 역 프록시 호스트 이름 기반 가상 호스트 또는 특정 URL 경로 접두어 중 하나를 (하지 않는 것입니다 또는 접두어)를 TCP 또는 Unix 소켓에서 수신하는 Go 프로세스에 추가하십시오. 아파치 (2.4 이상)와 Nginx 모두 쉽게 할 수 있습니다.

0

최근이 문제를 해결하기 위해 Go에는 필요한 모든 조각이 있습니다. 이 샘플에서는 한 걸음 더 나아가 SSL을 구현했습니다. 기본적으로 포트를 열어 UID를 감지하고, 0이면 원하는 사용자를 찾고 UID를 얻은 다음 glibc 호출을 사용하여 프로세스의 UID 및 GID를 설정합니다. 필자는 포트를 바인딩 한 직후에 setuid 코드를 호출하는 것이 가장 좋습니다. 권한을 삭제할 때 가장 큰 차이점은 http.ListenAndServe (TLS)를 사용할 수 없다는 것입니다. 헬퍼 함수 - 수동으로 net.Listener를 별도로 설정 한 다음 포트가 바인딩 된 후 http.Serve를 호출하기 전에 setuid를 호출해야합니다.

이 방법은 더 이상 고려하지 않고 높은 포트의 "개발"모드에서 UID! = 0처럼 실행할 수 있기 때문에 잘 작동합니다. 이것은 단지 스텁 일 뿐이라는 것을 기억하십시오. 설정 파일에서 주소, 포트, 사용자, 그룹 및 TLS 파일 이름을 설정하는 것이 좋습니다.

package main 

import (
    "crypto/tls" 
    "log" 
    "net/http" 
    "os/user" 
    "strconv" 
    "syscall" 
) 

import (
    //#include <unistd.h> 
    //#include <errno.h> 
    "C" 
) 

func main() { 
    cert, err := tls.LoadX509KeyPair("cert.pem", "key.pem") 
    if err != nil { 
     log.Fatalln("Can't load certificates!", err) 
    } 
    var tlsconf tls.Config 
    tlsconf.Certificates = make([]tls.Certificate, 1) 
    tlsconf.Certificates[0] = cert 
    listener, err := tls.Listen("tcp4", "127.0.0.1:445", &tlsconf) 
    if err != nil { 
     log.Fatalln("Error opening port:", err) 
    } 
    if syscall.Getuid() == 0 { 
     log.Println("Running as root, downgrading to user www-data") 
     user, err := user.Lookup("www-data") 
     if err != nil { 
      log.Fatalln("User not found or other error:", err) 
     } 
     // TODO: Write error handling for int from string parsing 
     uid, _ := strconv.ParseInt(user.Uid, 10, 32) 
     gid, _ := strconv.ParseInt(user.Gid, 10, 32) 
     cerr, errno := C.setgid(C.__gid_t(gid)) 
     if cerr != 0 { 
      log.Fatalln("Unable to set GID due to error:", errno) 
     } 
     cerr, errno = C.setuid(C.__uid_t(uid)) 
     if cerr != 0 { 
      log.Fatalln("Unable to set UID due to error:", errno) 
     } 
    } 
    http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) { 
     w.Write([]byte("Hello, world!")) 
    }) 
    err = http.Serve(listener, nil) 
    log.Fatalln(err) 
}