ExecuteStreamCommand 로 원격 서버의 스크립트를 실행하려다 다음 오류가 이어졌다.
returned exitcode 255
Warning: Identity file ... id_rsa user@host not accessible: No such file or directory
ssh: Could not resolve hostname ...: hostname contains invalid characters
Host key verification failed
ExecuteStreamCommand 는 셸을 거치지 않고 프로세스를 직접 실행한다. 따라서 셸 문법이 통하지 않는다. 따옴표는 문자열을 묶는 역할을 하지 못하고 인자 글자 그대로 전달되며, 세미콜론으로 명령을 이어 붙일 수도 없다.
Command Arguments 에 다음처럼 적으면 전체가 하나의 덩어리로 해석되거나 구분자에 따라 엉뚱하게 잘린다.
-i /opt/id_rsa user@host;"bash gen.sh"
-i 와 경로가 한 인자로 붙어 "-i /opt/id_rsa user@host 라는 이름의 파일" 을 찾게 되고, 따옴표가 호스트 이름에 섞여 들어가 호스트 이름에 허용되지 않는 문자가 있다는 오류가 난다. 종료 코드 255 는 ssh 자신이 실패했음을 뜻한다.
인자를 구분자 하나 기준으로 정확히 하나씩 나눠 적는다.
| 속성 | 값 |
|---|---|
| Command Path | /usr/bin/ssh |
| Argument Delimiter | ; |
| Command Arguments | -i;/opt/id_rsa;-o;StrictHostKeyChecking=accept-new;-o;ConnectTimeout=10;user@target.example.com;bash /opt/scripts/gen.sh |
| Ignore STDIN | 입력 FlowFile 내용을 넘기지 않으면 true |
| Output Destination Attribute | 표준 출력을 속성에 담으려면 속성 이름. 비우면 FlowFile 내용이 된다 |
옵션과 값은 각각 별도 인자다. -o;StrictHostKeyChecking=no 처럼 -o 와 값을 나눠 적는다. 따옴표는 쓰지 않는다.
원격에서 실행할 명령이 여러 개이거나 파이프·리다이렉션이 필요하면 원격 쪽에 스크립트를 두고 그 스크립트만 호출한다. 그래도 부족하면 /bin/bash 를 Command Path 로 두고 -c 와 명령 문자열을 인자로 넘긴다. 이 경우 명령 문자열 전체가 하나의 인자여야 한다.
개인키는 실행 계정만 읽을 수 있어야 한다. 권한이 느슨하면 ssh 가 키를 무시한다.
chown nifi:nifi /opt/nifi/conf/id_rsa
chmod 600 /opt/nifi/conf/id_rsa
컨테이너에서는 Secret 으로 마운트한다. Kubernetes Secret 볼륨은 기본 권한이 0644 이므로 defaultMode: 0600 을 지정하고, 마운트 지점 소유자가 실행 uid 와 맞는지 확인한다.
키 경로를 매번 지정하는 대신 ~/.ssh/config 에 적어 둘 수도 있다.
Host target
HostName target.example.com
User svcuser
IdentityFile /opt/nifi/conf/id_rsa
StrictHostKeyChecking accept-new
UserKnownHostsFile /opt/nifi/conf/known_hosts
다만 컨테이너에서는 홈 디렉터리가 휘발되는 경우가 많으므로, 설정 파일 위치를 볼륨으로 고정하거나 인자로 직접 넘기는 편이 예측 가능하다.
Pod 을 재시작하면 ~/.ssh/known_hosts 가 사라져 호스트 키 검증에서 막히는 문제가 생긴다. 여기서 두 옵션을 혼동하기 쉽다.
StrictHostKeyChecking=no 는 알려지지 않은 호스트를 자동으로 수락 한다. 다만 수락한 키를 known_hosts 에 기록한다. 그리고 이미 기록된 키와 달라지면 여전히 접속을 거부한다.
UserKnownHostsFile=/dev/null 은 기록 자체를 버린다. 매번 처음 보는 호스트로 취급되며 파일도 남지 않는다.
즉 StrictHostKeyChecking=no 만으로도 접속은 되지만 파일에 계속 쌓이고, 대상 서버를 재구축해 호스트 키가 바뀌면 Host key verification failed 로 다시 막힌다. 그래서 일회성 컨테이너에서는 두 옵션을 함께 쓰는 관행이 있다.
운영 관점에서는 셋 중 하나를 고른다.
첫째, 호스트 키를 미리 담은 known_hosts 를 ConfigMap 으로 마운트하고 -o;UserKnownHostsFile=/opt/nifi/conf/known_hosts 로 지정한다. 검증을 유지하면서 재시작에도 영향받지 않는 가장 바른 방법이다.
ssh-keyscan -H target.example.com > known_hosts
둘째, StrictHostKeyChecking=accept-new 를 쓴다. 처음 보는 호스트는 받아들이되 바뀐 키는 거부하므로, no 보다 안전하다.
셋째, 검증을 포기한다. 이 경우 중간자 공격을 막지 못하므로 내부망 임시 용도로만 쓴다.