오늘은 근래 종종 발행하는 Nginx 웹서버 구동 실패 원인, 98: Address already in use을 살펴보고 Nginx 웹서버에서의 서버 이슈 해결 방법에 대해서 살펴보도록 하겠습니다.
근래들어 nginx 웹서버 시작 시 에러가 나면서 구동에 실패하는 경우가 많아졌습니다. 대부분 원인은 nginx 웹서버가 사용하려는 80포트가 이미 사용중이기 때문이라고 합니다.
더 정확한 에러 메세지는 nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)인데요.
여기에서는 왜 이런 상황이 발생하면서 nginx 작동 시작을 실패하는지 살펴보면서 문제 해결 방법엔 무엇이 있는지 살펴보도록 하겠습니다.
Nginx 웹서버 작동 실패 메세지
아래는 오늘 제 서버가 작동이 되지 않아 systemctl status nginx.service 명령으로 확인하니 아래와 같은 메세지가 뜹니다.
:~# systemctl status nginx.service
● nginx.service - nginx - high performance web server
Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled)
Active:<strong> failed</strong> (Result: exit-code) since Mon 2020-07-2718:58:53 KST; 11s ago
Docs: http://nginx.org/en/docs/
Process: 34135 ExecStart=/usr/sbin/nginx -c /etc/nginx/nginx.conf (code=exited, status=1/FAILURE)
Jul 2718:58:51 happist.com systemd[1]: Starting nginx - high performance web server…
Jul 2718:58:51 happist.com nginx[34135]: nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
Jul 27 18:58:51 happist.comnginx[34135]: nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Addressalreadyinuse)
Jul 27 18:58:52 happist.comnginx[34135]: nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Addressalreadyinuse)
Jul 27 18:58:52 happist.comnginx[34135]: nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Addressalreadyinuse)
Jul 27 18:58:53 happist.comnginx[34135]: nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Addressalreadyinuse)
Jul 27 18:58:53 happist.comnginx[34135]: nginx: [emerg] stillcouldnotbind()
Jul 27 18:58:53 happist.comsystemd[1]: nginx.service: Controlprocessexited, code=exited, status=1/FAILUREJul 27 18:58:53 happist.comsystemd[1]: <strong><spanstyle="color: #fcb900;" class="ugb-highlight">nginx.service: Failed with result 'exit-code'.</span></strong>
Jul 27 18:58:53 happist.com systemd[1]: <strong><span style="color: #cf2e2e;" class="ugb-highlight">Failed to start nginx - high performance web server.</span></strong>Code language:PHP(php)
이러한 현상은 주로 서버를 다시 부팅 시키는 경우 종종 나타납니다. 한번 구동에 성공한 경우엔 service nginx restart와 같이 nginx를 다시 구동시키는 경우 아직 문제를 발견하지는 못했습니다.
이러한 현상은 예전 우분투 18.04를 사용하던 시절에는 거의 나타나지 않았지만 우분투 20.04로 업그레이드 한후에 종종 나타나는 현상인 것을 보니 우부투 20.04의 일시적인 현상으로 추정합니다.
단기 해결 방안
이렇게 서버를 재구동시킬시 80 포트르 다른 어플리케이션이 선점하는 문제는 아래와 같은 apache2 사용 중지시킴으로써 대부분 해결 가능했습니다.
즉 아래와 같은 명령을 활용합니다.
sudo /etc/init.d/apache2 stop
sudo service nginx restartCode language:PHP(php)
이렇게 apache2 stop 명령으로 대부분 문제는 해결됩니다.
장기적이고 근원적인 문제
이렇게 서버가 새롭게 부팅 문제가 발생 확률이 높아지면 서버 운영 시 위험도가 크게 높아집니다.
서버 운영 시 서버를 자동으로 리부팅 시키는 경우가 있을 수 밖에 없습니다. 예를들어 중요한 보안 업그레이드가 발생 시 보안 업데이트가 적용되기 위해서는 서버 리부팅이 필수적입니다.
그런데 서버 리부팅 시 웹서버를 구동시킬 수 없는 문제가 발생한다면 서버 운영에 큰 지장을 받을 수 있고 안정적인 서버 운영에 어려워집니다.
그러면 왜 이런 문제가 발생하는 것일까요?
구글링을 통해서 왜 이런 문제가 발생하는지 그 원을 찾아 보았지만 뚜렸한 답을 찾을 수는 없었습니다. 다만 아래와 같은 몇가지 요인을 추정할 수는 있습니다.
80 포트를 다른 어플리케이션이 선점하는 문제는 apache2와 연관이 깊다. 이렇게 판단하는 이유는 apsche2 stop 명령어로 대부분 문제가 해결되기 때문이다.
예전엔 이런 문제가 많지 않았지만 근래 Certbot이 업그레이드되면서 이런 문제 발생이 증가했다고 이야기 있다. Certbot 설치하면서 알게 모르게 apache2를 구동시키는 경우가 있는 게 아닐까하는 합리적인 의심을 가져 본다.
결국 Nginx 웹서버를 사용하고는 있지만 기본적으로 apache2를 설치되어 있고, 어떤 연유에서인지 모르지만 apache2가 먼저 구동되면서 80포트를 차지하는 것이 아닐까 싶습니다.
그러면 이 apache2를 어떻게 해야할까요? 여기에는 2가지 방법이 있다고 보여집니다.
apache2 포트를 80이 아닌 다른 것으로 변경
첫번째 방법으로 apache2가 사용하는 port를 80아 아닌 다른 포트로 변경하는 것입니다.
어째피 Nginx 웹서버에서는 apache2를 사용할 일이 거의 없고 apache2와 nginx가 포트를 공유할 수 없도록 apache2 포트를 변경해주면 될 듯 합니다.
apache2 포트 설정은 /etc/apache2 폴더에 있는 ports.conf 파일에서 정의하고 있습니다.
# If you just change the port or add more ports here, you will likely also# have to change the VirtualHost statement in# /etc/apache2/sites-enabled/000-default.conf
Listen 70# 80에서 70으로 변경
<IfModule ssl_module>
Listen 443
</IfModule>
<IfModule mod_gnutls.c>
Listen 443
</IfModule>
# vim: syntax=apache ts=4 sw=4 sts=4 sr noetCode language:PHP(php)
apache2를 삭제, 온전히 nginx만으로 운영
아니면 nginx 웹서버에서 apache2는 거의 사용하지 않기 때문에 이를 삭제하는 것입니다.
그러면 아래와 같이 apache2와 관련된 패키지들이 삭제되며 73메가 정도 용량을 확보할 수 있다는 메세지를 내면서 계속할지 질문을 합니다.
~# sudo apt-get --purge autoremove apache2
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following packages will be REMOVED:
apache2* apache2-data* apache2-utils* libllvm9* lockfile-progs* sendmail-base* sendmail-cf* sensible-mda*
0 upgraded, 0 newly installed, 8 to remove and1 not upgraded.
After this operation, 73.0 MB disk space will be freed.
Do you want to continue? [Y/n]Code language:PHP(php)
계속한다는 의미에서 Y를 누르면 삭제 작업이 진행됩니다.
(Reading database ... 145477 files and directories currently installed.)
Removing apache2 (2.4.41-4ubuntu3) ...
Removing apache2-data (2.4.41-4ubuntu3) ...
Removing apache2-utils (2.4.41-4ubuntu3) ...
Removing libllvm9:amd64 (1:9.0.1-12) ...
Removing sendmail-base (8.15.2-18) ...
Cleaning up the queues...done.
Removing lockfile-progs (0.1.18) ...
Removing sendmail-cf (8.15.2-18) ...
Removing sensible-mda (8.15.2-18) ...
Processing triggers for man-db (2.9.1-1) ...
Processing triggers for libc-bin (2.31-0ubuntu9) ...
(Reading database ... 144728 files and directories currently installed.)
Purging configuration files for sendmail-cf (8.15.2-18) ...
Purging configuration files for apache2 (2.4.41-4ubuntu3) ...
Purging configuration files for sendmail-base (8.15.2-18) ...
Processing triggers for systemd (245.4-4ubuntu3.2) ...
Code language:PHP(php)
이렇게 apache2가 모두 삭제되면 nginx를 다시 구동합니다.
sudo service nginx restartCode language:PHP(php)
마치며
그러나 이렇게 apache2를 삭제하고도 이런 문제가 몇번 더 발생했기 때문에 위에서 제시한 apache2 삭제는 해결책은 아닌 것으로 판단하고 있습니다.
또한 아래와 같이 apache2 실행을 중단 시키는 것도 먹히지 않는 경우를 만났기 때문에 완전한 해결책도 아닌 것으로 판단 합니다.
sudo /etc/init.d/apache2 stop
sudo service nginx restartCode language:PHP(php)
그래서 최종적으로는 fuser 명령어로 80포트 사용된 메모리를 전부 죽여버리고 다시 nginx를 실행하는 방법을 사용하기로 했습니다.
워드프레스를 오랫동안 운영하다보면 워드프레스 데이타베이스에 불필요한 자동 실행 데이타(wp-options Table)들이 증가해 워드프레스 속도를 지연시키게 됩니다. 그렇게 때문에 워드프레스 데이타베이스에서 이런 자동 실행 옵션(wp-options Table)들을 최적화해 워드프레스 속도 개선이 가능합니다.
이렇게 워드프레스 속도를 느리게 하는 것 중의 하나가 워드프레스 테마와 플러그인을 설치하면 자동 실행되도록 만드는 Autoloaded Data들입니다.
이들 Autoloaded Data들은 워드프레스 옵션 테이블((wp-options Table)에 저장되어 테마나 플러그인이 삭제되어도 없어지지 않고 계속 자동 실행되기 때문에 일정 시간이 지나면 워드프레스 속도에 큰 영향을 미치게 됩니다.
보통 테마나 플러그인을 삭제했다만 이것으로 모든 것이 해결된 것으로 알고 안심하지만 실상은 여전히 워드프레스 데이타베이스에 남아 자동 실행되면서 성능에 영향을 미칩니다.
오늘은 워드프레스 데이타베이스 중 옵션 테이블 항목을 살펴보고 여기에서 자동으로 실행되는 Autoloaded Data를 최적화하는 방법에 대해서 알아보도록 하겠습니다.
워드프레스 옵션 테이블(wp-options Table)과 문제점?
워드프레스 옵션 테이블(wp-options Table)은 워드프레스 데이타베이스 중에서 사이트 주소(Site URL), 홈 주소(Home URL), 관리자 이메일, 기본 카테고리, 시간 설정 기준 등의 정보를 저장하고 있는 테이블입니다.
그리고 여기에는 플러그인과 테마에 대한 정보도 모두 들어 있습니다.
사이트 주소(Site URL)
홈 주소(Home URL)
관리자 이메일
기본 카테고리
시간 설정 기준
워드프레스 데이타베이스 테이블 리스트 그리고 옵션 테이블(option table)
이 워드프레스 옵션 테이블(wp-options Table)에는 아래와 같은 4가지 필드가 있습니다. 이 필드 중 autoload는 워드프레스 사이트가 구동되면 자동으로 실행 시킬 것인지를 정합니다.
option_id
option_name
option_value
autoload
이러한 워드프레스 데이타베이스의 옵션 테이블은 아래와 같은 이유로 과도하게 데이타가 실행되는데 실상 많은 데이타 처리용으로 설계되지 않았기 때문에 효율이 떨어집니다.
옵션 테이블의 autoload 설정을 살펴보면 알겠지만 100% 전부 autoload하겠다고 설정되어 있습니다.
시간이 지날수록 워드프레스는 더 많은 양의 데이타를 자동으로 실행 합니다.
왜냐하면 현재 사용중인 테마나 플러그인을 자동실행하지 말도록 조정해도 자동으로 실행시켜버립니다.
더우기 과거에 사용하다 지워버린 테마나 플러그인에서 설정한 옵션도 자동 실행됩니다.
테마나 플러그인 개발자들은 플러그인이나 테마를 위한 별도 테이블생성대신 워드프레스 옵션 테이블에 데이타를 저장하는 것을 선호합니다. 그러다보니 옵션 테이블에서 처리하는 데이들이 많아졌습니다.
그러나 워드프레스 데이타베이스 중 옵션 테이블은 원래 테마나 플러그의 그렇게 많은 세부 데이타를 처리할 수 있을 정도로 효율적으로 설계되지는 않았습니다.
위와 같은 이유로 워드프레스 데이타베이스 내의 옵션 테이블은 적절하게 최적화해야 더 빠른 워드프레스 사이트를 만들 수 있습니다.
옵션 테이블에서 자동 실행되는 내용 파악하기
우선 어느 정도 자동 실행되는 데이타가 있는지 확인해 보시죠. 사이트 운영에서 기본은 발생되는 데이타를 최소화시켜 물리적으로 속도를 빠르게 만드는 것입니다.
옵션 테이블에서 자동실행 데이타 총량 확인
워드프레스 사이트에서 자동 실행되는 데이타가 어느 정도인지 확인은 phpMyAdmin에서 아래와 같은 명령을 사용해 알 수 있습니다.
SELECT SUM(LENGTH(option_value)) as autoload_size FROM wp_options WHERE autoload='yes';Code language:PHP(php)
데이타베이스 이름 클릭
상단 메뉴 중 SQL 메뉴 크릭
쿼리문 입력란에 위 명령어 입력
실행
저의 경우 자동 실행되는 데이타 양이 470397바이트가 나왔네요. 이 정도는 다른 사이트들보다 훨씬 더 많은 데이타가 실행된다고 평가할 수 있습니다.
제가 플러그인 10개이상 설치한 워드프레스 사이트의 초기 자동 실행 데이타를 살펴보니 기껏해야 10만 바이트가 넘는 정도이니 40만이 넘는 저의 경우는 굉장히 많은 데이타가 자동으로 실행되고 있다는 것을 알 수 있습니다.
어떤 데이타들이 자동 실행될까?
그러면 자동 실행되는 데이타들이 무엇일까요? 자동 실행되는 데이타를 줄이려면 무엇을 조정해야 할까요?
이를위해서 자동 실행되는 데이타양을 기준으로 상위 30개를 추출해 보면서 무엇을 빼야하는 지 살펴보죠. 자동 실행 데이타 Top30을 보여주는 명령어는 아래와 같습니다.
SELECT option_name, length(option_value) AS option_value_length FROM wp_options WHERE autoload='yes' ORDER BY option_value_length DESC LIMIT 30;Code language:PHP(php)
이 명령어를 기반으로 아래 이미지와 같은 결과를 얻었습니다. 이 데이타들을 살펴보면서 아래와 같은 문제점을 찾았습니다.
3위에 랭크된 redux_builder_amp는 amp 플러그인 관련된 내용으로 거의 1년전에 amp 플러그인을 삭제했는데 여전히 자동 실행되고 있습니다.
4위에 랭크된 td_011은 사용하지 않는 뉴스페이퍼 테마관련 내용으로 이도 거의 1년전부터 사용하지 않고 있는 테마입니다.
5위에 랭크된 lyte_cache_index은 유튜브 로딩 속도를 개선해주는 플러그인으로 이도 오래전부터 사용하지 않는 플러그인입니다.
9위에 랭크된 theme_mods_marni도 오래전부터 사용하지 않았던 marni 테마 관련 내용입니다.
따라서 적어도 amp 플러그인, 뉴스페이퍼 테마, 유튜브 속도 개선 플로그인 그리고 marni 테마 관련 내용을 지워버릴 필요가 있습니다.
옵션 테이블에서 상세 내용 삭제 방법
위에서처럼 워드프레스 데이타베이스 옵션테이블을 분석해보니 사용하지 않는 테마나 플러그인을 위해서 자동 실행되는 데이타들이 많다는 것을 알 수 있습니다.
phpMyAdmin에서 이들을 효과적으로 찾아 삭제하는 방법은 아래 명령을 사용해 찾을 수 있습니다. 아래는 amp 관련 내용을 모두 찾겠다는 명령입니다.
SELECT *
FROM `wp_options`
WHERE `autoload` = 'yes'AND `option_name` LIKE '%amp%'Code language:PHP(php)
그러면 아래와 같이 amp 관련 항목을 전부 보여줍니다. 이를 모두 선택해 지우면 되겠죠.
뉴스나 정보를 제공하는 사이트들은 대개 유료 구독 모델, 광고 모델 그리고 기부 모델로 나누어 볼 수 있습니다. 이 세가지 비지니스 모델은 독자적으로만 존재하는 것이 아니라 특성을 적절하게 섞어서 비지니스 모델 성과를 높이고 있습니다.
예를들어 유료 구독 모델이지만 부분적으로 광고를 실기도 하고, 광고를 주로 하면서 일정 부분만 유료 구독 모델로 운영하기도 합니다.
그냥 개인의 돈을 들여 운영하는 사이트도 있지만 어느 정도 규모를 가지고 목적에 맞추어 커스터마이제이션해 운영하려면 정기적인 비용이 적지않게 듭니다.
사이트를 운영하는 비용도 당연히 들겠지만 콘텐츠를 생산하고 유지하는데 드는 비용은 경우에 따라서는 어마어마한 비용이 드는 것이 사실입니다.
인터넷 시대에 콘텐츠는 무료라는 인식이 강하지만 점점 읽을만한 콘텐츠는 점점 유료화되고 있습니다.
왜냐하면 콘텐츠는 그냥 자연적으로 만들어지는 것이 아니라 누군가의 땀과 노력으로 만들어지기 때문이고, 이러한 품질 좋은 콘텐츠를 지속적으로 제공하려면 콘텐츠 제작자들에게 충분한 보상이 주어져야 장기적으로 안정적으로 품질좋은 콘텐츠를 제공할 수 있는 것이죠.
우리나라는 아직 덜하지만 점점 외국의 경우 어지간한 언론사들은 유료 구독모델로 변경된지 오래입니다.
광고, 구독, 기부가 적절히 활용된 4가지 구독 옵션을 제공
이러한 트렌드들보다도 먼저 유료 구독 모델을 운영한 곳이 LWN이라고 할 수 있는데요. LWN은 아래와 같이 광고와 유료 구독 그리고 기부 모델을 적절하게 사용하고 있습니다.
가장 비용이 낮은 starving hacker에게는 광고 모델과 구독 모델을 혼용해 사용하고 있으며.
가장 많은 돈을 내는 maniacal supporter은 구독 모델과 기부 모델을 적절해 활용한 구독 타입
구독 타입
월 구독료
광고 여부
접근 가능 콘텐츠
starving hacker
3.5$
O
모든 콘텐츠 접근 가능 정기 결제 불가 (비용 문제)
professional hacker
$7
X
모든 콘텐츠 접근 가능 정기 결제 가능 작성자별 댓글 필터링 댓글 답변 이메일 수신
professional hacker
$14
X
모든 댓글 이메일 통보 새 댓글 하일라이트 기능
maniacal supporter
$50
X
댓글에 서포터 표시 컨퍼런스에서 맥주/음료 제공
이렇게 광고와 구독 그리고 기부가 적절한 게 활용된 LWN이 비지니스 모델에서 광고가 차지하는 비중은 전체 수익의 10% 선이라고 밝히고 있습니다.
활발한 커뮤니티 이벤트
LWN은 Linux Weekly News라는 이름으로 거의 뉴스 사이트로 시작했지만 점점 성격은 정보 + 커뮤니티 성격이 가미되면서 이제는 오픈 소스를 비롯한 자유 소프트웨어(Free software) 커뮤니티를 지향하고 있습니다.
이러한 커뮤니티는 콘텐츠에 대한 활발한 논의(그렇기에 LWN의 구독 옵션에는 댓글에 대한 옵션을 매우 중시하고 있습니다.)와 더불어 오프라인 또는 온라인이벤트를 통한 활발한 교류를 통해서 커뮤니티를 발전시키고 있습니다.
2020년 7월 LWN 커뮤니티 캘린더(The LWN.net Community Calendar)
그렇기에 이러한 활발한 커뮤니티 이벤트를 통한 수익도 LWN의 중요한 비지니스 모델이라고 할 수 있습니다. 이 비중이 어느 정도 되는지는 확인할 수 없었습니다.
부분 유료 커뮤니티를 지향한 LWN에서 읽는 시사점
위에서도 언급했지만 LWN은 Linux Weekly News에서 출발해 오픈 소스를 비롯한 자유 소프트웨어(Free software) 커뮤니티를 지향하는 가운데 커뮤니티를 활성화하는 강력한 도구로서 고품질의 콘텐츠 확보에 주력했습니다.
그것은 콘텐츠 품질에 대한 강력한 관리를 통해 일정 수준을 넘는 고품질 콘텐츠만 제공하고, 다양하면서도 고품질 콘텐츠 확보를 위해 콘텐츠 제공에 문호를 개발하되 충분한 비용을 지불하면서 콘텐츠 제공이 활성화 할 수 있도록 했습니다.
고품질 콘텐츠를 위한 면밀한 관리
뉴욕타임즈등과 같은 유력 언론은 서면 인터뷰를 진행하는 경우 수십번에 걸친 메일 커뮤니케이션을 통해서 언론사에서 원하는 충분한 고품질 콘텐츠가 나오도록 만듭니다.
유력 언론사들이 그만큼 충분히 검증하고 리뷰하면서 충분한 콘텐츠를 완성해가는 것처럼, LAW도 글을 제안하고 작성 그리고 리뷰를 통해 실제로 발표되는데까지 많은 시간과 엄청난 커뮤니케이션을 통해서 충분히 검증하고 논리를 보강하면서 완성도를 높인다고 합니다.
앞서 언급된 글에서 소개된 사례에서도 LAW에 주제를 제안해 발표하는데 2주일 이상 걸렸고 무려 30통이 넘는 이메일을 주고 받으며 글을 편집하면서 완성도를 높였다고 합니다.
I’ve written a couple of articles for LWN (I waived the fee)[1][2]. It’s quite an involved process going through many rounds of editing. You have to stick to the house style and use the house markup. The results are excellent because of this consistent attention to detail, but I wouldn’t recommend it as a way to make a quick buck 🙂 The whole process for these two articles took two weeks from proposal to publication and involved 30 emails as well as many edits in their CMS.
콘텐츠 제공자에 대한 보상 시스템
콘텐츠를 제공하는 회사들은 좋은 글을 확보하기 위한 많은 노력을 기울입니다.
제가 관심을 가지고 살펴보고 있는 VPS 업체인 Vultr이나 Lenode와 같은 업체들은 품질 좋은 콘텐츠가 비지니스에 도움을 주기 때문에 서버 운영, 운영체제 활용등에 대한 글을 제공하는 경우 글당 $300 정도의 비용을 지불합니다.
LAW도 업계 평균 수준이상의 고료를 제공합니다. 다만 계속 좋은 글을 기고하게 되면 고려가 상당한 수준으로 올라간다고 합니다.
그것은 지속적으로 좋은 글을 올리면 그의 글을 신뢰하고 지속적으로 방문해 트래픽과 구독자가 증가하기 때문에 당연하게도 가치가 올라가기 때문이겠죠.
새로운 저자는 글 한 편당 $300 고료 지급 난이도가 높은 커널 관련 글은 $350 고료 지급
좋은 글을 지속적으로 기고하는 경우 고려가 상당한 수준으로 올라감
활성 커뮤니티를 통한 고품질 콘텐츠 생성 선순환
콘텐츠 제공자들이 글을 작성해 LAW에 기고하는 이유는 일정 정도 고료를 얻을 수 있다는 경제적인 동인도 있겠지만 자가기 알고 있는 내용을 알리고 그러는 가운데 인정을 받고 싶은 욕구가 있겠죠.
그것이 LAW와 같은 일정 권위를 인정받은 커뮤니티에서 인정을 받는다면 더욱 그러할 것입니다.
이는 활성화된 커뮤니티를 통해서 상당히 힘든 과정임에도 불구하고 LAW에 기고하도록 만들고 그 과정에서 LAW의 품질 관리 과정을 통해서 보다 고품질 콘텐츠를 만들 가능성을 높여주면서 좋은 콘텐츠가 늘어나는 선순환이 일어나는 것으로 보입니다.
The LSFMM 2019 group photo, Photo by LWN
프리미엄 모델
LAW는 오픈 소스와 자유 소프트웨어(Free Software)를 다루고 있기 때문에 영리적인 접근은 상당히 조심스러울 수 있습니다.
그러나 LAW는 유료 콘텐츠를 무한정 유료로 제한하는 것이 아니라 일정 시간이 지나면 누구나 자유롭게 열람할 수 있도록 만들면서 접근성을 높이면서도 유료화를 통한 지속 성장 가능성을 높였습니다.
이 설정 파일에서는 주로 아래와 같은 내용을 변경해 줍니다. 뭐 사용자 목적에 따라서 적절한 옵션을 변경할 수 있습니다.
스캔 후 이메일 통보 관련
먼저 스캔 결과를 이메일로 통보 여부, 값이 0이면 통보하지 않고, 1인 경우 메일 통보
email_alert=1Code language:PHP(php)
실제 편집 파일에 해당 건에는 아래와 같은 주석이 달려 있습니다.
# Enable or disable e-mail alerts, this includes application version# alerts as well as automated/manual scan reports. On-demand reports# can still be sent using '--report SCANID user@domain.com'.# [0 = disabled, 1 = enabled]
email_alert="1"Code language:PHP(php)
다음으로는 메일 주소를 설정합니다. 여러 메일 주소로 스캔 결과를 받아야 한다면 컴마(,)를 이용해 추가합니다.
# The destination e-mail addresses for automated/manual scan reports# and application version alerts.# [ multiple addresses comma (,) spaced ]
email_addr=”user@yourdomain.com, user2@yourdomain.com”Code language:PHP(php)
다음으로는 Maldet가 멀웨러를 감지하고 깨끗하게 치료했다면 굳이 메일 경고하지 않는다는 옵션 선택할지를 결정합니다. 1값은 멀웨어가 치료되었다면 굳이 메일 경고를 하지 않습니다.
email_ignore_clean="0"Code language:PHP(php)
실제 편집 파일에 해당 건에는 아래와 같은 주석이 달려 있습니다.
# Enable or disable slack alerts, this will upload the scan report as a file# into one or more slack channels# [0 = disabled, 1 = enabled]
slack_alert="0"Code language:PHP(php)
멀웨어 감염 파일 처리
다음에는 스캔 도중 멀웨어를 감지했다면 어떻게 할지에 대한 옵션인데요. 멀웨어에 걸린 파일을 특정 장소로 이동할지 그대로 둘지를 결정합니다.
값이 0이라면 감염되었다는 경고만 하고 파일은 그대로 유지합니다. 1 값을 주면 특정 장소로 이동 조치하고 경고해 줍니다.
quarantine_hits=1Code language:PHP(php)
실제 편집 파일에 해당 건에는 아래와 같은 주석이 달려 있습니다.
# The default quarantine action for malware hits# [0 = alert only, 1 = move to quarantine & alert]
quarantine_hits="1"Code language:PHP(php)
그 다음에는 감염된 멀웨어를 치료할지 여부를 선택합니다. 치료한다면 1값을 입력하고 그대로 두려면 0값을 유지합니다.
quarantine_clean=1Code language:PHP(php)
실제 편집 파일에 해당 건에는 아래와 같은 주석이 달려 있습니다.
# Try to clean string based malware injections# [NOTE: quarantine_hits=1 required]# [0 = disabled, 1 = clean]
quarantine_clean="1"Code language:PHP(php)
이번에는 조금 더 강력한 조치를 할 것인지를 선택하는데요. 멀웨어에 감염된 사용자는 서버에서 활동을 중지시키는 조치인데요 .
0값을 입력 시 멀웨어에 감염되었다고 판명되어도 그 사용자는 그대로 작동하며, 만약 1값을 입력시 멀웨어가 팀지되면 그 사용자는 사용이 중지됩니다.
1인 사용자 서버는 서비스사 멈추는 것이므로 신중하게 선택할 필요가 있습니다. 아무래도 서비스 중단까지는 부담스러우니 0값을 사용합니다.
quarantine_suspend_user=0Code language:PHP(php)
여기에는 아래와 같은 주석과 설명이 달려 있습니다.
# The default suspend action for users wih hits# Cpanel suspend or set shell /bin/false on non-Cpanel# [NOTE: quarantine_hits=1 required]# [0 = disabled, 1 = suspend account]
quarantine_suspend_user="0"Code language:PHP(php)
Maldet 실행
Maldet 설정이 끝났으면 Maldet를 실행해 봅니다. 우선 Maldet의 주요 실행 옵션을 살펴볼까요?
-u, –update-sigs [–force] 데이타베이스 업데이트
-a, –scan-all PATH 모든 파일을 스캔 예) maldet -a /home/username
Linux Malware Detect v1.6.4
(C) 2002-2019, R-fx Networks <proj@rfxn.com>
(C) 2019, Ryan MacDonald <ryan@rfxn.com>
This program may be freely redistributed under the terms of the GNU GPL v2
maldet(46026): {scan} signatures loaded: 17041 (14221 MD5 | 2035 HEX | 785 YARA | 0 USER)
maldet(46026): {scan} building file listfor /home/happist, this might take awhile...
maldet(46026): {scan} setting nice scheduler priorities for all operations: cpunice 19 , ionice 6
maldet(46026): {scan} file list completed in 2s, found 85826 files...
maldet(46026): {scan} found clamav binary at /usr/bin/clamdscan, using clamav scanner engine...
maldet(46026): {scan} scan of /home/happist (85826 files) in progress...
maldet(46026): {scan} clamscan returned an error, check /usr/local/maldetect/logs/clamscan_log for details!
maldet(46026): {scan} scan completed on /home/happist: files 85826, malware hits 0, cleaned hits 0, time 491s
maldet(46026): {scan} scan report saved, to view run: maldet --report 200707-2004.46026Code language:PHP(php)
Let’s Encrypt도 알고 보면 수많은 인증서 발급 기관, CA(Certificate Authority) 업체 중이 하나입니다. 다만 SSL 인증서 가격이 비싸 보급이 느려지면서 인터넷 보안에 문제가 있다는 문제 의식하에 무료 보급을 통한 인터넷 보안을 강화하겠다는 비영리 단체라고 할 수 있습니다.
인증서 발급 기관, CA(Certificate Authority) 중 점유율이 가장 높은 곳은 IdenTrust로 2020년 12월 기준으로 42%가 넘습니다.(42.3%)
그리고 과거 자료들을 보면 python-certbot-nginx 명령을 사용하는데 파이썬3으로 업그레이드되면서 파이썬3를 사용하라는 권고를 받습니다.
Let’s Encrypt SSL 인증서 발급 방법 4가지
Let’s Encrypt SSL 인증서 발급 방법은 webroot와 Standalone, DNS의 3가지 방식이 있습니다. 인증서 발급은 사이트에서 인증기관인 Let’s Encrypt에 접속해 이 사이트의 유효성을 검증하는 과정을 거치며 이 과정을 아래 3가지 방법 중 하나를 선택해 진행할 수 있습니다.
webroot : 사이트 디렉토리 내에 인증서 유효성을 확인할 수 있는 파일을 업로드하여 인증서를 발급하는 방법 . 실제 작동하고 있는 웹서버의 특정 데렉토리의 특정 파일 쓰기 작업을 통해서 인증 . 이 방식의 장점은 nginx를 중단시킬 필요가 없음. . 이 방법의 단점은 인증 명령에 하나의 도메인 인증서만 발급 가능
웹서버 . Nginx나 아파치와 같은 웹서버에서 직접 SSL 인증을 실시하고 웹서버에 맞는 SSL세팅값을 부여 . 발급이나 갱신을 위해 웹서버를 중단시킬 필요가 없음 . 인증서 갱신 시 상황에 맞게 세팅을 자동으로 업데이트 . 사용자가 세팅을 변경할 수 있지만 자동 업데이트 시 반영되지는 않음
Standalone : 사이트 작동을 멈추고 이 사이트의 네크워킹을 이용해 사이트 유효성을 확인해 Let’s Encrypt SSL 인증서를 발급하는 방식 . 80포트로 가상 staandalone 웹서버를 띄워 인증서를 발급 . 이 방식은 동시에 여러 도메인을 발급 받을 수 있음 . 그렇지만 인증서 발급 전에 Nginx를 중단하고 발급 완료 후 다시 Nginx를 시작해야 함
DNS : 도메인을 쿼리해 확인되는 TXT 레코드로 사이트 유효성을 확인하는 방법 . 와일드 카드 방식으로 인증서를 발급 가능 . 이 방법은 당연하게도 서버 관리자가 도메인 DNS를 관리/수정할 수 있어야 하며 . 인증서 갱신 시마다 DNS에서 TXT값을 변경해야 하므로 외부에서 TXT 레코드를 입력할 수 있도록 DNS가 API를 제공하는 경우만 갱신 과정을 자동으로 처리(클라우두플레어 API가 대표적인 사례)
사이트 보안을 강화는 방법으로 불필요하게 포트를 열어 놓았는지 확인해 반드시 필요한 포트만 여는 포트 사용 최적화 방법에 대해서 살펴보도록 하죠.
서버에서 사용하는 포트는 아래와 같이 세가지 주요 그룹으로 나눌 수 있습니다.
시스템 포트(System ports), 0-1023 – 핵심 서비스와 관련된 포트로 운영 체제에 필수적인 포트
사용자 포트(User ports), 1024-49151 – IANA가 ‘IETF Review’, ‘IESG Approval’, ‘Expert Review’ 프로세스를 위해 할당한 포트
동적 포트(Dynamic ports), 49152-65535 – 개인적이 용도로 사용하는 프라이빗 포트
어떤 포트를 사용하고 있는지 확인
현재 사이트에서 어떤 포트를 열어놓고 있는지 확인하기 위해서 아래와 같이 netstat -ano 명령을 사용해 봤습니다.
이 명령을 사용하면 아래와 같이 현재 열린 포트 및 대기하고 있는 포트들을 전부 다 보여줍니다. 또한 이 내용외에 다양한 내용을 출력해 줍니다. 너무 많아 기가 질릴 정도..
# netstat -ano
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State Timer
tcp 00127.0.0.53:530.0.0.0:* LISTEN off (0.00/0/0)
tcp 000.0.0.0:250.0.0.0:* LISTEN off (0.00/0/0)
tcp 00127.0.0.1:60100.0.0.0:* LISTEN off (0.00/0/0)
tcp 000.0.0.0:4430.0.0.0:* LISTEN off (0.00/0/0)
tcp 000.0.0.0:***** 0.0.0.0:* LISTEN off (0.00/0/0)
tcp 00127.0.0.1:33060.0.0.0:* LISTEN off (0.00/0/0)
tcp 000.0.0.0:800.0.0.0:* LISTEN off (0.00/0/0)
tcp 00141.164.48.62:443189.40.73.149:12786 ESTABLISHED off (0.00/0/0)
tcp 00141.164.48.62:443180.65.25.237:45753 ESTABLISHED off (0.00/0/0)
tcp 00141.164.48.62:443203.175.39.84:49433 ESTABLISHED off (0.00/0/0)
tcp 00141.164.48.62:44372.14.199.27:49062 ESTABLISHED off (0.00/0/0)
tcp 00141.164.48.62:443222.234.131.7:7507 ESTABLISHED off (0.00/0/0)
tcp 00141.164.48.62:44366.249.79.158:42357 ESTABLISHED off (0.00/0/0)
tcp 00141.164.48.62:443189.40.73.149:12792 ESTABLISHED off (0.00/0/0)
tcp 00141.164.48.62:44361.81.11.245:11848 TIME_WAIT timewait (7.58/0/0)
tcp 00141.164.48.62:443189.40.73.149:12788 ESTABLISHED off (0.00/0/0)
tcp 00141.164.48.62:443118.45.130.180:38805 TIME_WAIT timewait (57.05/0/0)
tcp 00141.164.48.62:443124.50.187.177:57167 ESTABLISHED off (0.00/0/0)
tcp 00141.164.48.62:44366.249.79.129:56320 ESTABLISHED off (0.00/0/0)
tcp 0180141.164.48.62:***** 124.50.187.177:57295 ESTABLISHED on (0.22/0/0)
tcp 00141.164.48.62:44366.249.79.120:43419 ESTABLISHED off (0.00/0/0)
tcp 00141.164.48.62:443211.243.82.156:49304 ESTABLISHED off (0.00/0/0)
tcp 00141.164.48.62:44366.249.79.159:40840 TIME_WAIT timewait (34.53/0/0)
tcp 00141.164.48.62:443211.178.105.144:56345 ESTABLISHED off (0.00/0/0)
tcp 00141.164.48.62:44366.249.79.118:54414 ESTABLISHED off (0.00/0/0)
tcp 00141.164.48.62:44366.249.79.158:62256 TIME_WAIT timewait (2.30/0/0)
tcp 00141.164.48.62:443222.121.168.2:4079 ESTABLISHED off (0.00/0/0)
tcp 00141.164.48.62:443121.151.167.84:47722 ESTABLISHED off (0.00/0/0)
tcp6 00 :::25 :::* LISTEN off (0.00/0/0)
tcp6 00 ::1:6010 :::* LISTEN off (0.00/0/0)
tcp6 00 :::***** :::* LISTEN off (0.00/0/0)
udp 00127.0.0.53:530.0.0.0:* off (0.00/0/0)
udp 00141.164.48.62:680.0.0.0:* off (0.00/0/0)Code language:PHP(php)
또는 netstat 명령대신 요즘 권장된다는 ss 명령을 사용해 봅니다. 조금 더 단순하게 보여줍니다. 간단한 대신 정보가 제한적이네요.
이 ip들을 확인해보니 집과 태블릿에서 사용하던 ip들입니다. 맨 위 ip는 집에서 사용하는 공인 ip이고, 나머지 4개는 태블릿에서 사용하는 유동ip로 보입니다.
그래서 집의 공인 ip와 태블릿의 유동 ip대인 211.36.대를 적용하기로 했습니다.
ip 허용 및 금지 설정
ip 허용 및 금지하는 방법을 살펴보도록 하죠. ip를 허용하는 방법은 /etc/hosts.allow 파일을 수정합니다.
nano /etc/hosts.allowCode language:PHP(php)
위에서 설명한대로 집의 공인 ip와 태블릿의 유통 ip 대역대를 적용하도록 하겠습니다.
ip 대역대 표시는 ip 4 자리 중 해당 부분만 표시할 수 있습니다. 우리가 흔히 아는 것처럼 *같은 것은 사용하지는 못합니다.
ip 세자리대까지 표현한다면 ‘124.50.187.’ 로 표시
ip 두자리대까지 표시한다면 ‘124.50.’로 표시
ip 한자리대까지 표시한다면 ‘124.’으로 표시
특정 ip만 적용하기 윌한 /etc/hosts.allow 파일은 아래와 같이 변경되었습니다.
# /etc/hosts.allow: list of hosts that are allowed to access the system.# See the manual pages hosts_access(5) and hosts_options(5).## Example: ALL: LOCAL @some_netgroup# ALL: .foobar.edu EXCEPT terminalserver.foobar.edu## If you're going to protect the portmapper use the name "rpcbind" for the# daemon name. See rpcbind(8) and rpc.mountd(8) for further information.#
sshd: 124.50.187.177
sshd: 211.36.Code language:PHP(php)
그리고 나머지 ip는 모두 사용 중지시킵니다. 이는 /etc/hosts.deny 파일을 수정합니다.
nano /etc/hosts.denyCode language:PHP(php)
아래 내용으로 변경합니다.
# /etc/hosts.deny: list of hosts that are _not_ allowed to access the system.# See the manual pages hosts_access(5) and hosts_options(5).## Example: ALL: some.host.name, .some.domain# ALL EXCEPT in.fingerd: other.host.name, .other.domain## If you're going to protect the portmapper use the name "rpcbind" for the# daemon name. See rpcbind(8) and rpc.mountd(8) for further information.## The PARANOID wildcard matches any host whose name does not match its# address.## You may wish to enable this to ensure any programs that don't# validate looked up hostnames still leave understandable logs. In past# versions of Debian this has been the default.# ALL: PARANOID
sshd: ALLCode language:PHP(php)
특정 사용자만 허용하는 방법
특정 사용만 ssh 접속을 허용하는 방법입니다. 이는 sshd_config 파일에서 특정 사용자를 허용하든지 금지시킬 수 있습니다.
nano /etc/ssh/sshd_configCode language:PHP(php)
이 파일에서 아래처럼 AllowUsers 다음에 허용할 사용자를 추가합니다. 예를들면 daisy 사용자만 추가한다면 다음과 같은 명령을 사용합니다.
AllowUsers daisyCode language:PHP(php)
이 파일에는 PermitRootLogin 명령어는 root 사용자 로그인을 허용할지 설정하는 명령인데요. 일부 글들을 보면 root 사용을 중지할 때 no 옵션을 준다고 설명하고 있는데요.
우분투에서 root 사용을 중지시킬 때 아래처럼 이를 no 옵션을 사용한다고 설명하는데 우분투 20.04에서는 작동하지 않더군요.
설치를 시작하다가 중간에 이 프로그램이 서버 용량을 사용할 것인데 설치를 진행할 것인지 질문합니다. 사용하겠다는 용량은 기껏 588K 정도 되기 때문에 부담없이 Y를 누릅니다.
# sudo apt install unattended-upgrades apt-listchanges bsd-mailx
Reading package lists... Done
Building dependency tree
Reading state information... Done
unattended-upgrades is already the newest version (2.3).
The following packages were automatically installed and are no longer required:
lockfile-progs sendmail-base sendmail-cf sensible-mda
Use 'sudoaptautoremove' toremovethem.
Suggestedpackages:
www-browserx-terminal-emulatorThefollowingNEWpackageswillbeinstalled:
apt-listchangesbsd-mailx
0 upgraded, 2 newlyinstalled, 0 toremoveand 31 notupgraded.
Needtoget 150 kBofarchives.
Afterthisoperation, 588 kBofadditionaldiskspacewillbeused.
Doyouwanttocontinue? [Y/n] YCode language:PHP(php)
설정 보안 업체이트 허용
다음으로는 강제 보안 업데이트를 허용합니다. 아래와 같은 명령을 사용하면 중간에 unattended-upgrades를 사용하도록 설정할지 질문하는 팝업이 뜹니다.
Applying updates on a frequent basis is an important part of keeping systems secure. By default, updates need to be applied manually using package management tools. Alternatively, you can choose to have this system automatically download and install important updates.Automatically download and install stable updates?
오늘 목적이 바로 그것이므로 키보드를 왼쪽으로 옮겨 Yes를 선택합니다.
unattended-upgrades 세부 옵션
이제 unattended-upgrades 세부 옵션을 정합니다. 이는 아래 파일을 편집합니다.
보안 업데이는 종종 우분투 서버 리부팅이 필요할 경우가 있습니다. 이 경우 관리자 확인없이 리부팅 허용(93번째 줄)
서버 리부팅이 필요한 경우 리부팅 시간을 위험이 적은 새벽 시간으로 설정(102번째 줄)
아래는 제가 설정한 내용입니다. 설명 등 군더더기 내용을 그대로 두었습니다.
// Automatically upgrade packages from these (origin:archive) pairs//// Note that in Ubuntu security updates may pull in new dependencies// from non-security sources (e.g. chromium). By allowing the release// pocket these get automatically pulled in.
Unattended-Upgrade::Allowed-Origins {
"${distro_id}:${distro_codename}";
"${distro_id}:${distro_codename}-security";
// Extended Security Maintenance; doesn't necessarily exist for// every release and this system may not have it installed, but if// available, the policy for updates is such that unattended-upgrades// should also install from here by default."${distro_id}ESMApps:${distro_codename}-apps-security";
"${distro_id}ESM:${distro_codename}-infra-security";
// "${distro_id}:${distro_codename}-updates";// "${distro_id}:${distro_codename}-proposed";// "${distro_id}:${distro_codename}-backports";
};
// Python regular expressions, matching packages to exclude from upgrading
Unattended-Upgrade::Package-Blacklist {
// The following matches all packages starting with linux-// "linux-";// Use $ to explicitely define the end of a package name. Without// the $, "libc6" would match all of them.// "libc6$";// "libc6-dev$";// "libc6-i686$";// Special characters need escaping// "libstdc\+\+6$";// The following matches packages like xen-system-amd64, xen-utils-4.1,// xenstore-utils and libxenstore3.0// "(lib)?xen(store)?";// For more information about Python regular expressions, see// https://docs.python.org/3/howto/regex.html
};
// This option controls whether the development release of Ubuntu will be// upgraded automatically. Valid values are "true", "false", and "auto".
Unattended-Upgrade::DevRelease "auto";
// This option allows you to control if on a unclean dpkg exit// unattended-upgrades will automatically run// dpkg --force-confold --configure -a// The default is true, to ensure updates keep getting installed//Unattended-Upgrade::AutoFixInterruptedDpkg "true";// Split the upgrade into the smallest possible chunks so that// they can be interrupted with SIGTERM. This makes the upgrade// a bit slower but it has the benefit that shutdown while a upgrade// is running is possible (with a small delay)//Unattended-Upgrade::MinimalSteps "true";// Install all updates when the machine is shutting down// instead of doing it in the background while the machine is running.// This will (obviously) make shutdown slower.// Unattended-upgrades increases logind's InhibitDelayMaxSec to 30s.// This allows more time for unattended-upgrades to shut down gracefully// or even install a few packages in InstallOnShutdown mode, but is still a// big step back from the 30 minutes allowed for InstallOnShutdown previously.// Users enabling InstallOnShutdown mode are advised to increase// InhibitDelayMaxSec even further, possibly to 30 minutes.//Unattended-Upgrade::InstallOnShutdown "false";// Send email to this address for problems or packages upgrades// If empty or unset then no email is sent, make sure that you// have a working mail setup on your system. A package that provides// 'mailx' must be installed. E.g. "user@example.com"
Unattended-Upgrade::Mail "업데이트 상황을 받아 볼 이메일 주소";
// Set this value to one of:// "always", "only-on-error" or "on-change"// If this is not set, then any legacy MailOnlyOnError (boolean) value// is used to chose between "only-on-error" and "on-change"// Remove unused automatically installed kernel-related packages// (kernel images, kernel headers and kernel version locked tools).//Unattended-Upgrade::Remove-Unused-Kernel-Packages "true";// Do automatic removal of newly unused dependencies after the upgrade//Unattended-Upgrade::Remove-New-Unused-Dependencies "true";// Do automatic removal of unused packages after the upgrade// (equivalent to apt-get autoremove)//Unattended-Upgrade::Remove-Unused-Dependencies "false";// Automatically reboot *WITHOUT CONFIRMATION* if// the file /var/run/reboot-required is found after the upgrade
Unattended-Upgrade::Automatic-Reboot "true";
// Automatically reboot even if there are users currently logged in// when Unattended-Upgrade::Automatic-Reboot is set to true//Unattended-Upgrade::Automatic-Reboot-WithUsers "true";// If automatic reboot is enabled and needed, reboot at the specific// time instead of immediately// Default: "now"
Unattended-Upgrade::Automatic-Reboot-Time "03:00";
// Use apt bandwidth limit feature, this example limits the download// speed to 70kb/sec//Acquire::http::Dl-Limit "70";// Enable logging to syslog. Default is False// Unattended-Upgrade::SyslogEnable "false";// Specify syslog facility. Default is daemon// Unattended-Upgrade::SyslogFacility "daemon";// Download and install upgrades only on AC power// (i.e. skip or gracefully stop updates on battery)// Unattended-Upgrade::OnlyOnACPower "true";// Download and install upgrades only on non-metered connection// (i.e. skip or gracefully stop updates on a metered connection)// Unattended-Upgrade::Skip-Updates-On-Metered-Connections "true";// Verbose logging// Unattended-Upgrade::Verbose "false";// Print debugging information both in unattended-upgrades and// in unattended-upgrade-shutdown// Unattended-Upgrade::Debug "false";// Allow package downgrade if Pin-Priority exceeds 1000// Unattended-Upgrade::Allow-downgrade "false";Code language:PHP(php)
APT 변경 사항 발생 시 받을 메일 주소 변경
다음으로는 listchanges.conf를 편집해 APT 변경 사항 발생 시 받아볼 이메일 주소를 root에서 설정합니다.
여기 설정은 매우 간단한데요. 아래와 같은 항목으로 구성되어 있고 이중 메일 주소를 설정하면 됩니다.
[apt]
frontend=pager
which=news
email_address=업데이트 상황을 받아 볼 이메일 주소
email_format=text
confirm=false
headers=false
reverse=false
save_seen=/var/lib/apt/listchanges.dbCode language:PHP(php)