기본 콘텐츠로 건너뛰기

라벨이 개발인 게시물 표시

서버에서 Client IP 를 추출하는 여러가지 방법

서비스 요구사항에 따라 Client IP 가 필요한 상황이 있다. 보안을 위해서 Client IP 를 확인하여 접근을 허용할 수 있다. 허용되지 않은 IP 의 경우 접근을 막을 수 있다. 로그 요구사항으로 어떤 사용자가 접근하고 있는 지를 기록하기 위해 Client IP 를 남겨야 할 수 있다. 하지만 사용자나 서비스의 네트워크 구성에 따라서 Client IP 를 추출하는 것이 쉽지 않을 수 있다. 프록시가 있어 직접 연결한 Client 를 실제 사용자로 판단할 수 없는 경우가 그렇다. 프록시 뒤에 있는 사용자를 찾으려고 노력하면 Client IP 를 숨기거나 우회하기 위해서 변조를 시도하는 상황을 마주하게 된다. 그래서 Client IP 를 추출하기 위한 여러 방안들을 아래에 정리하게 되었다. 결론부터 먼저 말하면, 일반적인 상황에서 나는 `X-Forwarded-For` 의 가장 오른쪽의 Public IP 를 Client IP 로 판단하기로 했다. 믿을 수 없는 목록 중에서 신뢰할 수 있으면서 간단하고, 빠른 방법이라 생각하기 때문이다. 하지만 여러 방안들을 조사했을 때, 어떤 상황에서는 사용할 수 있는 지, 또 어떤 지점이 문제가 되는 지 생각해볼 수 있었다. 그래서 고민했었던 여러 방안들을 소개하려고 한다. ### Remote IP 프록시가 없는 간단한 구조의 서비스라면 서버에 연결된 Remote IP 를 Client IP 로 추출할 수 있다. 하지만 Remote IP 가 실제 사용자의 IP 라는 확신이 없다면, Client IP 로 사용하기 어렵다. 사용자 네트워크 구성에서 proxy 가 있다면, 서버에 연결된 Remote IP 는 실제 사용자의 Client IP 가 아닐 수 있다. 여기에서 서비스 요구사항에 대한 명확한 정의가 필요해질 수 있다. 사용자 네트워크의 사설 IP 를 추출해야 하는 지, 아니면 공인 IP 를 추출해야 하는 지 정의가 필요하다. 사설 IP 는 서비스의 입장에서 큰 의미가 없기 때문에, 보통 공인 IP...

NGINX module 개발 시 참고 하기 좋은 글

## 🙋 NGINX 모듈은 언제 만드나요? Nginx 는 많은 웹 서비스들에서 사용하는 웹 서버 프로그램 입니다. Nginx 는 그 자체로 강력하고, 다양한 모듈을 가지고 있기 때문에 많은 서비스들에서 사용됩니다. 하지만 서비스의 요구사항에 따라서, Nginx 그 자체만으로는 부족한 상황이 발생합니다. 앱 서버에서 처리하기에는 앱의 로직과는 동떨어져있어 책임원칙에 위반하고, nginx 의 설정 로직에서 처리되면 좋을 것 같은 요구사항들이 있습니다. 예를 들면, 프록시를 할 때 특정 값을 가공하여 넘겨주고 싶을 수 있습니다. 또, header 에 있는 값을 가지고 `$remote_addr` 를 변조하여 사용하고 싶다거나 하는 상황이 있습니다. 그런 상황에서 적절한 처리를 할 수 있는 nginx 모듈을 직접 만들어 nginx 에 적용해줄 수 있습니다. Nginx 는 C 언어로 만들어져 있어 모듈도 보통 C 로 작성됩니다. 하지만 최근에는 [Lua](https://github.com/openresty/lua-nginx-module#readme) 로 작성하거나 [njs (nginx 에서 사용하기 위한 일종의 custom js)](https://nginx.org/en/docs/njs/) 를 사용하여 모듈이 작성되기도 합니다. 하지만 이 글에서는 C 언어로 Nginx 모듈을 작성할 때 참고하기 좋은 글들을 소개합니다. ## Nginx 공식 문서 > https://nginx.org/en/docs/dev/development_guide.html 공식 문서는 어떤 상황에서든지 중요한 것 같습니다. 이미 만들어진 프로그램에 대해서 추가로 개발할 때는 설명서를 자세하게 살펴봐야 합니다. 공식 문서에서는 Nginx 에서 사용되는 자료형과 구조체, 변수, 로그 방식부터 Nginx 가 HTTP 관련 요청을 처리하는 방식 등 다양한 내용들이 기록되어 있습니다. 따라서 모듈을 개발하는 경우가 아니더라도, Nginx 를 깊게 이해하고 싶다면 한번 쯤 읽...

Docker Image 를 로컬 파일로 저장하기

컨테이너 이미지를 활용하여 빠르게 서버 환경을 구축하는 건 요즘 흔한 일이다. 다만 오프라인 환경에서는 외부 환경에서 이미지를 가져올 수 없기 때문에, 컨테이너 이미지를 활용하기 위해서는 파일로 변환하여 폐쇄망으로 자료 전송하는 등의 과정이 필요하다. ## 외부 image 를 로컬 파일로 저장 ``` ## (online) - image download docker pull {image}:{tag} --platform={platform} ## (online) - image -> tar package docker save {image}:{tag} > {image_filename}.tar ``` ## 로컬 파일 image 를 로드 ``` ## (offline) load image from package docker load -i {image_filename}.tar ## (offline) check image loaded docker image ls ```

특정 문자열을 파일에서 일괄 변경하기

## `fruit.txt`  파일 내에 있는 모든 `apple` 을 `banana` 로 변경 ``` # linux sed -i 's/apple/banana/g' /fruit.txt ``` mac 의 경우 sed 의 -i 에 extension 파라미터가 필요하다. extension은 변경하는 파일을 복사한 뒤 붙일 일종의 postfix 이다. 원본 문서를 저장해두지 않을 경우 빈칸으로 두자. ``` # mac sed -i '.old' 's/apple/banana/g' /fruit.txt ```

Python 에 특정 OpenSSL 버전 연동하여 설치

## 문제 urllib3 을 사용할 때, 다음과 같은 에러가 발생하면서 python 스크립트가 실행되지 않는다. ``` ImportError: urllib3 v2.0 only supports OpenSSL 1.1.1+, currently the 'ssl' module is compiled with 'OpenSSL 1.0.2k-fips 26 Jan 2017'. See: https://github.com/urllib3/urllib3/issues/2168 ``` 원인을 파악해보고자 구글링을 하니 다음과 같은 공식 가이드와 짤막한 해결방안이 있었다. 요약하면, urllib3 부터는 OpenSSL 1.1.1+ 버전만을 지원하므로, 서버에 설치되어 있는 openssl 버전을 업그레이드 하여야 한다. 제공된 가이드의 경우 RHEL7 의 경우 os 업그레이드를 하라는 배보다 배꼽이 다소 더 큰 가이드를 하고 있다. [https://urllib3.readthedocs.io/en/latest/v2-migration-guide.html#common-upgrading-issues](https://urllib3.readthedocs.io/en/latest/v2-migration-guide.html#common-upgrading-issues) ## 해결방안 python 을 업그레이드한 openssl 을 연동해서 직접 컴파일하여 설치해보자. (아래의 [stackoverflow 글](https://stackoverflow.com/questions/69371800/how-to-link-python3-to-use-openssl11-or-latest-version-of-openssl-1-1-1-on-c)을 참고하였습니다.) ### OpenSSL 설치 ``` # Install OpenSSL 1.1.1 cd /opt curl https://ftp.openssl.org/source/old/1.1.1/openssl-1.1.1o.tar....

다형성을 사용하도록 조건문 다시 작성하기

객체 지향 프로그래밍을 하다 보면, 필연적으로 상속 구조를 활용하게 되는데요. 이 때, 서비스 로직에서 실제 인스턴스 타입별로 어떤 코드 실행을 다르게 해주어야 하는 경우가 발생합니다. 고민하지 않고 단순히 인스턴스 별 조건분기문으로 코드를 작성하게 되면, 새로운 요구사항이 추가될 때마다 코드의 변경이 많아질 수 있습니다. 또 그런 변경들이 프로그램의 로직을 복잡하게 만들어낼 수 있는데요. 오늘은 이런 상황에서 조건문을 사용하지 않고, 다형성을 활용할 수 있는 방안에 대해서 살펴보겠습니다. ## 상황 - 새를 나타내는 `Bird` 클래스가 있습니다. 이 `Bird` 객체를 API 응답으로 내려주기 위해서는 `BirdResult` 객체로 변형되어야 합니다.BirdResult 는 내부 객체와 API 응답 필드들을 분리하기 위하여 사용하고 있는 DTO 입니다. - 이 `BirdResult` 를 생성하기 위해 `Builder` 를 사용하고 있습니다. - `Bird` 로부터 `BirdResult` 의 각 응답 필드를 세팅하는 메서드는 아래와 같습니다. ``` // BirdResult.java /** * Bird 값을 주입한다. (As-is) * @param bird * @return Builder */ public Builder bird(Bird bird) { this.id = bird.getId(); this.name = bird.getName(); this.type = bird.getType(); // ... return this; } ``` ## 새로운 요구사항 - `Bird` 는 이제 새로운 하위타입들로 나누어져야 합니다. (`Canary` , `Duck` , `MockingBird` ) - 각 하위 타입에 따라서 `BirdResult` 에 채워주어야 할 필드가 달라...

빌더 패턴을 사용한 객체의 생성

빌더 패턴은 객체를 생성할 때 쓰이는 유용한 패턴입니다. 하지만 빌더에 대한 추가적인 구현이 필요해서 인지, 생각보다 많은 사람들이 빌더가 필요한 상황에서도 복잡한 생성자를 사용해서 객체를 생성합니다. 그리고 그에 대한 댓가로, 개발자의 파라미터 입력 실수에 의한 오류를 접합니다. 이 글에서는 Java 환경에서 생성자와 점층적 생성자, 그리고 빌더에 대해서 알아봅니다. 그리고 계층적 클래스 구조에서 빌더를 사용하는 방법에 대해서 설명하고자 합니다. ## 객체의 생성 빌더에 대해서 이야기 하기 전에, 먼저 객체를 생성하는 여러가지 방법에 대해서 알아보겠습니다. ### 생성자 가장 많이 사용하는 방법은 생성자를 통해 객체를 생성하는 것입니다. 생성자는 정의되어 있는 class 를 기반으로 객체를 생성하는 기본적인 방법이다. `new Class(…)` 형태로 직관적이고, class 내에 정의만 하면 되기 때문에 군더더기 없습니다. 하지만 객체가 가지고 있는 속성이 많을 경우에는 이야기가 좀 달라집니다. java 의 생성자는 입력 받는 파라미터를 순서와 파라미터의 클래스(Class) 또는 원시 타입(primitive type)으로 구분합니다. 이 때 사용자는 다음과 같이 객체를 생성해야 합니다. ``` new Something(id, weight, height, message, pages, rate); ``` 이렇게 되었을 때, `weight` 와 `height` 의 순서가 변경된다면 어떤 일이 발생할까요? 아마 `Something` 객체는 개발자가 의도한 것과는 정반대로 생성되어 프로그램의 동작에 문제가 발생할 것입니다. 실제로 개발하면서 이런 문제를 많이 겪습니다. 이런 문제는 변수명과 순서를 매핑할 수 없는 언어적인 한계에서 발생합니다. (python 에서는 명명된 선택적 매개변수를 지원하기 때문에 이런 문제가 없습니다.) ### 점층적 생성자 일반적인 생성자를 사용할 때 파라미터가 길어지는 문제점을 방지하기 위해서 점층적 생성자를...

안드로이드 native c++ 컴파일 관련 정리

# 툴체인 선택 표 1. 다양한 명령 집합에 대한 APP\_ABI 설정 | 아키텍처 | 툴체인 이름 | | ---------- | ------------------------------------ | | ARM 기반 | arm-linux-androideabi-**{gcc-version}** | | x86 기반 | x86-**{gcc-version}** | | MIPS 기반 | mipsel-linux-android-**{gcc-version}** | | ARM64 기반 | aarch64-linux-android-**{gcc-version}** | | X86-64 기반 | x86\_64-**{gcc-version}** | | MIPS64 기반 | mips64el-linux-android-**{gcc-version}** | # Sysroot 선택 ``` SYSROOT=$NDK/platforms/android-21/arch-arm ``` # 컴파일러 호출 ## 간단한 호출 다음은 NDK 내에 미리 빌드 되어있는 `arm-linux-androideabi-4.8` 툴체인을 이용한 빌드 방법이다. ``` export CC="$NDK/toolchains/arm-linux-androideabi-4.8/prebuilt/ \ linux-x86/bin/arm-linux-androideabi-gcc-4.8 --sysroot=$SYSROOT" $CC -o foo.o -c foo.c ``` 이 방법에서는 C++ STL (STLport, libc++ 또는 GNU libstdc++)을 사용할 수 없습니다. 예외나 RTTI가 지원되지도 않는다. ## 고급 방법 NDK는 명령줄에서 사용자 지정 툴체인 설치를 수행할 수 있는 `make-standalone-toolchain.sh` 셸 스크립트를 제공합니다. `$NDK/build/tools/` 디렉터리에 있으며, 여기서 $NDK는 NDK의 설치 루트 디렉터리입니다. ``` $NDK/bui...