위키 엔진을 만들던 중 CIDR 차단 기능을 구현해야 하는데, 이걸 어찌할지 몰라 한참 고민하고 있었다. PHP 상에서 처리를 해야 하나 고민했지만 해답은 SQL에서 찾을 수 있었다.
문제 상황
나는 MariaDB를 운용하고 있었고, 테이블의 각 행에는 차단 대상으로 지정된 CIDR들이 등록되어 있었다. | id | target_ip | |—-|——————| | 1 | 1.2.3.4/32 | | 2 | 123.45.67.0/24 | | 3 | 123.45.0.0/16 | | 4 | 0.0.0.0/1 |
그리고 쿼리를 통해 접속한 IP가 차단 대상에 해당하는지 확인해야 한다. 그래서 특정 IP 주소를 포함하는 CIDR이 테이블 안에 있는지를 살펴야 한다.
검색 끝에 아래와 같은 쿼리를 찾았다. 원래는 CIDR로부터 범위를 추출하는 쿼리였지만 잘 개조해서 아래처럼 ‘대상 IP’를 포함하는 CIDR들을 추출할 수 있는 쿼리를 만들었다.
대상 IP를 123.45.67.8로 지정한 쿼리 결과: | id | | 2 | | 3 | | 4 |
보통 PHP에서 한글 글자수를 센다던가, substr을 통해 자른다던가, 한글자씩 분리한다던가 할 때 php-mbstring 확장을 사용한다.
만약 자신이 만들고 있는 PHP 앱이 빠른 속도를 요구한다면, mbstring 확장을 떨쳐 내는 것은 이를 위한 좋은 방법이 될 수 있다. 실제로 해외의 여러 커뮤니티에서도 mb_strlen() 등은 매우 느리다고 이야기하고 있고, 그래서 구글링 끝에 얻은 대안 두 개를 소개한다.
javascript에는 .startsWith()와 .endsWith() 라는 함수가 각각 시작과 끝 문자열을 반환해주는 기능을 한다. 그런데 PHP에는 이게 없어서 직접 기능을 구현해야 했다.
이 함수는 PHP 8에서 새롭게 추가된 기능인데, 문자열이 특정 문자로 시작하거나 끝나는지의 여부를 bool 형태로 반환한다.
str_starts_with( string $haystack, string $needle )nstr_ends_with( string $haystack, string $needle )nn// 예제n$a = 'abcdefghi';nif(str_starts_with($a, 'a')){n echo 'Starts with a.';n}else{n echo 'NOT Starts with a.';n}nnif(str_starts_with($a, 'b')){n echo 'Starts with b.';n}else{n echo 'NOT Starts with b.';n}n// Starts with a. NOT Starts with b. 가 출력된다.nn// 예제n$a = 'abcdefghi';nif(str_ends_with($a, 'i')){n echo 'Ends with i.';n}else{n echo 'NOT Ends with i.';n}nnif(str_ends_with($a, 'h')){n echo 'Ends with h.';n}else{n echo 'NOT Ends with h.';n}n// Ends with i. NOT Ends with h. 가 출력된다.
처음 웹 개발을 시작한 이후, 도메인 관리를 위해 오랫동안 무료 서비스인 DNSZi를 써 왔다. 이후, CAA 레코드와 DNSSEC 등을 적용할 마음이 생겨 AWS Route53이나 Cloudflare, DigitalOcean, Linode 등을 전전하다가 LuaDNS라는 곳을 찾게 되었다. 그래서 내가 지금까지 애용하고 있는(혹은 있던) 두 서비스를 소개하고자 한다.
PHP 코딩하는 사람은 한 번쯤 들어봤을 법한 PHPSCHOOL.com 과 동일한 기업에서 운영하고 있다. 이 사이트는 그래도 국내에서 무료 DNS 서비스로 유명한 편이다. 지원 레코드: A, CNAME, MX, TXT, SRV 기타 서비스: 웹 파킹, 웹 포워딩, DDNS
일단 회원가입을 하고 도메인을 추가하면 아래 사진처럼 네임서버가 배정된다.
5차 네임서버까지 모조리 지원하기 때문에 도메인 설정에서 네임서버 지정란을 꽉 채울 수 있다. 네임서버 번호(ns**.dnszi.com)는 도메인마다 랜덤하게 배정된다.
여기 보이는 상단 메뉴에서 원하는 기능을 선택해 설정할 수 있다. 각 메뉴마다 설정에 관한 자세한 설명이 적혀 있어 초심자도 쉽게 이용할 수 있다.
또한 TTL 관리가 가능한데, 아래 사진처럼 선택이 가능하다.
만약 도메인 기본 TTL에서 설정한 값을 동일하게 서브 도메인에 적용시키고 싶다면 서브 도메인의 TTL 설정을 ::TTL:: 로 하면 된다.
만약 DNS 레코드의 설정을 변경하고 싶다면, 설정값을 수정한 후 해당 레코드 우측에 있는 ‘E’ 버튼을 누르면 된다. (오른쪽에 X는 삭제버튼) 복수의 레코드를 동시에 수정하려면 수정한 각 레코드의 왼쪽 체크박스를 체크한 후, 아래쪽의 ‘선택수정’을 누르면 된다.
만약 자신의 서버가 필요로 하는 기능이 단순 ip연결과 같은 기본적인 기능이라면, DNSZi를 이용하면 된다. 레코드 수, Zone 수 등에 제약을 받지 않고 무제한으로 이용할 수 있기 때문이다.
지원 레코드: A, AAAA, ALIAS, CAA, CNAME, DS, FORWARD, MX, NS, REDIRECT, SLAVE, SPF, SRV, SSHFP, TLSA, TXT 이 서비스의 강력한 장점은 바로 위와 같이 많은 종류의 레코드를 지원한다는 것이다. 물론 AWS Route53이나 Cloudflare가 더 많은 레코드를 지원했던 것으로 기억하지만, 이쪽은 유료이거나 기능 제약이 많아서 필자에게는 적합하지 않았다.
이 녀석은 해외 서비스이다. 일단 회원가입부터 하자. 로그인해서 zones 메뉴를 들어가 보면 이런 식으로 있을 것이다. (필자는 사전에 도메인을 등록해 두었다.)
사진 우측에 ‘Add Zone’ 이라는 걸 누르면 도메인을 추가할 수 있다.
맨 위에 도메인을 적고, ‘Add Zone’을 누르자. 아래의 템플릿이나 레코드 복사 설정은 안 해도 된다.
그러면 이제 위 사진처럼 레코드 목록이 뜰 것이다. 사진에는 잘렸지만 왼쪽 하단에 보면 ‘Add New Record’ 라는 게 있다. 그걸 누르면 레코드가 추가된다. host name 칸은 루트 도메인에 적용시키려면 빈 칸으로, 서브도메인에 적용시키려면 서브도메인 이름만 입력해주면 된다. (ex: a.abc.kr → ‘a’ 만 입력)
다 지정했으면 우측 하단의 Save를 누르자.
성공적으로 업데이트 되었다면 위와 같은 메시지가 뜬다. 참고로 저장이 완료되면 SOA – NS – (지정 레코드) 순으로 정렬이 되는데, 레코드 종류가 먼저 알파벳 순으로 정렬되고 그 다음 Name의 알파벳 순으로 정렬된다. 아래는 저장이 안 된 경우 뜨는 오류 메시지들이다. invalid (ipv4/ipv6) address : A, AAAA 레코드의 Content에 잘못된 ip 주소를 기입한 경우 content: bad {내용}: {지정값} : {내용} 의 값이 잘못 지정된 경우 records: Found other records with (도메인). delegation, it should contain only NS and DS records : 한 도메인에 대해 DS 레코드를 여러 개 지정한 경우 Max records reached, please access account settings page to upgrade your package : 사용 가능한 레코드 수를 초과한 경우 (무료 서비스는 30개까지다. 레코드 왼쪽의 번호는 0부터 시작하므로 참고하자.)
양식을 틀려먹은 경우 아래와 같은 오류 페이지로 넘어가 버린다. 이 경우 수정한 내용을 모두 재 입력해야 하는 참극이 발생하므로 조심하자.
2-2. 요금제와 고급 기능
그런데, 위에서 눈치챘겠지만 서비스 자원을 무한하게 이용할 수 있는 건 아니다. 회원가입을 하면 무료 계정으로 등록되며, 3개의 도메인, 각 30개의 레코드를 사용할 수 있다. 기본으로 SOA와 NS 레코드 4개가 지정되므로 사실상 25개만 사용 가능한 것이다.
그래서 위 사진처럼 요금제가 있다. Zones 수 = 도메인 수 라고 봐도 무방하다. 유료 서비스는 개인의 필요에 따라 적절히 구매하면 되는데, 어지간히 큰 사이트가 아니면 굳이 살 필요는 없다.
그러면 왜 이 서비스를 이용하냐고? 그것은 이제 아래에 서술할 고급 기능 때문이다.
고급 기능 1. DNS API
LuaDNS는 DNS API를 지원하는데, 이게 무엇이냐 하면 저 사이트 외부에서 DNS 정보를 변경할 수 있도록 하는 것이다. 처음 보는 사람은 그래서 이게 왜 필요하냐고 묻곘지만 API를 제공하는 DNS 서비스를 특별히 찾는 이유는 바로 무료 wildcard 인증서를 자동으로 업데이트할 수 있기 때문이다.
여기서 wildcard 인증서가 무엇인지 간단히 설명하자면, 원래 도메인의 SSL 인증서는 각 도메인마다 따로 받아야 한다. 예를 들어 abc.kr,a.abc.kr,www.abc.kr 등 서브 도메인을 포함해서 모든 도메인이 각각 별개의 인증서를 발급받아야 하는 것이다. 그런데 이를 일일이 발급하고 관리하기란 매우 까다로운 일이다. 그래서 나온 게 모든 서브도메인에 대응할 수 있는 wildcard 인증서이다. *.abc.kr 의 인증서 하나로 a.abc.kr. b.abc.kr 등 모든 서브도메인의 인증서를 갈음할 수 있다. (물론 루트 도메인인 abc.kr은 따로 발급받아야 한다.)
그런데 Let’s Encrypt에서 발급한 인증서는 3개월이면 유효기한이 지나 쓸 수 없게 된다. 그래서 이를 자동으로 재발급 해 주는 프로그램이 있는데, 바로 acme.sh 라는 프로그램이다. 이 프로그램에 대한 자세한 사용법은 여기를 참고하자.
이 서비스는 보통 맨 처음에 언급했던 Cloudflare과 같은 대형 기업에서 주로 제공하는데, 굳이 거기를 놔두고 여기로 오는 이유는 freenom 같은 곳에서 발급하는 무료도메인은 Cloudflare를 이용할 수 없기 때문이다. 필자는 기능에 제약이 너무 많아서 이 서비스를 이용하기 시작했다.
위에서 봤던 Zones 메뉴에서 저 빨갛게 동그라미 친 버튼을 누르면 DNSSEC 설정 페이지로 이동한다.
위에서 DNSSEC에 체크하고 Save를 누르면 아래 부분처럼 DNSEC(???) 메뉴가 나타난다. 여기까지 왔으면 DNS 서비스에서 할 일은 끝난 것이다. 루트 도메인에 대한 DS 레코드는 자동으로 적용되므로 따로 수정할 필요가 없다. 저기서 Key Tag, Algorithm, Digest Type, Digest와 저 빨간 원 안의 here를 누르면 나타나는 Public Key의 값을 들고 당신의 도메인 등록기관에 찾아가서 등록을 요청하자.
필자는 호스팅케이알을 이용하고 있어서 1:1 문의를 통해 등록했다. 위 사진처럼 알려주면 된다. DNSSEC 등록 방법은 도메인 기관마다 다르니 참고하자.
이렇게 두 서비스를 간단히 소개해 보았다. 본인의 서비스가 방금 소개한 두 고급 기능이 필요하다면 LuaDNS, 그렇지 않다면 DNSZi를 사용하면 되겠다.
$cost = 100;n$cost += 10; // $cost = $cost + 10; 과 같은 계산necho $cost;n// 110이 출력된다.nn++$cost; // $cost의 값을 1 더하여 111 출력n--$cost; // $cost의 값을 다시 1 빼서 110 출력nn$pre = 'abc';n$suf = 'def';n$pre .= $suf; // $pre = $pre + $suf; 와 같은 계산n// abcdef가 출력된다.
위와 같이 활용 가능하다.
5. 문자열과의 혼합
문자열과 함께 쓸 수도 있다.
$v = '나는';nnecho $v.' 글을 씁니다.';necho "$v 글을 씁니다."; // 큰따옴표만 사용해야 함.n// 둘 다 '나는 글을 씁니다' 가 출력된다.nn$x = '글을';nnecho "나는 ".$x." 씁니다.";necho "나는 $x 씁니다."; // 큰따옴표만 사용해야 함.n// 둘 다 '나는 글을 씁니다' 가 출력된다.nn$z = '밥';nnecho "잡곡".$z;necho "잡곡{$z}";n// 둘 다 '잡곡밥' 이 출력된다.