'sudo -E'가 내보내기 -f로 내보낸 함수 환경 변수를 보존하지 않는 이유는 무엇입니까?

'sudo -E'가 내보내기 -f로 내보낸 함수 환경 변수를 보존하지 않는 이유는 무엇입니까?

script.sh에서:

#!/bin/bash

func(){
  echo "I am here a func."
}

export -f func
export variable="I am here a variable"

sudo -E bash -c "func ; echo $variable"

출력은 다음과 같습니다.

bash: func: command not found
I am here a variable
  1. 왜?
  2. 초기 쉘에서 sudo를 통해 [script.sh에 선언된] 함수를 호출할 수 있는 솔루션이 있습니까?

사실 저는 다음과 같은 방법을 알고 있습니다.

#!/bin/bash

func(){
  echo "I am here a func."
}

FUNC=$(declare -f func)
export variable="I am here a variable"

sudo -E bash -c "$FUNC; func ; echo $variable"

그런데 이렇게 모든 기능을 등록해야 하니 너무 무겁다는 생각이 듭니다.

답변1

export -f func내 환경에서 다음을 확인한 후:

$ env | grep func
BASH_FUNC_func%%=() …

Bash는 와 같은 이름 BASH_FUNC_foo%%과 로 시작하는 값을 가진 변수로 함수를 내보냅니다 ().쉘 쇼크이름에는 접두어( )가 없습니다 foo=() …. Apple 접두사는 __BASH_FUNC<. 어쨌든 값은 ().

sudo이러한 이름과 환경 전체에 매우 주의를 기울이십시오. sudoBash 스크립트를 실행하기 위해 스크립트를 사용할 수 있다고 상상해 보세요 . 스크립트가 합법적으로 사용된다고 상상해 보세요 cat. 귀하라는 이름의 셸 함수를 내보내면 cat스크립트를 효과적으로 변경하고 임의의 코드를 실행할 수 있습니다.

당신은 당신 자신의 것을 사용할 수 있습니다 $PATH(그리고이것을 할 수 있는 방법이 있나요?; 그러나 관리자는 이를 방지하는 방법도 있습니다. 또는 사용할 수 있는 경우 $LD_PRELOAD(그리 쉽지는 않습니다. 참조)man 5 sudoers) 을 언급했다 LD_.

내보낸 쉘 함수로 돌아가 보겠습니다. 먼저 sudo -V루트( sudo sudo -V일반 사용자) 로 실행 하면 출력의 일부는 다음과 같습니다.

Environment variables to remove:
        *=()*

아니면:

Environment variables to remove:
        __BASH_FUNC<*
        BASH_FUNC_*

버전에 따라 다르다고 생각합니다 sudo.

다음 줄을 사용하여 목록에서 이러한 패턴을 제거할 수 있습니다 sudoers(그러나 이렇게 하지 말고 먼저 전체 답변을 읽으십시오).

Defaults       env_delete -= "*=()* BASH_FUNC_* __BASH_FUNC<*"

"유지" 목록에 추가할 수도 있습니다(추가하지 않아도 됨).

Defaults        env_keep += "*=()* BASH_FUNC_* __BASH_FUNC<*"

그러나 내보낸 함수가 반드시 와 함께 작동하는 것은 아닙니다 sudo. 그 이유는 패턴이 *=()*하드 코딩될 수 있고 sudo거부 변수의 일부 버전 값이 로 시작하기 때문 입니다 (). 이 행동은 진화되었습니다. 사용 가능한 솔루션은 sudo사용 중인 버전에 따라 다릅니다.

~에서man 5 sudoers:

명령 환경

환경 변수는 프로그램 동작에 영향을 미칠 수 있으므로 sudoers실행 중인 명령에 의해 상속되는 사용자 환경의 변수를 제한하는 방법이 제공됩니다. sudoers에는 환경 변수를 처리하는 두 가지 방법이 있습니다.

기본적으로 이 env_reset옵션은 활성화되어 있습니다. [...] HOME, MAIL, SHELL환경 변수는 대상 사용자를 기준으로 초기화되고 LOGNAME변수는 호출 사용자를 기준으로 설정됩니다. 또는 옵션이 허용되면 , 및 같은 다른 변수는 호출하는 사용자 환경에서 보존됩니다. 이것은 실제로 환경 변수의 화이트리스트입니다. [...] 값이 or 로 시작하는 환경 변수는 이름과 값 부분이 모두 or 와 일치하지 않는 한 제거됩니다. 이는 bash 셸에서 함수로 해석될 수 있기 때문입니다. 버전 1.8.11 이전에는 이러한 변수가 항상 제거되었습니다.USERSUDO_*DISPLAYPATHTERMenv_checkenv_keep()env_keepenv_check

그러나 env_reset이 옵션이 비활성화된 경우 env_check및 옵션 env_delete에 의해 명시적으로 거부되지 않은 모든 변수는 호출 프로세스에 의해 상속됩니다. 이 경우 env_check동작은 env_delete블랙리스트와 같습니다. 버전 1.8.21 이전에는 ()값이 로 시작하는 환경 변수가 항상 제거되었습니다. 버전 1.8.21부터 패턴을 사용하여 env_deletebash 쉘 기능을 일치시킵니다. 잠재적으로 위험한 환경 변수를 모두 블랙리스트에 올리는 것은 불가능하므로 env_reset기본 동작이 권장됩니다.

env_check, 또는 로 env_delete지정된 환경 변수에는 0개 이상의 문자와 일치하는 env_keep하나 이상의 문자가 포함될 수 있습니다 . *다른 와일드카드 문자는 지원되지 않습니다.

기본적으로 환경 변수는 이름으로 일치됩니다. 단, 패턴에 등호( =)가 포함된 경우에는 변수명과 값이 모두 일치해야 합니다. 예를 들어 bash 쉘 함수는 다음과 같이 일치될 수 있습니다.

env_keep += "BASH_FUNC_my_func%%=()*"

=()*접미사가 없으면 bash 쉘 기능이 기본적으로 유지되지 않으므로 이는 일치하지 않습니다.

보시다시피 env_reset이것은 중요합니다. 아마도 활성화되어 있을 것입니다. -Eoption()의 요점 은 sudo -E요청 시 비활성화하는 것입니다.env_reset


내 Kubuntu 18.04.3 LTS에서 내 sudo버전은 1.8.21입니다. 기본적으로 *=()*삭제할 변수 목록에 배치됩니다 . 다음 줄은 sudoers이를 중지시킵니다.

Defaults       env_delete -= "*=()*"

그러면 sudo -E내보낸 쉘 기능은 모두 보존됩니다.이것이 나의 주요 대답입니다.


내 데비안 9 sudo버전은 1.8.19입니다. 기본적으로 제거할 변수 목록에 BASH_FUNC_*및 가 배치됩니다 . __BASH_FUNC<*이러한 항목을 제거해도 아무런 효과가 없습니다 (). 이 동작이 하드 코딩된 것처럼 보이는 값이 무엇이든 이 도구는 거의 항상 변수를 거부하기 때문입니다.

이 버전의 매뉴얼은 몇 가지 단서를 제공합니다. 해결책을 찾았습니다.

Defaults       env_keep += "BASH_FUNC_func%%=()*"

그 다음에sudo 아니요 -Efunc내보내면 이 특정 셸 기능( )이 유지됩니다. 단점(어떤 경우에는 장점일 수도 있음):

  • 이는 적용되지 않으므로 sudo -E필요에 따라 나머지 환경을 간단히 유지할 수는 없습니다.
  • 와일드카드 없이 변수의 전체 이름을 명시적으로 지정해야 합니다. BASH_FUNC_*%%=()*그렇지 않으면 BASH_FUNC_*작동 *=()*하지 않습니다.

이 방법은 1.8.21에서도 작동합니다.


매뉴얼에 따르면 1.8.11 버전 이전에는 로 시작하는 변수가 ()항상 제거되었습니다. 당신이 sudo그 나이라면 질문에 사용한 트릭(또는 유사한 트릭) 외에는 해결책이 없습니다.


노트:

  • sudoers다른 파일처럼 편집 하지 마십시오 visudo. 만일의 경우를 대비한 기타 예방 조치:
    • 별도의 "대체" 셸을 루트( )로 시작합니다 sudo -i. sudoers메인 쉘에서 어리석은 짓을 했다면, 이 파일에 의존하지 않고도 루트로 실행할 수 있습니다 .
    • SSH를 통해 연결하는 경우 상승된 셸을 생성하기 전에 또는 를 사용하세요 tmux. screen어리석은 일을 하여 sudoers(우연히) 연결이 실패하면 관리자 권한 쉘은 일반 사용자가 종료하는 대신 다시 연결될 때까지 기다립니다.
    • cp -a특히 실험 중인 경우 먼저 파일의 백업 복사본을 만듭니다( ).
    • 작업이 완료되었다고 생각되면 "대체" 상승된 셸을 종료하기 전에 루트 액세스 권한을 다시 얻을 수 있는지 확인하세요.
  • sudo당신이 유일한 사용자이고 sudoer가 임의의 코드를 실행하도록 허용하는 트릭에 대한 방어를 강화하는 데 별 의미가 없더라도(당신은 sudo어떤 트릭 없이도 임의의 코드를 자유롭게 실행할 수 있고 그것을 원하기 때문에) 너무 많은 것을 허용하기 전에 두 번 생각하십시오. . 쉘 기능을 그대로 유지 하면 sudo지금 당장은 아니지만 미래에는 쉽게 공격을 받을 수 있습니다.

관련 정보