Infisical에서 영감을 얻어, env를 DB에서 관리하는 것으로 결정지었다.
그리고 이 env를 활용하는 모듈을 직접 개발하여 사내 환경변수 관리 모듈 시스템으로 도입하였다.
작년 ISMS 심사를 받으면서 보안에 대한 공부를 조금이라도 더 하는 계기가 되었다.
당시 심사 도중 환경변수에 관련한 지적이 오간 적이 있었는데, 이에 대해서 react-native 내에 직접적으로 환경 변수가 사용되는 기존 시스템은 문제가 많다는 것을 확인하게 되었다.
자세한 내용은 react-native 공식 docs security 부분을 참조하면 좋다.
그곳을 보면 시작부터 시작부터 앱개발하면서 보안에 대해 경시하는 경우가 많다고 지적한다( 뜨끔...)
그래서, 어떤 점이 문제가 되는지에 대해 찾아보고 고민해본 결과를 작성해보고자 한다.
민감한 API 정보들은 절대로 코드 안에 대놓고 삽입하지 말라고 그런다. 이렇게 삽입된 데이터는 언제든지 번들링된 파일에서 접근이 가능하다고 한다.
실제로 그러한지 확인을 해보자
// 내 앱의 bundling 파일을 보여주세요
// 1. IOS
npx react-native bundle --entry-file=index.js --bundle-output='./bundle.js' --dev=false --platform='ios' --assets-dest='./ios' --reset-cache
// 2. ANDROID
npx react-native bundle --entry-file=index.js --bundle-output='./bundle.js' --dev=false --platform='android' --assets-dest='./android/app/src/main/res' --reset-cache
위에 작성된 커맨드를 React-native 프로젝트의 루트폴더 위치에서 입력해보면, 터미널 창이 열심히 일을 한 뒤에 번들링 파일을 하나 퉤 하고 내뱉는다.
우리 앱의 자바스크립트 코드를 하나로 뭉쳐놓은 파일을 전해주는데, 당연한 이야기지만 코드 상에서 민감한 Secret 키같은게 존재할 경우 그대로 여기에 박혀있게 된다.
예를 들어,
코드 상에 직접적으로 민감한 값을 심어놓게 된다면 그대로 번들 파일에 그것이 노출되게 된다.
이런 문제점을 해결하기 위해, .env파일을 사용하는 것이 일반적이다.
이 장소에 작성해 둔 환경변수값은 "react-native-config" 라이브러리를 이용하게 된다면 앱을 빌드하는 시점에서 env 내부의 값을 읽은 후, 자바스크립트에 주입시킨다고 한다.
실제로 env 파일을 생성해서 코드 내에서 이 env를 읽는 코드를 작성해놓았을 때, 번들링된 js 파일을 검색해보면 해당 값이 노출되지 않는 것을 확인할 수 있었다.
이를 활용하여 fastlane과 같은 배포 자동화 도구를 함께 도입한다면 손쉽게 env를 주입해 배포하는 것도 가능하다.
env 관련 공부를 해보다 보니, 다른 회사에서는 배포를 할 때 CI/CD 툴로 fastlane을 사용한다는 글을 발견해서 나중에 한번 공부하고 도입하는 시도를 해보려고 한다.
fastlane을 통한 배포자동화 link1, link2
이것만으로도 이미 충분히 env 관리라고 생각하지만, 개발 차원에서의 불편함이 완전히 해소되는 것은 아니었다.
예를 들어, 개발 팀원들이 개발을 하면서 환경 변수를 관리할 때에 불편한 점은 아래와 같았다.
"도대체 언제 환경 변수가 바뀌었는 지 알아차려서 이것을 내 로컬에 적용해야 하는거죠?"
회사에서는 개발 단계에서 관리될 환경변수에 대한 솔루션이 구축되지 않았던 상태였기에,
모든 개발자들이 Notion에 환경 변수를 하나하나 기입하고, 이것을 업데이트 하며, 필요에 따라 복사하여 로컬에 붙여넣는 최신화(....) 를 하는 상황에 처해 있었다.
그런데 단순하게 생각하더라도, 이것은 너무나도 귀찮고 휴먼 에러가 발생하기 쉬운 환경이다.
우선 아래와 같은 귀찮은 문제들이 있었다.
1. 매번 Notion을 켜야 한다 (제일 귀찮음)
2. 전체 값을 드래그 해서 복사해서 붙여넣기를 로컬에 가져와서 해야한다.
3. 값을 추가하려고 하면 혹시라도 중복된 값이 있을 지 일일이 확인하고 있어야 한다.
4. 내가 값을 변경했으면 이를 노션에 업데이트하고 주변 동료에게 공지해야 한다
배포 당시에는 자동화를 이용하여 환경 변수를 주입할 수 있는데,
정작 개발 단계에서 항상 개발자들이 로컬 환경의 env의 최신화에 대해서 많은 노력을 투자해야 하는 상황은 얼 핏 보더라도 바람직하지 않은 상황이었다.
그래서 이것 저것 방법을 찾기 시작했다.
Docs에 따르면, Serverless function과 같이 특정 역할을 수행하는 레이어를 만들어서 환경 변수 저장소와 app 사이를 중재하도록 해야 한다고 한다.
ISMS에서 심사를 받을 때, 권고사항으로도 들었던 이야기이지만 필요한 환경 변수를 서버에서 요청해서 사용하도록 들었는데 그 이야기가 바로 저 오케스트레이션 레이어라는 것으로 지금 이해되고 있었다.
오케스트레이션 레이어는 다음과 같은 주요 기능을 수행합니다:
자동화된 배포 및 관리: 오케스트레이션 레이어는 서비스 및 컴포넌트의 배포를 자동화하고, 필요한 경우 스케일링 및 로드 밸런싱과 같은 관리 작업을 수행합니다. 이를 통해 시스템의 안정성과 확장성을 유지할 수 있습니다.
리소스 관리: 오케스트레이션 레이어는 컴퓨팅 리소스(예: 가상 머신, 컨테이너)를 효율적으로 할당하고, 리소스의 상태 및 가용성을 모니터링합니다. 필요에 따라 리소스를 프로비저닝하고 해제하여 최적의 성능을 달성합니다.
서비스 디스커버리와 통신: 오케스트레이션 레이어는 서비스 디스커버리 메커니즘을 제공하여 서비스 간 통신을 관리합니다. 서비스 간의 네트워크 연결 설정, 로드 밸런싱, 서비스 검색 등을 처리합니다.
상태 관리와 오류 복구: 오케스트레이션 레이어는 서비스 및 컴포넌트의 상태를 관리하고, 필요한 경우 오류 복구와 재시작을 수행합니다. 장애가 발생한 경우 자동으로 대처하여 시스템의 신뢰성을 높입니다.
Docs에서의 설명에 따르면, orchestration은 여러가지가 될 수 있다.
예시로 든 것은 serverless 함수를 대안으로 쓸 수 있다는 것이다.
위의 설명에 따르면, serverless 함수로 환경 변수를 요청해 응답받는 케이스는 리소스 관리 에 해당하는 부분이 될 수 있을 것이다.
위의 글에 따르면, Persisted한 데이터 (즉, 따로 디스크에 저장하게 되는 환경변수들) 은 새롭게 네트워크 요청을 할 필요는 없어지지만, 공격자에 의해서 언제든지 접근이 가능해진다고 한다.
따라서, 정말로 민감한 정보일 경우 저장을 하지 않고 매번 요청을 날려서 응답으로 사용하도록 하라는 권고사항에 대한 내용이었다.
일단, 회사 내에서는 env 자체를 ISMS 심사 권고사항으로 인해 서버에 저장을 하고 있는 상황이었는데,
이 구조를 적극 활용하여, 현재 회사의 Backend 서버가 Orchastration layer 역할을 해주면 좋겠다는 판단을 하게 되었다.
사실 네트워크 요청을 통해 DB에서 환경변수를 가져오는 것에 대해서는 크게 문제될 것은 없는데, 우리 서버에서는 가장 큰 장벽이 존재하였다.
그것은 바로 서버에 요청을 날리려면, 반드시 인증된 유저여야했다는 점이다.
위 부분은 서버에서 Bearer 토큰을 분해해서 해당 토큰이 유효한지 아닌 지에 대해 확인하고, DB 요청을 날릴 수 있을 지 아닐 지에 대해서 구별하는 grapql-shield의 기능이다.
문제는 여기서 말하는 서버의 Bearer 토큰이 앱의 로직 상, 특정 조건을 만족해야만 생길 수 있는 값이었기 때문에 native 앱이 빌드되는 최초의 순간부터 필요한 환경 변수들과 같은 경우 네트워크 요청을 날릴 수 조차 없는 상황이 발생하게 되었다.
내부 로직상 자세한 설명은 어렵지만, 앱을 실행시키고 나서 지갑을 생성하는 과정을 거쳐야만 access token을 발행할 수 있었기에, 앱을 설치하고 실행만 했을 단계에서 본인인증과 같은 서비스에 필요한 환경 변수들을 네트워크 요청을 통해 받아올 수 없는 상황이 발생했다.
그래서 이 permission 룰을 그냥 모두 허용하도록 하자니, "환경 변수에 관한 테이블을 접근하는데 아무 조건도 없이 접근한다? 이건 그냥 보안적으로 문제되는 거 아님?" 과 같은 의견들이 당연스럽게 나오게 되었다.
그래서 계속 토의를 하다가 github actions라는 게 있는데 한번 연구해보는게 어떻겠냐는 팀원의 제안에 따라 공부를 해보게 되었다.
github actions는 깃헙에서 제공하는 자동화 시스템이다.
예를 들어, 우리가 자주 사용하는 커맨드, "git pull" 을 실행한다고 가정하자.
그러면 github actions에 등록된 액션은 마치 미들웨어처럼 해당 작업의 중간에서 "특정 작업" 을 수행하는 역할을 한다.
그런데, 해당 기능은 카카오 테크블로그를 보나, 다른 secrets 삽입후 테스팅해보는 블로그를 보더라도 확인된 점이지만, 원격에서 특정 작업을 실행하도록 만드는 데에 그 주점이 있지, 특정 action을 실행했을 때 우리의 "로컬환경" 에 무언가를 하도록 하는 것은 불가능하다고 판단하였다(아마 내 지식이 부족한 걸 수도 있지만, 아무리 찾아봐도 원격의 가상 환경에서 실행되고 있기 때문에 사용자의 로컬에 무슨 작업을 가하는 것은 불가능해 보였다)
그래서 더 고민을 하다가, Infisical이라는 오픈소스를 발견하게 되었다.
위의 사진을 보면 알 수 있겠지만, 말 그대로 env를 저장하고 관리하는 GUI까지 한번에 제공해주는 엄청난 솔루션이다.
예를 들어, next.js로 페이지를 제작한다고 할 때, 나는 이 사이트를 AWS의 EC2에 배포를 하게 될 것이다.
그러면, 해당 Infisical을 가상환경에 구현해두고 앱을 실행시킬때마다 infisical에 저장되어 있는 secret을 주입하면서 시작시킬 수 있다.
간단히 말해서 환경 변수를 환경에 주입시켜주고, 손쉽게 UI적으로도 관리할 수 있게 만든 툴이다.
정말로 개발을 할 때 필요한 기능을 제공하고 있어서 사용하려고 하였지만, 아쉬운 부분이 있어서 포기하게 되었는데 그 이유는
1. 현재 사용되어야 하는 환경은 react-native이다.
2. React-native에서 환경변수가 반영되도록 빌드되는 설정이 좀 다르다.
3. React-native 앱에서는 필연적으로 실기기 테스트도 해야 하는데, 단순히 환경에다가 secret들을 주입시키는 것으로 테스트가 불가능했다.
아마, 내가 부족해서 방법을 찾지 못했을 가능성이 더 높겠지만 일단 앱에서 사용하기에는 여러가지 제약점이 많았다.
하지만, 정말 웹에서 환경변수 관리하면서 개발하기에는 아주 좋아보였고 infisical 덕택에 영감을 가져온 게 있었다.
infisical의 공식 문서에서 로컬로 해당 환경을 구성하는 커맨드를 직접 입력해보면 알겠지만,
Repository에서 클론을 해온 후, 거기에 있는 도커파일을 이용하여 컨테이너를 만드는 것을 알 수 있다.
이 컨테이너의 backend 부분을 보면 MongoDB를 이용해서 DB를 구축하고 있는데 이거 완전
엥? 우리 서버에 있는 DB랑 하는 게 똑같네?
라는 판단이 든 것이다.
그래서, 이런 생각을 하게 되었다.
이미 서버에 환경 변수를 저장, 관리하고 있고 이것을 요청해서 env로 만들어서 저장는 모듈을 하나 만들어두면 쓰기 참 좋겠구나
라고 말이다.
그래서, 직접 사내 환경변수 관리용 모듈을 만들어보기로 하였다.
이에 대해서는 다음 글에서 이어가려고 한다.
잘 보구 갑니다!