Computer >> 컴퓨터 >  >> 프로그래밍 >> Ruby

프로덕션 환경에서 RubyGems.org를 쓰면 안 되는 이유와 자체 젬 호스팅 가이드

결론부터 말하자면, 젬(gem)은 훌륭하고 RubyGems.org 역시 훌륭한 서비스입니다.

하지만 최근 애플리케이션에 새로운 젬을 추가할 때마다 묘하게 불안한 기분이 들었습니다. 생각할수록 우리가 젬을 사용하는 방식은 단순히 결함이 있는 수준이 아니라, 언제 터져도 이상하지 않을 재앙처럼 느껴졌습니다.

소셜 엔지니어링의 실체

며칠 전 RubyNation 컨퍼런스에서 Ben Smith가 "Hacking With Gems"라는 인상적인 발표를 했습니다.

젬을 개발하는 이야기가 아니라, 젬 자체를 해킹 수단, 즉 공격 벡터로 활용하는 방법에 대한 이야기였습니다.

그는 흥미로운 실험을 선보였습니다.

컨퍼런스에 참석했다면 GitHub 스티커와 각종 굿즈 옆에 깔끔하게 인쇄된 카드 더미를 보셨을 겁니다.

카드에는 딱 한 문장이 적혀 있었습니다: gem install rubynation. 그리고 참석자의 약 10%가 실제로 이 명령어를 실행했습니다.

프로덕션 환경에서 RubyGems.org를 쓰면 안 되는 이유와 자체 젬 호스팅 가이드

그 젬이 하지 않은 것들

이 젬은 몇 가지 유용한 동작을 수행했지만, 정작 하지 않은 것들이 더 흥미롭습니다.

이 젬은 다음과 같은 작업을 하지 않았습니다:

  • gemcutter dotfile에서 RubyGems.org 자격 증명 훔치기

  • 평문(plaintext) 비밀번호를 가로채 Rails의 /public 디렉터리에 저장하기

  • 시스템에 은밀한 SSH 계정 추가하기

하지만 충분히 할 수 있었습니다. 믿기지 않는다면 Ben에게 직접 물어보세요.

이 사실이 주는 경고

다시 한번 강조하겠습니다. 제대로 새겨들으시길 바랍니다.

약 25명의 '컴퓨터 프로그래머'가 손쉽게 조작되어 개발 머신에서 임의의 코드를 실행했습니다. 심지어 일부는 root 권한으로 실행했습니다.

이 분들을 나무라려는 것은 아닙니다. 이것이야말로 루비 커뮤니티의 평범한 모습이기 때문입니다.

왜 이런 일이 일어났을까?

사람들은 젬을 신뢰합니다.

  • 대개 잘 작동하기 때문입니다.

  • Rails조차 하나의 젬이기 때문입니다.

  • Aaron Patterson, Steve Klabnik, Yehuda Katz 같은 유명 개발자들이 유지보수하기 때문입니다.

하지만 모르는 사람을 맹목적으로 신뢰하기 시작하면 문제가 됩니다.

gem install rails를 입력할 때와 똑같은 마음으로 아무 망설임 없이 gem install rubynation을 입력하는 순간, 문제는 시작됩니다.

솔직해집시다. 이런 경험 한 번쯤 없는 개발자가 있을까요? 우리는 게으르고, 젬은 너무나 편리합니다.

RubyGems.org의 잘못은 아니다

다만 RubyGems.org는 잘못된 행위, 즉 신뢰할 수 없는 코드를 서버에서 실행하고, 서버에서 돌아가는 코드의 변경 사항조차 인지하지 못하는 상황을 지나치게 쉽게 만들어 줄 뿐입니다.

그래서 저는 프로덕션 환경에서 RubyGems.org 사용을 중단할 것을 제안합니다.

해결책

서명만으로는 부족하다

모든 개발자가 젬에 서명하는 것이 좋습니다. 하지만 서명만으로는 부족합니다. 서명은 배포자의 신원만 확인해 줄 뿐, 그 사람이 선한지, 악의적인지, 아니면 예측 불가능한지는 알려주지 못합니다.

구원자는 오지 않는다

이상적인 세계라면, 안전하다고 검증된 젬 버전들을 배포해 주는 신뢰할 수 있는 조직이 있을 것입니다.

이 발상이 거부감을 준다면, Debian 같은 조직이 리눅스 생태계에서 비슷한 역할을 수행하는 방식을 떠올려 보세요. 그래도 거부감이 든다면... 제 블로그를 읽어 주셔서 영광입니다, Stallman 씨.

왜 '이상적인 세계'라고 표현했을까요? 다른 대안들은 엄청난 노동력이 필요하고, 저는 그 작업을 감당하고 싶지 않기 때문입니다.

...YC 지망생 여러분 중 누군가 이 문제를 해결해 주시면 감사하겠습니다.

유일한 현실적 대안: 직접 호스팅(DIY)

안타깝게도 우리는 이 지저분한 현실 세계에 살아야 합니다. 젬을 완전히 통제할 수 있는 유일한 현실적인 방법은 다음 두 가지입니다.

  • 악성 코드가 숨어 있는지 코드를 직접 검토하기

  • 젬을 직접 호스팅하기

다행히 자체 젬 호스팅은 생각보다 어렵지 않습니다. 지금부터 그 방법을 소개합니다.

자체 젬 호스팅 방법

프로덕션 환경에서 RubyGems.org를 쓰면 안 되는 이유와 자체 젬 호스팅 가이드

마법 같은 것은 없다

젬 '서버'란 웹에 올려진 단순한 파일들의 집합일 뿐입니다. S3나 Dropbox 등 어디에서든 호스팅할 수 있습니다.

테스트벤치용 Rails 앱 만들기

여기서 Rails를 사용하는 유일한 이유는 미리 준비된 Gemfile을 활용할 수 있기 때문입니다. 이 앱 자체는 아무 기능도 없습니다.

$ rails new myapp
$ cd myapp

.gem 파일 내려받기

다음 bundler 명령어는 Gemfile.lock에 명시된 모든 .gem 파일을 내려받아 /vendor/cache 디렉터리에 저장합니다.

$ bundle package
$ mkdir /tmp/gem_server
$ mv vendor/cache/ /tmp/gem_server/gems

'서버' 파일 생성하기

.gem 파일이 담긴 디렉터리를 서빙 가능한 형태로 만들려면 gem generate_index를 실행합니다.

$ gem generate_index -d /tmp/gem_server/

온라인에 올리기

물론 프로덕션 환경에서는 권장하지 않지만, 예시로 Dropbox에 옮겨 보겠습니다.

$ mv /tmp/gem_server /Users/snhorne/Dropbox/Public/

Gemfile 수정 후 bundle update

Gemfile의 "source" 라인을 rubygems 대신 새 호스트를 가리키도록 수정합니다.

# Gemfile

source 'https://dl.dropboxusercontent.com/u/12345/gem_server'
...
gem 'rails', '3.2.13'

이제 Dropbox에서 젬을 가져오는 것을 확인할 수 있습니다.

$ bundle update
Fetching gem metadata from https://dl.dropboxusercontent.com/u/12345/gem_server/.
Fetching full source index from https://dl.dropboxusercontent.com/u/12345/gem_server/
Using rake (10.1.0)
Using i18n (0.6.1)
Using multi_json (1.7.7)
Using activesupport (3.2.13)

보너스: HTTP 기본 인증으로 프라이빗 젬 보호하기

프라이빗 젬을 보호하려면 Gemfile을 다음과 같이 작성하면 됩니다:

# Gemfile

source 'https://username:password@example.com/'

다른 대안들

이 방식이 상황에 맞지 않는다면, 더 다양한 기능을 제공하는 무료 및 상용 젬 호스팅 솔루션도 있습니다.

Geminabox는 오픈소스 애플리케이션으로, 번거로운 명령줄 작업을 상당 부분 자동화해 줍니다.

Gemfury는 프라이빗 젬 호스팅을 제공하는 상용 서비스입니다. 과거 고객사의 사내 전용 젬을 호스팅하는 데 사용한 적이 있는데, 설정이 간편했고 문제가 발생한 적은 한 번도 없었습니다.