crontab, rsync, ssh 환상조합?

rsync 와 ssh 의 마지막 꽃삽질 분투기.
이 둘에 관한 얘기는 벌써 많이 썼다.

위 글들에는 ‘가정’이 있다.

  • 클라이언트와 서버는 ssh 로 통신하며, ssh 는 공개키암호화를 사용하여 인증한다.
  • ssh 키는 Passphrase 를 사용하여 암호화 되어있다.

두번째 항목만 없다면 이렇게 복잡한 방식을 취하지 않아도 된다. 즉, ssh-keygen 시에 Pass Phrase 를 할당하지 않으면 된다. 이러면 cron 작업에 사용해도 아무 문제가 없다.
(그러나 보안엔 문제가 될 수 있다.)

먼저, passphrase 가 있을 때 생기는 문제에 대해 알아본다.

인증 문제 : 결국 rsync 불가.

crontab -e 를 통해 rsync 작업을 등록한다. 명령어는 형태가 된다.

rsync -avz -e ssh Host:/Directory /home/xxx/yyy

저 작업을 그냥 터미널에서 실행하면 아무런 오류가 없이 실행이 된다.
기본 키(~/.ssh/ 에 저장된)에 Passphrase 가 지정되어 있다면, 그리고 ssh-add 를 통해 등록되어있다면, 접속 시에 Passphrase 를 묻지 않는다. 자동 접속이 이뤄지고, 파일 복사가 아무런 걸림돌없이 진행된다.

그런데, 이 명령을 그대로 cron 작업에 올려주면 문제가 발생한다. 간단하게는 Permission denied 고, 좀 더 자세하게 보면 이런 오류들이 보인다.

debug2: no passphrase given, try next key
debug2: we did not send a packet, disable method
debug1: No more authentication methods to try.
id@remote: Permission denied (publickey).
rsync: connection unexpectedly closed (0 bytes received so far) [Receiver]

cron 으로 rsync/ssh 를 실행하면, 비록 ssh 접속에 문제가 없는 사용자 계정으로 설정했다하더라도, cron 이 실행한 rsync 에서 인증에 문제가 발생한다. 이를 위해 검색으로 찾은 몇가지 편법을 모두 실행해봤지만, 대부분 실패했다.

그렇다면 자동화는 할 수 없는걸까?

그리하여, 위 ‘편법’ 중 하나를 택하여 여기에 정리한다.
원본은 다음 글.


이 방법을 위해선, Passphrase 를 사용하지 않는 새 키쌍을 생성해야 한다. 하지만, 그렇다면 보안에 문제가 생길 가능성이 있다.

이를 위해 rsync 팀은 이 접속을 rsync 에만 국한 시키는 방식, 즉, 이렇게 만든 키로는 ssh 접속을 할 수 없게끔 하는 법을 이미 예전에 고안했다고 하며, 이 글의 저자 Guy Rutenberg 는 그 사용법을 자세히 기록해놓았다. (고맙습니다!)

서버 : rrync 얻기

rsync 제작진은, 이런 문제를 해결하기 위해 rrsync 라는 스크립트를 제공한다. 이건 기본 설치되어 있지는 않고, 압축되어 제공된다. 데비안/우분투의 경우, /usr/share/doc/rsync/scripts/rrsync.gz 에서 찾을 수 있다. 이것을 서버의 홈디렉토리 쪽에 압축해제한다. (어디든 관계없지만, ~/bin 또는 ~/.local/bin 등이 좋겠다.

** 헷갈리지 말아야 한다. 클라이언트가 아니고 서버에 푼다. 물론, rsync 실행 자체는 당연히 서버가 아닌 클라이언트에서 하는게 맞다.

userremote@remote:~$ gunzip /usr/share/doc/rsync/scripts/rrsync.gz -c > ~/bin/rrsync
userremote@remote:~$ chmod +x ~/bin/rrsync

클라이언트 : rsync 를 위한 새로운 ssh 키 쌍 생성

이제, 로컬 PC(Client)에서 서버에 접속 시에 사용할 rsync 전용 키 쌍을 생성한다. 단, 문제가 되는 Passphrase 는 없이 만든다. Passphrase 를 물어올 때 그냥 엔터만 입력하면 된다.

userlocal@local:~$ ssh-keygen -f ~/.ssh/id_for_rsync -C "rsync 전용"

파일명(id_for_rsync)이나 -C 뒤의 설명(rsync 전용)은 원하는대로 아무렇게나 주면 된다.
위와 같이 만들었다면, 다음과 같이 개인키/공개키 쌍이 생성된다.

userlocal@local:~$ ll ~/.ssh
-rw------- 1 userlocal userlocal  399 2020-05-17 14:15 id_for_rsync
-rw-r--r-- 1 userlocal userlocal   94 2020-05-17 14:15 id_for_rsync.pub

이 키(공개키)를 서버의 계정(위의 예대로라면, /home/userremote/.ssh/authorized_keys 로 복사한다. 원 글 저자인 Guy Rutenberg 는 scp 를 사용해서 일단 키 자체를 서버로 옮겼지만, 여기선 전용 프로그램인 ssh-copy-id 를 사용하기로 한다.

userlocal@local:~$ ssh-copy-id -i id_for_rsync.pub userremote@remote

** 이 때 ssh-copy-id 가 사용할 ssh 인증키는, 당연히 지금 만든 키가 아니고 이전에 사용하던 키다. 그 키로써 인증이 이뤄진다.

서버 : authorized_keys 편집

서버로 다시 접속하여, authorized_keys 에 공개키가 추가되었는지 확인한다.
제대로 됐다면, 다음과 같은 내용이 들어가 있어야 한다.

ssh-ed25519 AAAAC3...어쩌고 저쩌고.. rsync 전용

만약 이 키를 일반 접속에 사용할 예정이라면 그대로 놔두면 되지만, rsync 에만 한정시킬 계획이므로, 이 앞에 다음 명령을 추가한다.

command="$HOME/bin/rrsync -ro /원하는 디렉토리",no-agent-forwarding,no-port-forwarding,no-pty,no-user-rc,no-X11-forwarding
  • $HOME 대신에 직접 경로를 넣어줘도 된다.
  • 원하는 디렉토리에는, rsync 로 접속할 디렉토리를 넣어준다. 만약 여기를 비워두면, 나중에 클라이언트에서 rsync 명령 뒤에 직접 디렉토리를 넣어도 될 듯은 한데, 시험해보지 않아서 확신은 없다.

최종 결과는 이런 형식이 된다.

command="$HOME/bin/rrsync -ro /원하는 디렉토리",no-agent-forwarding,no-port-forwarding,no-pty,no-user-rc,no-X11-forwarding ssh-ed25519 AAAAC3...어쩌고 저쩌고.. rsync 전용

클라이언트 : rsync 명령

설정은 모두 끝났다. 이제 이런 식으로 rsync 를 사용하면 되고, Passphrase 가 없으므로 cron 에서도 사용할 수 있다.

rsync -avzu -e "/usr/bin/ssh -p 1234 -i /home/userlocal/.ssh/id_for_rsync" userremote@remote: "로컬 디렉토리"
  • userremote@remote: 뒤엔 디렉토리를 적지 않는다. 자동으로, authorized_keys 에 입력했던 ‘/원하는 디렉토리’ 가 소스 디렉토리로 인식된다.
  • “로컬 디렉토리” 는 공백이 들어가 있거나 변수일경우, 반드씨 겹따옴표로 감싼다. 홑따옴표를 사용하면 오류가 발생한다.

끝! 이제 pubkey 오류는 안녕!


한가지 실험. 정말 이렇게 만든 키로는 로그인이 되지 않을까?
실험을 해봤다.

ssh -vvv -i ~/.ssh/id_rsync_only userremote@remote

그런데…
접속이 된다.

워낙에 ssh 는, 키가 여러 개일 경우, 지정한 키로 접속이 안되면 다른 키로 또 다시 시도한다고 한다. 위처럼 키를 지정해줬음에도 불구하고, 저 키로 안되니 다른 키로 접속을 시도하는 상황을, -vvv 를 통해 확인할 수 있었다.
내 경우, 접속에 사용하는 다른 키가 ssh-add 로 등록이 되어 있었기 때문에, 그 키를 사용해서 자동 접속이 되었다.

물론, -vvv 로 인한 오류 출력을 보고 접속이 안됨을 대충 짐작할 순 있었지만 정말 안되는지 확인해보고 싶었다.

검색에 검색을 거듭한 결과, 이렇게 하면 진짜로 ‘접속 불가’가 됨을 눈으로 볼 수 있었다.

ssh -p 1234 -i /home/userlocal/.ssh/id_for_rsync -F /dev/null -o IdentitiesOnly=yes  userremote@remote

정말로 끝!

안녕하세요. 글 남겨주셔서 고맙습니다.