지난주 후반부, 한국 뿐만 아니라 전세계가 시끌시끌했습니다. 사실상의 서버측 로깅 표준으로 자리잡힌 log4j 로깅 모듈의 보안 취약점이 발견되었고 공격방법이 인터넷에 고개되면서 Zero Day Vulnerability 가 발생했기 때문입니다.
취약점을 간단히 요약하자면, "로그로 기록되어야 하는 문자열에 악의적인 내용을 집어넣어 원격지에서 서버의 코드, 명령을 실행할 수 있는 취약점이 있었음" 입니다.
취약점을 통해 외부에 노출되지 않은 LDAP 서버 등으로 악의적인 코드가 담긴 자바 클래스를 전송하고 log4j 를 통해 이 클래스를 메모리에 로딩하는 방식으로 서버에 침투하게 됩니다. 이번 취약점은 오래전 SSL 프로토콜의 취약점으로 명성을 날렸던 하트블리드(heartbleed)급으로 알려졌습니다.
취약점에 대한 대응은 크게 두가지 방식으로 진행되었습니다. 1. 취약점이 패치된 log4j 가 배포되기 전까지는 코드 인젝션이 발생할 수 있는 JNDI 의 옵션을 끈다 2. 취약점 패치 버전 (2.15.0+) 가 배포된 후에는 새로운 log4j 로 교체한다
이런 대응이 여러 회사의 개발조직 중심으로 대응되는 동안 물들어올때 노를 저어야 한다는 것을 잘 알고 있는 클라우드 벤더들은 열심히 관련된 포스팅을 날리며 자사의 보안 솔루션들을 홍보하기 시작했습니다.
간만에 글로벌 초대형 급의 취약점으로 주말이 시끌시끌 했습니다. 아직까지 취약점은 현재 진행형이기 때문에 두눈 부릅뜨고 log4j 를 쓰는 곳이 없는지 살펴봐야 하겠습니다!
스크래치 Scratch 를 비롯한 드래그 앤 드랍 방식의 코딩 도구들이 아이들 코딩 교육에 많이 쓰입니다. 스크래치 이후 등장한 엔트리 entry 등 수많은 도구들은 스크래치가 제안한 블록 코딩을 철저히(?) 따르고 있기도 합니다. 성인들의 세계에서는 블록 코딩은 아니지만 노코드 No code 개발이 하나의 트랜드가 된 것 같습니다.
구글에서 내놓은 앱시트 AppSheet 역시 노코드를 표방하는 개발 도구입니다. 단순히 노코드를 하는 것을 넘어서 데이터 기반으로 개발한다는 것을 강조하고 있습니다. 파이어베이스 Firebase 류의 Pass Storage를 바탕으로 개발 작업을 할 때 느꼈던 감성을 앱시트를 사용한 개발에서도 느낄 수 있는 것인가, 하는 기대감이 생기게 되더군요.
사실 원래부터 모든 어플리케이션, 소프트웨어, 서비스는 데이터 중심이었죠. 데이터가 없으면 잘 만든 UI도 아무짝에 쓸모 없습니다. 근래에는 데이터하면 결국 AI, ML로 이어지니 이런 맥락에서 앱시트는 포지셔닝을 하는 느낌입니다. 쉽고 빠르게 필요한 것을 데이터 기반으로 만들고 거기서 또 데이터를 추출해낸다?
가령 구글 시트에 위와 같은 간단한 테이블을 만들어 두고 앱시트를 통해서 Form 중심으로 CRUD 를 할 수 있는 앱을 만든다가 기본적인 컨셉입니다. 어차피 대부분의 어플리케이션이 DB에 대한 CRUD 라는 것에서 착안한 듯 합니다. 특히나 기업에서 사용하는 In-House 도구에서는 무척 유용할 것 같네요
노코드는 앱 에서 뿐만 아니라 머신러닝 쪽에서도 대세로 자리잡는 느낌입니다. 코드를 써야하는 과제와 쓰지 않아도 되는 과제의 분리를 통해 누구나 필요한 것을 만들어 쓸 수 있게 하는 방향성은 이제 대세가 된 것 같네요!
AWS의 CDN 제품인 CloudFront는 원본 서버에서 제공하는 기본 오브젝트를 지정할 수 있는 기능을 제공하고 있습니다. 보통은 원본 서버에서 사용하는 웹 서버에서 기본 오브젝트를 지정하는 경우가 많지만 S3 등 다른 제품을 함께 사용할 때는 기본 오브젝트를 지정할 수 있는 방법이 없기 때문에 파일명을 지정할 수 있는 기능이 필요합니다.
기본 루트 오브젝트 설정 Set Default Root Object
CloudFront Distribution의 기본 설정 탭에서 기본 루트 오브젝트를 설정할 수 있습니다. Distribution 목록에서 작업하려는 Dist ID를 선택하여 상세 화면으로 진입합니다. 상단 탭중 첫번째 탭인 General 탭으로 이동합니다. Settings 섹션의 `Edit` 버튼을 눌러 상세 설정 화면으로 진입해 봅니다.
Edit 화면의 중간 정도를 살펴보면 `Default root object - optional` 항목을 찾으실 수 있습니다. 이곳에 지정된 파일 이름이 CloudFront를 통해 접근시 사용되는 기본적인 파일 이름이 됩니다. 가령 www.iamnopd.com 이라는 도메인이 있다고 가정했을 때, 브라우저에 www.iamnopd.com 만 입력해도 알아서 index.html 을 찾아 사용자 브라우저로 응답을 하게 되는 거죠.
다양한 경로에 대한 기본 오브젝트 설정
그런데 문제가 있습니다. `Default root object`에 지정한 파일 이름은 진짜, 레알, 루트 경로에 대한 요청에 대해서만 적용되는 한계가 있습니다. 가령 원본이 S3 버킷이라고 했을 때, 버킷의 최상위 경로에도 index.html 이 있고, 버킷에 생성되어 있는 api 폴더의 하위에도 index.html 이 있다고 해봅시다.
원본의 서로 다른 경로를 특정 사용자 Path의 루트로 바인딩 하기 위해서는 (아시겠지만) 아래와 같이 동일 원본을 Origin Path만 분기하여 등록하면 됩니다. 이 구성과 Default root object 의 조합은 /api 경로에 대해서 적용되지 않는다는 상황으로 이해하지면 될 것 같습니다. 그러면 어떻게 하면 될까요?
CloudFront Function을 이용한 경로 조작
S3에서 뭔가를 하면 되지 않을까? 하고 생각했을지도 모르지만 그것보다는 CloudFront Function을 쓰면 간단하게 구현할 수 있습니다. 아시겠지만 Lambda@Edge를 쓰는 것보다 CloudFront Function을 쓰는 것이 사용도 간편하고(=기능이 다소 적고) 비용도 저렴합니다. 묻지도 따지지도 말고 바로 코드를 보겠습니다.
function handler(event) {
var pathFrom = /^\/api$/g
var pathTo = '/api/index.html'
var pathDecided = '';
var request = event.request;
pathDecided = request.uri;
if (pathDecided.match(pathFrom) != null) {
pathDecided = pathDecided.replace(pathFrom, pathTo);
}
request.uri = pathDecided;
return request;
}
코드는 무척 간단합니다. 간단한 Regex 를 이용하여 들어온 URI (request.uri)를 단순히 문자열 비교한 후에 실제로 원본이 받게될 URI 를 변경해서 return 해주는 코드입니다. 요청량이 많아도 크게 부담스러운 금액이 나오지는 않을것 같습니다. 간단하지만 Default root object가 단일 경로에 대해서만 적용되는 제약을 회피(?)하는 방법을 살펴봤습니다 :-)
테스트도 참 쉽죠? 원하는 결과를 테스트를 통해 얻었다면, 만든 함수를 적용하고자 하는 Distribution의 Behavior의 Viewer Request 함수로 지정하면 됩니다. 끝!
공식 문서의 taints 와 tolerations 에 대해서는 아래의 링크를 참고하면 된다. taints 는 노드에 적용되어 <수용 가능한 Pod 의 특성 정의>정도인 것 같고, tolerations 는 Pod 에 적용되어 <내가 동작할 수 있는 Node 를 선택하기 위한 특성 정의>라고 보면 될 것 같다.
노드에 설정된 taints 를 확인하기 위해서는 kubectl 에서 describe 옵션을 사용하면 된다.
Pod 을 생성할 때 배치된 Node 가 없다면 (이 경우에는 Taints 로 특정한 값이 부여된 Node 밖에 없다) Pod 이 생성되지 못하고 Pending 상태로 머무르며, 상세 에러 내용이 taints 와 tolerations 에 대한 내용이 노출된다.
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
mosquito 0/1 Pending 0 88s
$ kubectl describe pod mosquito
Name: mosquito
Namespace: default
Priority: 0
Node: <none>
...
...
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling 6s (x3 over 92s) default-scheduler 0/2 nodes are available: 1 node(s) had taint {node-role.kubernetes.io/master: }, that the pod didn't tolerate, 1 node(s) had taint {spray: mortein}, that the pod didn't tolerate.
Pod 을 Taints 가 지정된 Node 로 배포하기 위해서는 Pod 의 스펙에 관련된 값을 지정해 주어야 한다.