2P by GN⁺ | ★ favorite | 댓글 1개
  • Rust 생태계에서는 유지보수 중단 의존성이 RUSTSEC에 오르는 순간, 직접 문제가 없던 라이브러리도 사용자와 CI를 통해 기술 부채가 됨
  • insta가 의존하던 yaml-rust는 원 작성자의 관심이 줄어든 뒤 기능 요청과 버그가 쌓인 상태였음
  • RUSTSEC 등록 이후 직접·간접 사용자들의 CI가 실패했고, 금융 비유로는 등급 하락마진콜이 발생한 셈임
  • 대체 라이브러리나 포크도 뚜렷한 해법이 아니어서, 의존성을 바꿔도 유지보수 부담과 새 의존성 위험이 남음
  • 최종 대응은 yaml-rust 코드를 insta 안에 벤더링하는 것이었고, 부실 기술 부채를 AAA로 포장한 CDO에 가깝다는 비판으로 이어짐

yaml-rust 의존성이 기술 부채로 드러난 과정

  • instayaml-rust에 의존하고 있었고, yaml-rust는 원 작성자가 관심을 잃은 뒤 이슈가 계속 쌓인 상태였음
    • 일부는 기능 요청이고, 일부는 실제 버그였음
    • insta 유지보수자는 해당 문제를 직접 겪지 않았지만, 유지보수 중단 의존성이라는 점에서 기술 부채였음
  • yaml-rustRUSTSEC 데이터베이스에 추가되는 논의에 오르면서 상황이 바뀜
    • RUSTSEC는 금융 비유에서 등급 평가 기관 역할을 함
    • 등록 이후 yaml-rust를 직접 또는 간접으로 쓰는 많은 프로젝트의 CI가 몇 분 안에 실패하기 시작함
    • 사용자들이 insta 유지보수자에게 yaml-rust 사용 문제를 지적하면서, 금융 비유상 마진콜이 발생한 셈이 됨

선택지와 실제 대응

  • 대안으로 갈아타는 방식은 매력적이지 않았음
    • 한 대안은 yaml-rust의 포크이고, 유지보수자가 1명뿐이며 의존성 3개를 추가함
    • 그중 하나는 이미 “B-” 등급을 받은 상태였음
    • 생태계의 다른 선택지는 지적받기 전에 기본값을 바꾸는 결정을 했음
  • 직접 포크하는 방식도 근본 해결책이 아니었음
    • 포크한 라이브러리에는 같은 유지보수 요구가 따라옴
    • 버그 리포트에 대응하지 않으면 결국 기존 yaml-rust처럼 지적받게 됨
    • 따라서 포크는 시간을 벌 수는 있지만 문제를 없애지는 못함
  • 실제 대응은 yaml-rust 코드를 insta 안으로 합치는 벤더링이었음
    • insta는 이제 insta 코드와 yaml-rust가 합쳐진 형태가 됨
    • 이는 부실 기술 부채를 AAA로 격상한 것 같은 구조임
    • 제목의 CDO는 2007년 금융위기 때 악명 높아진 부채담보부증권을 가리킴
  • 최종 평가는 “아무도 이기지 못했다”에 가까움
    • 문제 코드는 사라지지 않았고, 위치만 insta 내부로 옮겨짐
    • 외부 의존성으로 보일 때의 압박은 줄었지만, 유지보수 부담 자체는 남아 있음

댓글과 토론

Hacker News 의견들
  • 직설적으로 말하면, 가장 인기 있는 YAML 파서인 serde_yaml(https://lib.rs/crates/serde_yaml) 작성자가 예고나 후임 관리자 지정 없이 갑자기 손을 떼고, deprecated 및 unmaintained로 표시한 상황임
    left-pad와 완전히 같지는 않음. 패키지는 여전히 동작하고 crates.io는 삭제도 허용하지 않지만, 이 패키지는 4,000개 다른 크레이트에서 쓰이고 있음
    감사 및 자동 업데이트 도구들이 관리되지 않는 크레이트 사용을 문제 삼게 될 것임

    • 정확하지 않음. 원문은 yaml-rust(https://github.com/chyh1990/yaml-rust)에 의존한다고 말하고, 이 프로젝트가 이제 관리되지 않게 된 것임
      동시에 사람들이 대거 그쪽으로 옮겨가는 걸 막기 위해 serde_yaml도 unmaintained로 표시된 것임
    • 여기서는 해당하지 않지만, 의존성이 없는 라이브러리는 가끔 정말로 기능이 완성된 상태라 보안 수정 외에는 바꿀 필요가 거의 없을 수 있음
      감사 도구와 회사 정책이 최근 커밋이 없다는 이유만으로 “관리되지 않음”을 계속 경고하지 말고 이런 경우를 구분할 만큼 똑똑했으면 함
    • 그 작성자는 Rust 라이브러리 생태계에서 꽤 다작하는 사람 아닌가? 다른 라이브러리에는 이런 조치를 하지 않았음
      더 자세한 정보가 있는지 궁금함
  • 설명 없이 나온 CDO라는 약어가 바로 이해되지 않았는데, 글에서 collateralized라는 표현이 여러 번 나오는 걸 보면 부채담보부증권(collateralized debt obligation)일 듯함
    https://en.wikipedia.org/wiki/Collateralized_debt_obligation
    처음에는 chief data officer를 떠올렸음

    • 여기서의 비유는 CDO가 다른 부채들로 만들어진 금융상품이라는 데 있음
      구체적으로 2008년에는 여러 주택담보대출에 대한 부분 소유권 같은 것이었고, 부실 주택담보대출이 채무불이행에 빠지자 CDO도 같이 망가졌음 [1]
      어쨌든 글쓴이가 생각하는 것은 부채 기반 비유라기보다 leftpad나 연쇄 DNS 장애 같은 시스템 리스크에 가까움. 2008년이 그렇게 큰 혼란이 된 큰 이유도 부채로 상품을 만들었다는 사실 자체보다 시스템적 문제가 컸기 때문임 [2]
      1. 재미있는 사실로, 2008년 전에는 주택담보 기반 금융상품 수요가 끝없이 커져서 CDO로 만든 CDO까지 만들기 시작했음. 직업이 없는 사람에게도 주택담보대출을 내주고, 그걸 AAA 신용등급 상품에 포장하면 팔기 좋은 수익성 상품이 됨
      2. Lehman과 Bear Stearns의 레버리지 비율이 30~40배였다는 점도 있음. 이건 미친 수준임
    • 맞음. 함의는 부실한 기술 부채를 평판 좋은 패키지, 즉 AAA 패키지로 묶도록 인센티브를 만들면 Rust 생태계가 거품으로 향하고 결국 붕괴할 수 있다는 것임
      규제 역할을 해야 할 평가 기관이 이미 공략당했다는 의미이기도 함
    • The Big Short를 봐야 함. CDO가 뭔지 거기서 배웠음
    • CDO가 세계 경제를 무너뜨리던 시절의 헤드라인을 읽기엔 너무 어렸던 세대가 이제 있다는 걸 깨달았음. 늙은 느낌임
    • https://en.wikipedia.org/wiki/Collaboration_Data_Objects를 떠올렸음 ;)
  • 이게 “승리”인지는 “승리”의 정의에 너무 좌우돼서 논쟁하고 싶지는 않지만, 장점은 분명히 있음. 실행되지도 않고 외부 라이브러리에서 도달할 수도 없는 취약 코드 경로는 이제 안전한 코드 경로가 됨
    물론 신경 쓰이는 상태이긴 하지만 안전하긴 함
    이런 식의 벤더링에는 또 다른 장점도 있음. 자기 라이브러리에 탄탄한 테스트 커버리지가 있다면, 새로 가져온 라이브러리에 대해 코드 커버리지 도구를 돌릴 수 있음
    라이브러리 수정은 어려울 수 있지만, 자기 코드가 건드리지 않는 부분을 비교적 쉽게 삭제해 나갈 수도 있음. 구조에 따라 다르지만 취약 코드가 전부 삭제된다면 명확한 이득이고, 실제로 일부를 쓰고 있었다는 걸 발견해도 더 분명한 이득일 수 있음
    즉 공개 유지보수를 위해 라이브러리를 포크하는 건 큰 책임이지만, 필요한 부분만 벤더링해서 손보는 건 훨씬 작은 부담임. 실제 가지치기를 하지 않더라도 그걸 쉽게 만든다는 사실 자체가 진전임
    단점도 분명히 있지만, 전부 나쁜 건 아님

    • 실제로는 아주 좋은 결과임. 모든 의존성은 보안 리스크이고, 모든 의존성을 벤더링하지 말자는 논리는 알려진 보안 문제를 피하려면 외부 의존성을 최신으로 유지해야 한다는 데 있음
      그런데 외부 의존성을 더 이상 지켜보는 사람이 없다면 그 논리는 사라지고, 그 의존성은 부담이 됨
      의존성이 abandoned로 표시되고 보안 보고서에 잡히기 시작한 건 원하는 동작임. 덕분에 벤더링할지, 즉 “나쁜 행위자가 뭔가 몰래 넣는” 위험을 제거할지, 아니면 다른 선택을 할지 정보에 기반해 판단할 수 있음
      Rust 빌드 시스템이 이걸 쉽게 허용한다는 점도 좋음
    • Google에서는 모든 서드파티 의존성을 정책상 벤더링해야 한다는 점을 짚을 만함
      보통 거대한 모노레포 전체에서 해당 의존성의 버전도 하나만 허용됨. 내가 있던 이후 바뀌었을 수도 있음
      그리고 각 third_party 의존성마다 지정된 책임자나 OWNERS가 있음
      이 덕분에 비교적 질서가 유지됨
      다만 이런 강제된 규율은 시간과 돈이 충분하고 “빠르게 움직이고 부숴라” 철학에 묶이지 않은 Google 같은 조직에서는 잘 맞을 수 있음. 스타트업에서도 얼마나 잘 맞을지는 모르겠음
    • 라이브러리 관리자가 자기 라이브러리의 전이적 코드 커버리지, 예를 들어 어떤 다른 라이브러리의 테스트 스위트가 자신을 사용하는지 계산할 수 있는 도구가 있는지 궁금함
      어떤 의미에서는 다른 라이브러리들이 무작위에 가까운 호출을 제공하는 방향성 있는 퍼즈 테스트라서 흥미로운 테스트 전략이 될 수 있음
  • JS npm 생태계에서도 같은 패턴을 봤음
    npm audit은 보안 문제에서 대체로 양치기 소년처럼 굴고, 라이선스가 허락한다면 코드를 안으로 끌어오는 것이 사용자의 가짜 이슈에 압도되지 않는 가장 안정적인 방법 중 하나임
    사용자들이 맥락을 이해하지 못하거나, 고용주의 정책이 현실과 동떨어진 곳에서 만들어졌기 때문에 신경 쓰지 않는 경우가 많음
    빌드 파이프라인의 전이 의존성 코드베이스 일부에서 쓰이는 정규식이 실제로 서비스 거부 공격에 악용될 수 있는 건 아님
    깊은 전이 의존성의 “이슈”는 특히 우회하기 귀찮을 수 있음. 구조상 “우리는 그 코드 경로에 절대 들어가지 않으므로 결함의 영향을 받지 않는다”거나 “그 경로에 들어가는 유일한 경우는 오프라인 환경의 신뢰된 입력이다” 같은 사실을 기술적으로 입증하기 어려운 경우가 많음

    • JS 세계에서 개발 의존성의 이슈는 정말 골칫거리임
      이런 문제가 중요한 시나리오가 존재하긴 함. 예를 들어 침해된 빌드 도구가 빌드 중인 라이브러리에 악성 코드를 주입하는 경우가 있음
      하지만 그런 경우는 극히 드물고, 빌드에서 호출할 뿐이라 실제로는 중요하지 않은 서비스 거부 가능 정규식의 물결 속에 묻혀 있음
      여기에 일반적인 빌드 도구가 전이 의존성 500억 개쯤 되는 트리를 갖는 점까지 더해지면 정말 고된 일이 됨
      이런 이슈를 보고하는 도구는 “재배포하면 악용 가능”과 “빌드 파이프라인에서 사용하면 악용 가능”을 구분해야 한다고 봄
    • 설령 지금은 그 문제 경로에 들어가지 않는다고 해도, 나중에 의존성이 업데이트되면서 문제 경로에 들어가게 될 수 있음
  • “이제 부실 기술 부채가 갑자기 AAA 등급이 됐다”에서 “갑자기”라는 표현은, 같은 코드가 벤더링됐다는 이유로 이전보다 더 좋은 부채 등급을 받는 게 말이 안 된다는 뜻으로 보임
    하지만 그건 코드 자체의 가치만 보는 것이고, 전체 가치 제안에서 가장 중요한 부분을 놓침
    관리자가 코드를 안으로 가져오면, 이제 그 코드는 그 관리자의 소유가 됨. 죽은 프로젝트의 코드를 활동 중인 관리자가 벤더링한다면, 이슈에 대응하고 풀 리퀘스트를 리뷰하고 버그를 고칠 수 있는 활동 중인 사람이 생기므로 그 코드의 가치는 올라감
    다른 비유로, 방치된 반려동물을 새 주인에게 보내면 더 잘 돌봄을 받고 건강해지고 오래 살 수 있기 때문에 가치가 올라가는 것과 같음

    • 그 의존성을 간접적으로 쓰는 사람들에게만 해당함. 이 경우 버그를 찾기 위해 보는 눈은 아주 적을 가능성이 큼
      관리자도 크고 낯선 코드베이스에 적응해야 해서 수정 구현이나 리뷰에 진입 장벽이 생길 것임
  • 약간 곁가지이고 논쟁적인 생각이지만, 소스 기반 패키지 관리자가 공개된 패키지의 유지보수를 레지스트리가 강제로 인수할 법적 권리를 보장하지 않는다면 끔찍한 문제를 피하기 어렵다고 봄
    방치, 악의적 변경, 악의적 제거, 사칭 같은 문제들임
    어떤 패키지가 더 큰 커뮤니티에 충분히 중요하다고 판단된다면, 원소유자의 손에서 패키지 레지스트리 항목을 빼앗아 포크를 가리키게 할 방법이 필요함
    당연히 이런 조치에는 많은 드라마가 따르겠지만, 하위 사용자들을 적극적으로 보호할 수 있음

    • 이건 별문제가 아닌 것 같음. 오픈소스의 아름다움은 포크할 수 있다는 데 있음
      실제 문제는 오픈소스 프로젝트를 유지보수하는 데 시간과 노력이 필요하고, 여유 시간이 있는 다른 사람을 찾기 쉽지 않다는 점임
    • 레지스트리가 필요하다고 판단할 때 패키지 소유권을 빼앗는 소스 기반 패키지 관리자는 기여를 끌어들이기 어려워야 함
      모든 기여가 자동으로 특정 집단의 소유가 되는 저작권 모델보다도 덜 매력적임
      GitHub가 자유 소프트웨어를 호스팅하면서 그 소프트웨어의 저작권 침해에 특화된 곳이라고 주장할 여지는 있겠지만, 적어도 지금은 패키지의 명목상 소유권을 빼앗으려 하지는 않음
    • 커뮤니티 차원에서 중요하다는 이유라면, 잘해도 패키지 수준에서 할 일이지 레지스트리 수준에서 할 일은 아님
      커뮤니티 얘기인 이상 고객-공급자 관계가 아니고, 대부분의 패키지는 무료로 제공될 만큼만 중요함
      “월 10달러 줄 테니 우리를 위해 10만 개 패키지를 관리해 달라” 같은 굴욕적인 금액이 될 수도 있음
    • 이 문제들은 모두 같은 종류가 아님. 동결된 패키지는 하위 의존성들이 결정을 내려야 한다는 뜻이지만, 벤더링을 포함해 선택지는 충분히 많음
      인터넷에는 관리되지 않는 소스 코드가 많고, 그냥 있는 그대로 한 번 공개된 것일 뿐임. 무료 코드에 대해 누구도 업데이트를 보장받지 않음
    • 대안은 Kik NPM 문제임. 어느 쪽이든 나쁨
  • 누군가 이미 yaml-rust를 포크해 yaml-rust2(https://github.com/Ethiraric/yaml-rust2/blob/master/document...)를 만든 건 꽤 운이 좋았음
    그 포크가 YAML 테스트 스위트를 완전히 통과하고, 벤치마크에서도 더 빠르다는 점도 멋짐. 마이그레이션도 간단해 보임
    결국 문제는 남아 있음. 우리는 지금은 기꺼이 무료 노동을 제공하는 다른 사람들의 작업에 의존하지만, 그게 영원히 계속되지는 않을 수 있음
    그들의 시간과 노력에 보상하고 좋은 작업을 계속해 주길 바라는 것 말고 우회 방법이 있는지는 모르겠음

    • yaml-rust는 원래 순수 Rust 구현이었고, 태그라인에 문자 그대로 그렇게 적혀 있음
      “A pure rust YAML implementation.”
      오히려 serde_yaml은 libyaml을 c2rust로 변환한 unsafe-libyaml에 의존했기 때문에 순수 Rust라고 보기 더 어려웠음
    • 버스 팩터 1인 프로젝트는 본질적으로 위험함
      충분히 긴 시간 범위에서는 오픈소스 관리자가 프로젝트를 그만둘 확률이 1임
      이를 피하는 유일한 방법은 1인 유지보수 프로젝트를 금기시하는 것임
    • 이 문제에 대한 내 기본 해법은 가능한 한 서드파티 의존성을 피하는 것임
      Rust 표준 라이브러리를 만든 사람들이 표준 라이브러리를 빈약하게 유지하길 원하기 때문에 Rust 프로젝트에는 아마 선택지가 아닐 수 있음
      하지만 기능이 충분한 표준 라이브러리를 가진 다른 언어를 쓸 때는 분명 가능한 선택임
      필요한 것들이 내장된 언어를 의도적으로 쓰기 때문에, 데이터베이스 드라이버 말고는 외부 라이브러리를 거의 쓰지 않음
  • 이 상황 전체가 좀 우스꽝스러워 보임. 코드가 동작하고 수년간 그래 왔다면, 관리되지 않는다는 게 왜 문제인지 모르겠음
    수정할 필요가 없고 한계와 기능을 알고 있다면 괜찮음
    코드는 저절로 상하지 않음. 수십 년 전 코드를 빌리거나 통합해서 잘 쓴 적이 여러 번 있음
    개인적으로는 그 라이브러리에 대한 모든 불평을 그냥 무시하고 넘어갈 것 같음

    • 당연한 답은 그것이 여러 면에서 “동작”하지 않는다는 것임
      이미 많은 버그가 쌓였고, 수정이 존재하는데도 절대 패치되지 않을 것임. https://github.com/chyh1990/yaml-rust/issues와 /pulls를 보면 됨
    • 수십 년 전 코드를 빌리거나 통합했다는 게, 글쓴이가 라이브러리를 자기 프로젝트 안으로 벤더링해서 한 일 아닌가?
      자기가 쓰는 코드 조각의 유지보수를 떠안는 것과 같음
      때로는 나쁠 수 있음. 복사-붙여넣기 코딩이나 중복 노력이 될 수 있으니까
      하지만 서드파티 구성요소가 완전히 관리되지 않는 상태라면 꽤 이해할 만함
  • 맞음, 의존성은 벤더링하면 됨. “거의 완성”에 가깝고 개발과 유지보수가 느려진 의존성은 20년 동안 대체로 그렇게 해 왔음
    다만 “batteries are not included”인 언어에서는 일해 본 적이 없음

  • cargo vendor --aggressive 같은 걸 만들어서, 내 크레이트 기준으로 의존성 안의 죽은 코드를 모두 가지치기할 방법이 있을까?
    “의존성을 리뷰하라”는 문제를 더 다루기 쉽게 만들 수 있을지 궁금함
    글의 본론에서는 벗어나지만, 결국 의존성 선택과 그에 따르는 모든 책임은 우리에게 있다는 점에서 관련이 있음
    실제로 크레이트에 컴파일되어 들어가는 것에 대해 더 책임질 수 있도록 돕는 도구가 들어설 여지가 있어 보임

    • 그럼 라이브러리의 모든 버그 리포트도 자기 버그 추적기로 복사해 오나?
      그렇지 않다면 개선되는 게 무엇인지 모르겠음