0.7 C
New York
금요일, 12월 26, 2025

Buy now

Home Blog Page 234

서버 구동 이슈, 80포트 사용으로 nginx 구동 불가 nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)

0

오늘은 근래 종종 발행하는 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-27 18: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 27 18:58:51 happist.com systemd[1]: Starting nginx - high performance web server…
Jul 27 18: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.com nginx[34135]: nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
Jul 27 18:58:52 happist.com nginx[34135]: nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
Jul 27 18:58:52 happist.com nginx[34135]: nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
Jul 27 18:58:53 happist.com nginx[34135]: nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
Jul 27 18:58:53 happist.com nginx[34135]: nginx: [emerg] still could not bind()
Jul 27 18:58:53 happist.com systemd[1]: nginx.service: Control process exited, code=exited, status=1/FAILURE
Jul 27 18:58:53 happist.com systemd[1]: <strong><span style="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는 거의 사용하지 않기 때문에 이를 삭제하는 것입니다.

이는 아래와 같은 명령어를 사용합니다.

sudo apt-get --purge autoremove apache2Code language: PHP (php)

그러면 아래와 같이 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 and 1 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를 실행하는 방법을 사용하기로 했습니다.

fuser -k 80/tcp
/etc/init.d/nginx start Code language: PHP (php)

워드프레스 자동 실행 최소화로 워드프레스 속도 개선 방법

워드프레스를 오랫동안 운영하다보면 워드프레스 데이타베이스에 불필요한 자동 실행 데이타(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)
워드프레스 데이타베이스 테이블 리스트 그리고 옵션 테이블(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)
  1. 데이타베이스 이름 클릭
  2. 상단 메뉴 중 SQL 메뉴 크릭
  3. 쿼리문 입력란에 위 명령어 입력
  4. 실행
워드프레스 데이타베이스 옵션테이블 최적화, phpMyAdmin에서 자동 실행 데이타 확인

저의 경우 자동 실행되는 데이타 양이 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 테마 관련 내용을 지워버릴 필요가 있습니다.

워드프레스 데이타베이스 옵션 테이블에서 자동 실행되는 데이타 Top 30 리스트

옵션 테이블에서 상세 내용 삭제 방법

위에서처럼 워드프레스 데이타베이스 옵션테이블을 분석해보니 사용하지 않는 테마나 플러그인을 위해서 자동 실행되는 데이타들이 많다는 것을 알 수 있습니다.

phpMyAdmin에서 이들을 효과적으로 찾아 삭제하는 방법은 아래 명령을 사용해 찾을 수 있습니다. 아래는 amp 관련 내용을 모두 찾겠다는 명령입니다.

SELECT * 
FROM `wp_options` 
WHERE `autoload` = 'yes'
AND `option_name` LIKE '%amp%'Code language: PHP (php)

그러면 아래와 같이 amp 관련 항목을 전부 보여줍니다. 이를 모두 선택해 지우면 되겠죠.

워드프레스 데이타베이스 최적화, phpMyAdmin에서 amp관련 항목을 찾기

위와 같은 방법으로 해당 테마 또는 플러그인 해당 내용을 찾아 삭제하면 됩니다.

워드프레스 속도 개선을 위한 오브젝트 캐시 적용법

오늘은 워드프레스 속도 개선을 위한 방안의 하나로 오브젝트 캐시를 살펴보고 워드프레스 사이트에 오브젝트 캐시 적용 방법을 살펴보도록 하겠습니다.

간단히 워드프레스 캐시 종류를 알아보고 이중에서 조금 생소한 개념이 오브젝트 캐시를 이해하고 이 오브젝트 캐시 성능을 높여 워드프레스 속도 개선하는 방법을 알아봅니다.

워드프레스 캐시 종류

워드프레스 사이트 이용자가 요청하는 정보가 처리되는 과정을 이해하기 위해서 데이타가 흐르는 플로우를 간단히 그린 다이어그램을 보시죠.

  • 사용자 측면에서는 크롬과 같은 브라우저에서 처리할 수 있는 브라우저 캐시가 있습니다.
    이 브라우캐시는 한번 사이트를 방문 후 방문 페이지의 데이타를 브라우저 캐시에 저장해 놓음으로써 추가 방문 시 빠른 속도로 접속할 수 있습니다.
  • 웹 서버에서는 사용자가 요구했던 페이지 구성 정보를 저장해 놓아 도 다른 사용자가 그 페이지를 요청하면 추가 작업할 필요없이 바로보여줄 수 있게 만든 페이지 캐시로 반응 속도를 크게 높일 수 있습니다.
  • 페이지 구성을 위해 PHP에서 데이타베이스에 접속해 필요 정보를 받아온 결과를 저장해 놓은 것이 오브젝트 캐시입니다.
웹사이트 데이타 흐름 및 단계별 캐시 플로우, 브라우저 캐시, 페이지 캐시, 오브젝트 캐시, Brower Cache, Page Cache, Object Cache, Image from SinupWP
웹사이트 데이타 흐름 및 단계별 캐시 플로우, Brower Cache, Page Cache, Object Cache, Image from SinupWP

이중에서 브라우저 캐시는 사용자 측면에 일어나는 것이고, 페이지 캐시와 오브젝트 캐시는 서버 측면에서 일어나는 일입니다.

페이지 캐시 – FastCgi 캐시

페이지 캐시는 서버측면에서는 일반적으로는 Nginx에서 제공하는 FastCgi 캐시를 사용합니다.

서버를 직접 제어할 수 없는 호스팅을 사용해 워드프레스 사이트를 운영한다면 워드프레스 캐스 플러그인을 사용할 수 있습니다. WP Rocket과 같은 플러그인이 그것이죠.

일반적으로 우리가 알고있는 워드프레스 캐시 플러그인보다는 FastCgi 캐시와 같은 서버 측면의 캐시가 훨씬 더 효율적이라고 알려져 있습니다.

오브젝트 캐시

PHP와 Mysql 데이타베이스간 이루어지는 데이타에 대해서는 이미 PHP 자체에서 제공하는 Opcache와 워드프레스 자체에서 제공하는 오브젝트 캐시가 각기 작동합니다.

그러나 이러한 캐시 프로그램들은 웹서버 + Mysql + 워드프레스 CMS간 이루어지는 다양한 데이트 요청을 충분하게 캐시하지 못합니다. 특히 우커머스와 같은 데이타 요청이 많은 경우 성공율이 크게 낮아집니다.

이렇게 웹서버 + Mysql + 워드프레스 CMS간 데이타 요청 결과를 저장하는 오브젝트 캐시 효율을 높이기 위해서 Redis와 같은 특별한 캐시 프로그램을 사용해 효율을 높입니다.

오브젝트 캐시, Redis 설치 및 사용법

위에서 이야기한대 오브젝트 캐시 효율을 높이기 위한 Redis 사용법에 대해서 살펴봅니다.

이 캐시는 PHP와 Mysql간 데이타를 제어하는 것이니 크게 워드프레스 운영자가 크게 신경쓸 여지가 적습니다.

서버단에서 Redis를 사용할 수 있도록 패키지를 설치해주고, 관련 워드프레스 플러그인을 설치, Redis를 사용토록 설정하기만 하면 됩니다. 나머지는 알아서 하겠죠.

워드프레스 오브젝트 캐시, Redis Object cache, redis-wordpress-ubuntu
워드프레스 오브젝트 캐시, Redis Object cache, redis-wordpress-ubuntu

우분투 서버에서 Redis 패키지 설치

우분투 서버에서 Redis를 사용할 수 있도록 Redis 패키지 설치 후 php7.4-fpm을 다시 가동시킵니다. 이는 아래 명령을 사용합니다.

sudo apt install redis-server
sudo service php7.4-fpm restartCode language: PHP (php)

Redis 지원 워드프레스 플러그인 설치

앞에서 서버에 설치한 Redis가 워드프레스 데이타를 효율적으로 캐시할 수 있도옥 워드프레스 명령들을 Redis와 연계시켜주는 워드프렛 플러그인을 설치합니ㅏㄷ.

이러한 워드프레스 플러그인은  WP RedisRedis Object Cache가 있는데요. 오브젝트 캐시를 소개하는 전문가들은 이 두 플러그인중에서 Redis Object Cache를 추천하고 있습니다.

워드프레스 오브젝트 캐시 플러그인, wordpress object cache Redis, WP Redis
워드프레스 오브젝트 캐시 플러그인, wordpress object cache Redis, WP Redis

Redis 플러그인 설정

Redis Object Cache 사용법은 아주 단순합니다. 설정에서 Enable Object cache 버튼을 눌러 활성화 시켜 주기만 하면 됩니다.

워드프레스 오브젝트 캐시 플러그인 Redis Object cache 설정
워드프레스 오브젝트 캐시 플러그인 Redis Object cache 설정

유료 커뮤니티 모델로 성공한 LWN 사례에서 보는 고품질 콘텐츠 확보 중요성

리눅스와 자유 소프프퉤어(Free Software) 전문 뉴스 사이트인 LWN은 오래전부터 유료 구독 모델을 적용해 성공적으로 성장하면서 눈여겨 볼만한 미디어 사례를 만들고 있는데요.

오늘은 LWN이 어떻게 오랫동안 유료 구독 모델을 성공적으로 운영할 수 있었는지 정리해 보도록 하겠습니다. 이 사례는 GreekNews에 올라온 글을 기반으로 추가 보완한 것입니다.

리눅스 관련 가장 좋은 뉴스들이 올라오는 LWN의 글 작성 가이드

리눅스와 자유 소프트웨어(Free Software) 전문 매체, LWN 메인 페이지
리눅스와 자유 소프트웨어(Free Software) 전문 매체, LWN 메인 페이지

LWN의 비지니스 모델

뉴스나 정보를 제공하는 사이트들은 대개 유료 구독 모델, 광고 모델 그리고 기부 모델로 나누어 볼 수 있습니다.
이 세가지 비지니스 모델은 독자적으로만 존재하는 것이 아니라 특성을 적절하게 섞어서 비지니스 모델 성과를 높이고 있습니다.

예를들어 유료 구독 모델이지만 부분적으로 광고를 실기도 하고, 광고를 주로 하면서 일정 부분만 유료 구독 모델로 운영하기도 합니다.

그냥 개인의 돈을 들여 운영하는 사이트도 있지만 어느 정도 규모를 가지고 목적에 맞추어 커스터마이제이션해 운영하려면 정기적인 비용이 적지않게 듭니다.

사이트를 운영하는 비용도 당연히 들겠지만 콘텐츠를 생산하고 유지하는데 드는 비용은 경우에 따라서는 어마어마한 비용이 드는 것이 사실입니다.

인터넷 시대에 콘텐츠는 무료라는 인식이 강하지만 점점 읽을만한 콘텐츠는 점점 유료화되고 있습니다.

왜냐하면 콘텐츠는 그냥 자연적으로 만들어지는 것이 아니라 누군가의 땀과 노력으로 만들어지기 때문이고, 이러한 품질 좋은 콘텐츠를 지속적으로 제공하려면 콘텐츠 제작자들에게 충분한 보상이 주어져야 장기적으로 안정적으로 품질좋은 콘텐츠를 제공할 수 있는 것이죠.

우리나라는 아직 덜하지만 점점 외국의 경우 어지간한 언론사들은 유료 구독모델로 변경된지 오래입니다.

광고, 구독, 기부가 적절히 활용된 4가지 구독 옵션을 제공

이러한 트렌드들보다도 먼저 유료 구독 모델을 운영한 곳이 LWN이라고 할 수 있는데요. LWN은 아래와 같이 광고와 유료 구독 그리고 기부 모델을 적절하게 사용하고 있습니다.

  • 가장 비용이 낮은 starving hacker에게는 광고 모델과 구독 모델을 혼용해 사용하고 있으며.
  • 가장 많은 돈을 내는 maniacal supporter은 구독 모델과 기부 모델을 적절해 활용한 구독 타입
구독 타입월 구독료광고 여부접근 가능 콘텐츠
starving hacker3.5$O모든 콘텐츠 접근 가능
정기 결제 불가
(비용 문제) 
professional hacker$7X모든 콘텐츠 접근 가능
정기 결제 가능
작성자별 댓글 필터링
댓글 답변 이메일 수신
professional hacker$14X모든 댓글 이메일 통보
새 댓글 하일라이트 기능
maniacal supporter$50X댓글에 서포터 표시
컨퍼런스에서 맥주/음료 제공

이렇게 광고와 구독 그리고 기부가 적절한 게 활용된 LWN이 비지니스 모델에서 광고가 차지하는 비중은 전체 수익의 10% 선이라고 밝히고 있습니다.

활발한 커뮤니티 이벤트

LWN은 Linux Weekly News라는 이름으로 거의 뉴스 사이트로 시작했지만 점점 성격은 정보 + 커뮤니티 성격이 가미되면서 이제는 오픈 소스를 비롯한 자유 소프트웨어(Free software) 커뮤니티를 지향하고 있습니다.

이러한 커뮤니티는 콘텐츠에 대한 활발한 논의(그렇기에 LWN의 구독 옵션에는 댓글에 대한 옵션을 매우 중시하고 있습니다.)와 더불어 오프라인 또는 온라인이벤트를 통한 활발한 교류를 통해서 커뮤니티를 발전시키고 있습니다.

2020년 7월 LWN 커뮤니티 캘린더(The LWN.net Community Calendar)
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
The LSFMM 2019 group photo, Photo by LWN

프리미엄 모델

LAW는 오픈 소스와 자유 소프트웨어(Free Software)를 다루고 있기 때문에 영리적인 접근은 상당히 조심스러울 수 있습니다.

그러나 LAW는 유료 콘텐츠를 무한정 유료로 제한하는 것이 아니라 일정 시간이 지나면 누구나 자유롭게 열람할 수 있도록 만들면서 접근성을 높이면서도 유료화를 통한 지속 성장 가능성을 높였습니다.

  • LAW에 게재되는 글은 2주정도 LAW에 독점 게재 권리를 가짐
  • 따라 LAW 유료 구독자들은 이런 모든 글을 대기 시간없이 바로 볼 수 있음
  • 일반 사용자는 일정 시간 경과 후에야 볼 수 있음

멀웨어 스캔을 위한 리눅스 백신 Maldet 사용법

0

오늘은 LMD(Linux Malware Detect)로 알려진 리눅스 서버 멀웨어 감염 여부를 체크해주는 리눅스 멀웨어 스캐너인 Maldet를 사용해 정기 멀웨어 스캔 및 스캔 결과를 이메일로 받아보는 방법에 대해서 알아보도록 하겠습니다.

이전 글에서도 여러 번 말씀드렸지만 우분투를 비롯한 리눅스를 사용한 운영체제에서도 윈도우즈만큼은 아니지만 바이러스나 멀웨어를 퍼트리는 해커들이 활발하게 활동하고 있습니다.

따라서 정기적으로 바이러스나 멀웨어 감염 여부를 점검해 보는 것이 좋습니다.

이를 위해 ClamAV가 많이 사용되지만 멀웨어 스캔을 위해서 Maldet도 많이 사용됩니다. 그래서 저는 테스트삼아 두가지를 모두 사용해 정기적으로 스캔토록 만들었습니다.

여기에서는 Maldet를 할용해 정기적으로 리눅스 서버 멀웨어 스캔 및 스캔 결과를 이메일로 받아보는 방법에 대해서 살펴보겠습니다.

Maldet 설치 및 사용

우서 Maldet를 설치하는 방법을 살펴봅니다. 이하는 모두 우분투하에서 진행되는 방법입니다.

어선 /opt 폴더로 이동합니다.

cd /opt/Code language: PHP (php)

Maldet 공식 사이트인 rfxn.com에서 Maldet 최신 버젼을 다운받습니다. 이는 아래 명령어를 사용합니다.

wget http://www.rfxn.com/downloads/maldetect-current.tar.gzCode language: PHP (php)

최신 버전은 다운받았지만 이는 압축 파일이므로 압축을 풀어 줍니다.

tar xfz maldetect-current.tar.gzCode language: PHP (php)

압축을 풀고 ls -l 명령어로 어떤 파일 및 폴더가 생겼는지 확인해보면 maldetect-1.6.4와 같은 폴더가 생긴 것을 볼 수 있습니다.

트Maldet 설치 폴더 파일 리스트 maldetect-1.6.4

이제 이 폴더로 이동합니다. Maldet 버젼에 따라 폴더 이름이 달라지므로 확인 후 폴더 이동하는 것이 좋습니다.

cd maldetect-1.6.4Code language: PHP (php)

이 설치 폴더로 이동한 다음 Maldet 설치 스크립트 파일, install.sh을 만날 수 있는데요. 이를 실행해 줍니다.

./install.shCode language: PHP (php)

이제 설치스크립트가 실행되면서 설치를 시작합니다.

리눅스 백신 Maldet 설치 화면

Maldet 설정

이제 Maldet 설치가 끝났으면 설정으로 들어갑니다.

설정 파일은 /usr/local/maldetect/conf.maldet인데요. 이를 편집기로 수정합니다.

nano /usr/local/maldetect/conf.maldetCode language: PHP (php)

이 설정 파일에서는 주로 아래와 같은 내용을 변경해 줍니다. 뭐 사용자 목적에 따라서 적절한 옵션을 변경할 수 있습니다.

스캔 후 이메일 통보 관련

먼저 스캔 결과를 이메일로 통보 여부, 값이 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)

다음으로는 메일 주소를 설정합니다. 여러 메일 주소로 스캔 결과를 받아야 한다면 컴마(,)를 이용해 추가합니다.

email_addr=”user@yourdomain.com, user2@yourdomain.com”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
  • -e, –report SCANID email
    메일 보내기
    예) maldet –report SCANID user@domain.com

특정 폴더를 전부 스캔 시 아래와 같은 명령어 사용

maldet -a /home/usernameCode language: PHP (php)

그러면 아래와 같은 메세지를 내면서 스캔을 시작한 후 그 결과를 표시해 줍니다.

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 list for /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.46026
Code language: PHP (php)

최신 Let’s Encrypt SSL 인증서 발급 방법 4가지 정리

여기에서는 우분투 20.04운영체제에서 웹서로로 NGINX를 사용 시 무료 SSL 인증서로 인기있는 Let’s Encrypt SSL 인증서 발급 방법 전반에 대해서 살펴보도록 하겠습니다.

이전에도 정리한 적이 있지만 시간이 흘러 발급 방법이 달라져 수정 정리할 필요가 생겼습니다.

Let’s Encrypt도 알고 보면 수많은 인증서 발급 기관,  CA(Certificate Authority) 업체 중이 하나입니다. 다만 SSL 인증서 가격이 비싸 보급이 느려지면서 인터넷 보안에 문제가 있다는 문제 의식하에 무료 보급을 통한 인터넷 보안을 강화하겠다는 비영리 단체라고 할 수 있습니다.

  1. 인증서 발급 기관, CA(Certificate Authority) 중 점유율이 가장 높은 곳은 IdenTrust로 2020년 12월 기준으로 42%가 넘습니다.(42.3%)
  2. IdenTrust 다음으로는 DigiCert Group(15.7%) > Sectigo(14.1%) > GoDaddy Group(5.4%) > GlobalSign(2.3%
  3. Let’s Encrypt 점유율은 생각보다 낮아서 0.1%에 불과
  4. 하지만 Let’s Encrypt 사용을 아래 그래프에서 보듯 매우 빠르게 증가
    (개인적인 생각으로는 무료이기 때문에 테스트로 중복 인증도 많다고 생각)
Let’s Encrypt SSL 인증서 사용 증가 추이, Graph by Let’s Encrypt
Let’s Encrypt SSL 인증서 사용 증가 추이, Graph by Let’s Encrypt

Let’s Encrypt SSL 인증서는 아래와 같은 장점들이 있어 사용이 확대되고 있습니다.

  1. 인증 절차가 단순(서버에서 필요한 프로그앰들이 설치되어 있다면) 단 한줄 명령어로 발급
  2. 발급 대기 시간 없이 바로 발급(이메일 입력 등을 포함해 1분 이내)
  3. Nginx나 아파치와 같은 웹서버에 맞추어 자동 옵션 설정되도록 설치 가능
  4. 인증 유효기간은 90일이지만 인증을 자동화 가능
  5. 무엇보다도 무료

근래 새롭게 사이트를 구축하하면서 Let’s Encrypt SSL 인증서 발급 과정에서 기존에 알고 있던 방법들이 대부분 작용하지 않는 것을 확인했습니다. 그것은

  • Let’s Encrypt SSL 인증서에서 여러가지 보안 이슈들이 발견되면서 발급 방법에 대한 보완이 진행되었고,
  • 발급에 필요한 패키지들도 업그레이드 되었고,
  • 예전에 알려졌던 기능들 일부는 지원이 중단(예를들어 자동 갱신 기능으로 많이 사용했던 Cert-Auto기능 지원 중단 등)되기도 했으며
  • 운영체제 업그레이드, DB 업그레이드에 따른 문제 발생 등

위와 같은 요인들로 인해서 기존에 알고 있던 방법들이 이를 제대로 반영하지 못하는 것으로 보입니다.

이제 Let’s Encrypt SSL 인증서 발급 방법에 대해서 알아 보시죠.

인증서 설치를 위한 Certbot tool 설치

Let’s Encrypt SSL 인증서 발급은 Certbot을 이용합니다. Certbot은 우분투 20.04를 설치 후 letsencrypt을 설치했다면 그 안에 포함되어 있기 때문에 별도 Certbot을 설치할 필요가 없습니다.

sudo apt update
sudo apt-get install  letsencrypt -yCode language: PHP (php)

[참고] Nginx 또는 아파치용 인증서 프로그램 설치 이용

Let’s Encrypt SSL 인증서 발급은 매우 다양한 방법으로 이루어지는데요. 이 중에는 Nginx나 아파치와 같은 웨서버가 전적으로 인증서를 제어토록 하는 방법이 있습니다.

이 경우 인증서 관리는 웹서버가 세팅과 관리를 알아서 한다고 하는데요. 저는 몇번 테스트해보고 기존 방식을 사용하기로하고 사용을 그만 두었습니다.

이 방법을 사용하려면 웹서버에 맞는 Certbot을 추가 설치합니다.

우선 우분투에서 Nginf용 Certbot 설치는 아래 명령을 사용합니다. 여기서는 Nginx 웹서버용 파이썬3를 설치합니다.

sudo apt update 
sudo apt upgrade -y 
sudo apt install certbot python3-certbot-nginxCode language: PHP (php)

만약 Apache를 사용한다면 python3-certbot-nginx 명령은 아파치를 지원하도록 python3-certbot-apache 명령으로 대체되겠죠.

sudo apt install certbot python3-certbot-apacheCode language: PHP (php)

그리고 과거 자료들을 보면 python-certbot-nginx 명령을 사용하는데 파이썬3으로 업그레이드되면서 파이썬3를 사용하라는 권고를 받습니다.

Let’s Encrypt SSL 인증서 발급 방법 4가지

Let’s Encrypt SSL 인증서 발급 방법은 webroot와 Standalone, DNS의 3가지 방식이 있습니다. 인증서 발급은 사이트에서 인증기관인 Let’s Encrypt에 접속해 이 사이트의 유효성을 검증하는 과정을 거치며 이 과정을 아래 3가지 방법 중 하나를 선택해 진행할 수 있습니다.

  1. webroot : 사이트 디렉토리 내에 인증서 유효성을 확인할 수 있는 파일을 업로드하여 인증서를 발급하는 방법
    . 실제 작동하고 있는 웹서버의 특정 데렉토리의 특정 파일 쓰기 작업을 통해서 인증
    . 이 방식의 장점은 nginx를 중단시킬 필요가 없음.
    . 이 방법의 단점은 인증 명령에 하나의 도메인 인증서만 발급 가능
  2. 웹서버
    . Nginx나 아파치와 같은 웹서버에서 직접 SSL 인증을 실시하고 웹서버에 맞는 SSL세팅값을 부여
    . 발급이나 갱신을 위해 웹서버를 중단시킬 필요가 없음
    . 인증서 갱신 시 상황에 맞게 세팅을 자동으로 업데이트
    . 사용자가 세팅을 변경할 수 있지만 자동 업데이트 시 반영되지는 않음
  3. Standalone : 사이트 작동을 멈추고 이 사이트의 네크워킹을 이용해 사이트 유효성을 확인해 Let’s Encrypt SSL 인증서를 발급하는 방식
    . 80포트로 가상 staandalone 웹서버를 띄워 인증서를 발급
    . 이 방식은 동시에 여러 도메인을 발급 받을 수 있음
    . 그렇지만 인증서 발급 전에 Nginx를 중단하고 발급 완료 후 다시 Nginx를 시작해야 함
  4. DNS : 도메인을 쿼리해 확인되는 TXT 레코드로 사이트 유효성을 확인하는 방법
    . 와일드 카드 방식으로 인증서를 발급 가능
    . 이 방법은 당연하게도 서버 관리자가 도메인 DNS를 관리/수정할 수 있어야 하며
    . 인증서 갱신 시마다 DNS에서 TXT값을 변경해야 하므로
    외부에서 TXT 레코드를 입력할 수 있도록 DNS가 API를 제공하는 경우만 갱신 과정을 자동으로 처리(클라우두플레어 API가 대표적인 사례)

1. webroot으로 SSL 인증서 발급

이 방법에 대해서는 Let’s Encrypt 설치 및 SSL 인증서 발급, NGINX 설정 방법nginx 서버에 HTTPS사용을 위한 SSL 인증서 발급받기 (Let’s Encrypt)등을 참조했습니다.

솔직히 이 방법은 번거롭기도하고 나중에 설명하겠지만 한계도 있는 방법입니다. 한번 설치해 운영하다가 한 서버에서 여러 도메인을 운영하는 것이 쉽지 않고, 이 방식으로 인증 시 일부 회사 방화벽에서 거부당하는 사례를 발견해서 사용 중단했습니다.

well-known이 있는 곳을 지정

위에서 설명한 것처럼 이 방법은 사이트 디렉토리 내에 인증서 유효성을 확인할 수 있는 파일을 업로드하여 인증서를 발급하는 방법으로 서버 내에 .well-known이 있는 곳을 지정해 줍니다.

이는 엄청 번거롭지만 1) 특정 폴더를 만들고 2) 이 특정 폴더에서 letsencrypt.conf 파일을 만들고 3) 이를 Nginx 설정 파일에서 이 파일을 읽어오도록 합니다.

먼저 특정 폴더를 만들고 mkdir -p 명령 옵션을 사용해 존재하지 않는 폴더를 만들어 줍니다.

mkdir -p /var/www/letsencrypt/.well-known/acme-challengeCode language: PHP (php)

다음으로는 webroot 경로를 알려주는 letsencrypt.conf 파일을 만듭니다. 이 파일 위치는 일반적으로 /etc/nginx/snippets/에 만드네요. 뭐 nano 편집기를 이용해서 아래와 같이 새로 만들 파일을 엽니다.

nano /etc/nginx/snippets/letsencrypt.conf Code language: PHP (php)

이 파일에 아래와 같은 내용을 추가합니다.

location ^~ /.well-known/acme-challenge/ {
default_type "text/plain";
root /var/www/letsencrypt;
}Code language: PHP (php)

다음으로는 Nginx 설정 파일 중 server 영역에서 아래 파일을 참조하도록 합니다.

include /etc/nginx/snippets/letsencrypt.conf;Code language: PHP (php)

이렇게 설정이 끝나면 Nginx를 다시 시작해 주어야겠죠.

sudo service nginx restartCode language: PHP (php)

예를 들면 아래와 같습니다.

server {
    listen 80 default_server;
    listen [::]:80 default_server ipv6only=on;
    server_name happist.com www.happist.com;

    include /etc/nginx/ssl/letsencrypt.conf;

    location / {
        return 301 https://www.happist.com$request_uri;
        expires epoch;
        # return 301 아래에 expires epoch; 을 붙여주는 것은 301 리다이렉트가 캐싱되지 않도록 하기 위함
    }

}Code language: PHP (php)

SSL 인증서 발급

SSH command line에서 아래 명령을 입력합니다.

certbot certonly --webroot --webroot-path=/var/www/letsencrypt  -d 사이트명.com -d www.사이트명.com  Code language: PHP (php)

여기서 webroot-path는 사이트 루트를 말하는 것이 아니라 .well-known 폴더가 있는 곳을 말합니다.

2. 웹서버를 통한 Let’s Encrypt SSL 인증서 발급

앞에서 설명했듯이 SSL인증서를 일반적인 방식으로 발급할 수도 있도 Apache나 Nginx와 같은 웹서버 옵션을 이용해서 Let’s Encrypt SSL 인증서 발급받을 수도 있습니다.

웹서버를 통한 SSL 인증서 발급 방법의 좋은 점은 standalone 방식과 비슷한 방식이면서도 다르게 발급 받을 시 사이트 서비스를 중단하지 않아도 된다는 점이고, 웹서버가 알아서 적절한 SSL 옵션을 제안해 적용해 준다는 점입니다.

이 과정을 다시 자세하게 설명하면

Certot 설치

서버에서 SSL 인증서를 설치할 웹서버용 인증서 설치 툴인 Certbot을 설치합니다. 위에서 설명했듯이 nginx 웹서버용입니다. Certbot은 파이썬 2.7 또는 3.6버젼 이상을 지원한다고 하네요.

sudo apt install certbot python3-certbot-nginxCode language: PHP (php)

아파치라면 아래 명령어를 사용합니다.

sudo apt install certbot python3-certbot-apacheCode language: PHP (php)

Nginx 설정 확인

Nginx 설정에서 도메인이 제대로 설정되어 있는지 확인합니다. 다른 방식은 nginx 설정에서 도메인에 적용되어 있는지가 중요하지 않지만 웹서버를 이용하는 SSL 인증 방식 선택 시 Nginx 설정에서 도메일이 제대로 설정되어 있어야 합니다.

Nginx 설정 파일은 웹서버 설치 방식에 따라 달라지지만 Nginx 기본 설치 방식으로는 설치 시 /etc/nginx/conf.d/에 있고, 우분투에서 권장하는 방식으로 설치 시 /etc/nginx/sites-available/ 디렉토리에 있습니다.

저는 우분투 20.04를 사용하지만 Nginx 기본 설치 방식으로 설치해 /etc/nginx/conf.d/ 디렉토리 아래에 happist.com.conf라는 파일을 만들어 설정 내용을 넣엇습니다.

nano /etc/nginx/conf.d/happist.com.confCode language: PHP (php)

여기에서 다음과 같은 server_name이 등록되어 있는지만 확인하면 됩니다.

server_name example.com www.example.com;Code language: PHP (php)

도메인 등록이 되어 있는지 확인이 되었으면 편집기를 닫습니다. 없다면 도메인을 등록 , 저장 후 편집기를 빠져 나옵니다.

본격적으로 SSL 인증을 시작하기 전에 nginx가 제대로 작동하는지 확인합니다. 우선 테스트해 봅니다.

sudo nginx -tCode language: PHP (php)

아무 문제가 없다고 나오면 nginx를 다시 가동시킵니다.

sudo systemctl reload nginxCode language: PHP (php)

방화벽에서 HTTPS를 허용

아마 기본적으로 설정되어 있기는 하겠지만 80포트와 443포트를 허용해 주고 있는지 확인합니다.

우분투 20.04라면 기본 방화벽으로 ufw를 사용하고 있겠죠. 여기에서라면 ‘Nginx Full’ 옵션을 사용합니다.

sudo ufw allow ssh
sudo ufw allow 'Nginx Full'Code language: PHP (php)

그냥 iptables를 사용한다면 다음 명령으로 80포트와 443포트를 사용 가능하게 만들 수 있습니다.

sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPTCode language: PHP (php)

SSL 인증서 발급

이제 본격적으로 SSL 인증서를 발급해 보시죠. Nginx 웹서버의 경우 아래 명령어를 사용합니다.

sudo certbot --nginx -d 사이트명.com -d www.사이트명.com  Code language: PHP (php)

만약 아파치라면 다음과 같은 명령을 사용합니다.

sudo certbot --apache d 사이트명.com -d www.사이트명.com  Code language: PHP (php)

그러면 /etc/etsencrypt 폴더에 자동으로 SSL 적용 옵션을 제안해 줍니다. 사용자는 이 옵션을 그대로 사용할 수도 있고, 독자적인 옵션을 적용할 수도 있습니다.

그러나 별도 옵션을 적용하면 nginx가 제안하는 사항들이 제대로 업데이트가 안되기 때문에 매번 수작업으로 업데이트를 해주어야 합니다.

독자적인 옵션을 사용하지 않고 그냥 웹서버가 제안하는 옵션 그대로 사용한다면 나쁘지 않다는 생각입니다.

자동 갱신

자동 갱신은 크론에서 다음 명령어를 적용해 가능토록 만들 수 있습니다.

sudo certbot renew --dry-runCode language: PHP (php)

3. standalone 옵션 적용 SSL 인증서 발급

여기 소개하는 standalone 옵션은 사이트 서비스를 중단하고 사이트의 네트워킹을 이용해 인증서버와 접속하는 방식을 사용합니다. 처음에 가장 많이 소개된 방식이기도 합니다.

이러한 방식은 단점이면서도 장점을 가지는데요. 단점은 이 인증을 진행하는 동안 사이트 서비스를 중단해야 한다는 점이구요. 장점은 간편하고 빠르고 더우기 안정성도 높습니다.

사내 방화벽을 매우 까다롭게 관리하는 국내 일부 회사 중에서 SSL 인증 방식에 따라 접속을 허용하는 않는 경우도 있는데요. 우리나라에서 가장 큰 회사 중의 하나도 이 standalone 방식만 제대로 통과되는 것으로 확인하기도 했습니다.(2020년 5월 기준)

이런 저런 이유로 웹서버를 중단시키고 발급받는 불편함은 있지만 상대적으로 안정적인 방식이라고 생각합니다.

아래와 같은 명령어를 사용합니다. 혼란을 피하기 위해 처음부터 다시 설명해 봅니다.

우선 SSL 인증을 위한 Certbot tool을 설치합니다. 이전 단계에서 설치 했다면 Pass

sudo apt update
sudo apt-get install  letsencrypt -yCode language: PHP (php)

다음으로는 웹서버를 중단시킵니다. 아무 디렉토리에서나 가능한 명령이지만 root로 이동해서 진행해 봅니다.

cd /root/
service nginx stopCode language: PHP (php)

이제 certbot 명령을 이용해 SSL 인증을 시작합니다. standalone 명령을 사용하고 도메인 이름만으로 인증이 진행되는 -d 옵션을 적용합니다.

certbot certonly --standalone -d 사이트명.com -d www.사이트명.comCode language: PHP (php)

인증이 완료되면 웹서버를 다시 가동시킵니다.

service nginx restartCode language: PHP (php)

여러개 도메인을 한번에 인증받기

이 standalone 옵션은 또한 한꺼번에 여러 개의 사이트를 인증 받을 수 있는데요. 아래와 같은 명령을 사용할 수 있습니다. 이는 연속해서 사이트명을 나열해주면 가능합니다.

예를들면 아래와 같이요.

service nginx stop
certbot certonly –standalone -d happist.com -d http://www.happist.com -d sample1.com -d http://www.sample1.com -d sample2.kr -d http://www.sample2.kr -d sample3.co -d http://www.sample3.co -d sample4.net -d http://www.sample4.net
service nginx restart
Code language: PHP (php)

단 이렇게 여러개 사이트를 인증 받을 시 인증서 존재 위치가 맨앞 사이트명으로 통일되게 됩니다. 나중에 사이트를 없앨 때 난감할 수도 있습니다.

즉 위의 경우 맨 앞에 happist.com 이름을 딴 폴더가 만들어지고 이 폴더 아래에 4개의 .pem 파일이 만들어지고, 이 파일들이 5개 사이트에 대한 인증이 구성됩니다.

그럴 경우 나중에 어느 한 사이트를 없애는 경우 – 사이트가 커져서 그 사이트는 다른 서버에서 운영을 한다던지, 아예 사이트를 폐쇄해 버리든지 – 하는 경우 관리가 불편해 질 수 있습니다.

따라서 조금 귀찮드라로도 사이트 하나 하나 명령어를 따로 입력하는 것이 관리엔 더 좋습니다. 아래처럼

certbot certonly --standalone -d happist.com -d www.happist.com

certbot certonly --standalone -d sample1.com -d www.sample1.com

certbot certonly --standalone -d sample2.kr -d www.sample2.kr

certbot certonly --standalone -d sample3.co -d www.sample3.co

certbot certonly --standalone -d sample4.net -d www.sample4.netCode language: PHP (php)

이렇게 각각 명령을 사용해 인증하면 사이트마다 폴더가 생겨서 관리가 좀금 더 쉽워집니다. 아래처럼 각 사이트별 폴더가 형성됩니다. 그 폴더 아래에 그 사이트에 해당하는 각종 .pem 파일이 만들어지기 때문에요.

  • /etc/letsencrypt/live/happist.com
  • /etc/letsencrypt/live/sample1.com
  • /etc/letsencrypt/live/sample2.kr
  • /etc/letsencrypt/live/sample3.co
  • /etc/letsencrypt/live/sample4.net

4. DNS 이용해 발급 받기

도메인이 연결된 DNS의 TXT레코드를 이용해 인증받는 방식으로 많은 장점을 가진 방법이지만 갱신 시 마다 DNS 정보 중 TXT 레코드를 새로 생성해야 하므로 이를 지원하는 DNS API가 없는 경우 자동 갱신이 어렵다는 단점이 있습니다.

  • 와일드 카드 방식으로 인증서를 발급 가능
  • 당연하게도 서버 관리자가 도메인 DNS를 관리/수정할 수 있어야 하며
  • 인증서 갱신 시마다 DNS에서 TXT값을 변경해야 하므로
    외부에서 TXT 레코드를 입력할 수 있도록 DNS가 API를 제공하는 경우만 갱신 과정을 자동으로 처리

이 방법은 위에서 소개한 방법 중에서 가장 까다로운 방법으로 에러 가능성이 높아 여러번 시도하다보면 최대 허용 시도를 초과해 당분간(1주일 정도) 인증이 불가능해지는 불쌍사가 종종 발생하더군요.

그래서 저는 편리함에도 불구하고 사용하지 못하고 있습니다.

클라우드플레어(CloudFlare)의 경우

DNS를 클라우드플레어(CloudFlare)에 도메인 네임서버로 등록해 사용하는 경우 클라우드플레어(CloudFlare)가 제공하는 DNS API를 이용해서 와일드 카드 인증서 등록 및 갱신이 가능합니다.

클라우드플레어(CloudFlare) API 관리 폴더와 파일 생성

클라우드플레어(CloudFlare) API를 관리할 폴더와 파일을 생성합니다.

cd /root
mkdir .secret
nano /root/.secret/cloudflare_api.iniCode language: PHP (php)

cloudflare_api.ini 파일에는 아래 내용을 추가합니다.

여기서 이메일 주소는 클라우드플레어(CloudFlare) 계정에 등록된 이메일이고, Global API Key는 CloudFlare의 계정 관리에서 발급 받을 수 있습니다.

dns_cloudflare_email = "이메일 주소 입력"
dns_cloudflare_api_key = "Global API Key 입력"Code language: PHP (php)

생성한 폴더와 파일에 대한 권한을 변경합니다.

chmod 700 /root/.secret
chmod 400 /root/.secret/cloudflare_api.iniCode language: PHP (php)

Certbot tool과 클라우드플레어 DNS 인증 플러그인 설치

여기에서는 SSL 인증 진행에 필요한 Certbot tool인 letsencrypt과 클아우드플레어 인증 플러그인을 설치합니다.

먼저 인증 플러그인 설치를 위한 파이선 PIP를 설치합니다.

<em>apt install python-pip</em>Code language: PHP (php)
<em>pip install certbot-dns-cloudflare</em>Code language: PHP (php)

혹시SSL 인증을 위한 Certbot tool가 설치되지 않았다면 설치합니다. 이전 단계에서 설치 했다면 Pass

sudo apt update
sudo apt-get install  letsencrypt -yCode language: PHP (php)

SSL 인증서 발급

SSL 인증서 발급 명령어입니다. example.com을 예를 든 것이고 자신의 도메일을 대체하면 됩니다.

/usr/local/bin/certbot certonly --dns-cloudflare --dns-cloudflare-credentials /root/.secret/cloudflare_api.ini -d example.com,*.example.com --preferred-challenges dns-01Code language: PHP (php)

이후 nginx라면 dhparam을 생성해 줍니다.

mkdir /etc/nginx/ssl  
cd /etc/nginx/ssl
openssl dhparam -out dhparams.pem 4096Code language: PHP (php)

이후 아래 명령을 추가합니다.

openssl rand 48 > 
session_ticket.key Code language: PHP (php)

인증서 자동 갱신

이렇게 발행된 인증서를 자동으로 갱신하기 위해서는 코론에 등록합니다.

crontab -eCode language: PHP (php)

Nginx라면 아래와 같은 내용을 추가합니다.

30 4 * * 0 /usr/local/bin/certbot renew --quiet --post-hook "/usr/sbin/service nginx reload" > /dev/null 2>&1Code language: PHP (php)

아파치라면 아래 명령을 사용합니다.

30 4 * * 0 /usr/local/bin/certbot renew --quiet --post-hook "/usr/sbin/service apache2 restart" > /dev/null 2>&1Code language: PHP (php)

Vultr에서 DNS를 이용한 와일드카드 인증 방법

가상서버 호스팅인 Vultr를 이용하는 경우에도 DNS를 이용해 와일드카드 인증이 가능하다고 합니다.

이는 Install Wildcard Certificate from Let’s Encrypt (Ubuntu 20.04 + Nginx + Vultr)를 참조해 간단히 정리했습니다.

필요 사항

  • 우분투 20.04 설치된 서버
  • Vultr DNS에 도메인 네임 등록, 예를들어 example.com
  • A/AAAA 레코드 및 CNAME DNS 레코드 등록
  • Vultr API 사용 가능 상태로 변경
    . 이는 Account – API에서 Enanble API를 눌러서 활성화 함
    . 그러면 Personal Access Token 번호가 나오는데 이를 기록해 놓음
Vultr 가상 서버 API

필요 패키지 설치

이 작업에 필요한 필요 패키지를 설치합니다. 우선 파이썬이 없으면 파이선3를 설치합니다. 그렇지만 우분투 20.04를 설치하면 파이썬 3가 설치되어 있습니다.

<code>sudo apt install -y python3</code>Code language: PHP (php)

다음으로는 Lexicon tool을 설치하는데요. 이는 다양한 DNS 프로바이더로부터 DNS 기록을 관리할 수 있는 파이썬 도구라고 합니다.

<code>sudo apt install -y lexicon</code>Code language: PHP (php)

acme.sh 클라이언트 설치

이 클라이언트는 root 사용자만 설치 가능하므로 root 사용자로 전환합니다.

<code>sudo su - root</code>Code language: PHP (php)

scme.sh를 다운받아 설치합니다. 아래 your_email@example.com는 Vultr 계정에서 사용하는 이메일 주소입니다.

<code>git clone https://github.com/Neilpang/acme.sh.git </code>
<code>cd acme.sh </code>
<code>./acme.sh --install --accountemail "your_email@example.com" </code>
<code>source ~/.bashrc </code>
<code>cd</code>Code language: PHP (php)

와일드카드 SSL 인증

도메인에 대한 RSA와 ECC와일드카드 인증을 받습니다. 아래와 같은 명령을 사용합니다.

  • 아래 your_email@example.com는 Vultr 계정에서 사용하는 이메일 주소
  • XXXXXXXXXXXXXXX은 Personal Access Token 번호
Configure your API key and username
 export PROVIDER=vultr
 export LEXICON_VULTR_USERNAME="your_vultr_email@example.com"
 export LEXICON_VULTR_TOKEN="XXXXXXXXXXXXXXX"
 RSA 2048
 acme.sh --issue --dns dns_lexicon -d example.com -d '*.example.com' --keylength 2048
 ECC 256
 acme.sh --issue --dns dns_lexicon -d example.com -d '*.example.com' --keylength ec-256Code language: PHP (php)

이러한 작업 결과 RSA 및 ECC/ECDSA인증 및 키는 아래 폴더에 위치하게 됩니다.

RSA: <code>~/.acme.sh/example.com</code> directory.
ECC/ECDSA: <code>~/.acme.sh/example.com_ecc</code> directory.Code language: PHP (php)

이들 폴더를 이용할 수 없기 대문에 이들을 /etc/letsencrypt 폴더로 옮김니다. 먼더 폴더를 만들고..

<code>sudo mkdir -p /etc/letsencrypt/example.com </code>
<code>sudo mkdir -p /etc/letsencrypt/example.com_ecc</code>Code language: PHP (php)

아래 명령으로 인증 및 복사합니다.

RSA
 acme.sh --install-cert -d example.com \
         --cert-file /etc/letsencrypt/example.com/cert.pem \
         --key-file /etc/letsencrypt/example.com/private.key \
         --fullchain-file /etc/letsencrypt/example.com/fullchain.pem \
         --reloadcmd "sudo systemctl reload nginx.service"
 ECC/ECDSA
 acme.sh --install-cert -d example.com --ecc \
         --cert-file /etc/letsencrypt/example.com_ecc/cert.pem \
         --key-file /etc/letsencrypt/example.com_ecc/private.key \
         --fullchain-file /etc/letsencrypt/example.com_ecc/fullchain.pem \
         --reloadcmd "sudo systemctl reload nginx.service"Code language: PHP (php)

SSL 인증서 관련 참고

최신 Let’s Encrypt SSL 인증서 발급 방법 4가지 정리

Let’s Encrypt SSL 인증서 발급 및 자동 갱신 방법(업데이트)

최신 보안 트렌드를 반영한 Let’s Encrypt 인증서 세팅 방법

웹, 브라우저, 서버에서 SSL 인증서 정보 확인 방법, SSL 인증서 만료일 확인 등

서버 보안 관련

랜섬웨어 대응, 서버 및 워드프레스 필수 보안 설정 15가지

워드프레스 보안 강화 NGINX 설정 방법 8가지

리눅스 서버 보안을 위한 포트 사용 최적화

리눅스 서버 root 사용 중지로 리눅스 서버 보안 강화하기

우분투 서버 보안 자동 업데이트 및 업데이트 메일 통보 방법

워드프레스 보안 진단 WPScan 사용법 및 이메일로 결과 받아보기

DDoS 취약 기능 XMLRPC 사용 중지로 워드프레스 보안 강화하기

워드프레스 보안 강화를 위한 사용자명 변경 방법

워드프레스 멀웨어 경험에서 배운 워드프레스 보안 가이드

가상서버호스팅 서버 보안 설정 방법 – Nginx +Ubuntu의 경우

리눅스 서버 보안을 위한 포트 사용 최적화

0

사이트 보안을 강화는 방법으로 불필요하게 포트를 열어 놓았는지 확인해 반드시 필요한 포트만 여는 포트 사용 최적화 방법에 대해서 살펴보도록 하죠.

서버에서 사용하는 포트는 아래와 같이 세가지 주요 그룹으로 나눌 수 있습니다.

  1. 시스템 포트(System ports), 0-1023 – 핵심 서비스와 관련된 포트로 운영 체제에 필수적인 포트
  2. 사용자 포트(User ports), 1024-49151 – IANA가 ‘IETF Review’, ‘IESG Approval’, ‘Expert Review’ 프로세스를 위해 할당한 포트
  3. 동적 포트(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        0      0 127.0.0.53:53           0.0.0.0:*               LISTEN      off (0.00/0/0)
tcp        0      0 0.0.0.0:25              0.0.0.0:*               LISTEN      off (0.00/0/0)
tcp        0      0 127.0.0.1:6010          0.0.0.0:*               LISTEN      off (0.00/0/0)
tcp        0      0 0.0.0.0:443             0.0.0.0:*               LISTEN      off (0.00/0/0)
tcp        0      0 0.0.0.0:*****           0.0.0.0:*               LISTEN      off (0.00/0/0)
tcp        0      0 127.0.0.1:3306          0.0.0.0:*               LISTEN      off (0.00/0/0)
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      off (0.00/0/0)
tcp        0      0 141.164.48.62:443       189.40.73.149:12786     ESTABLISHED off (0.00/0/0)
tcp        0      0 141.164.48.62:443       180.65.25.237:45753     ESTABLISHED off (0.00/0/0)
tcp        0      0 141.164.48.62:443       203.175.39.84:49433     ESTABLISHED off (0.00/0/0)
tcp        0      0 141.164.48.62:443       72.14.199.27:49062      ESTABLISHED off (0.00/0/0)
tcp        0      0 141.164.48.62:443       222.234.131.7:7507      ESTABLISHED off (0.00/0/0)
tcp        0      0 141.164.48.62:443       66.249.79.158:42357     ESTABLISHED off (0.00/0/0)
tcp        0      0 141.164.48.62:443       189.40.73.149:12792     ESTABLISHED off (0.00/0/0)
tcp        0      0 141.164.48.62:443       61.81.11.245:11848      TIME_WAIT   timewait (7.58/0/0)
tcp        0      0 141.164.48.62:443       189.40.73.149:12788     ESTABLISHED off (0.00/0/0)
tcp        0      0 141.164.48.62:443       118.45.130.180:38805    TIME_WAIT   timewait (57.05/0/0)
tcp        0      0 141.164.48.62:443       124.50.187.177:57167    ESTABLISHED off (0.00/0/0)
tcp        0      0 141.164.48.62:443       66.249.79.129:56320     ESTABLISHED off (0.00/0/0)
tcp        0    180 141.164.48.62:*****     124.50.187.177:57295    ESTABLISHED on (0.22/0/0)
tcp        0      0 141.164.48.62:443       66.249.79.120:43419     ESTABLISHED off (0.00/0/0)
tcp        0      0 141.164.48.62:443       211.243.82.156:49304    ESTABLISHED off (0.00/0/0)
tcp        0      0 141.164.48.62:443       66.249.79.159:40840     TIME_WAIT   timewait (34.53/0/0)
tcp        0      0 141.164.48.62:443       211.178.105.144:56345   ESTABLISHED off (0.00/0/0)
tcp        0      0 141.164.48.62:443       66.249.79.118:54414     ESTABLISHED off (0.00/0/0)
tcp        0      0 141.164.48.62:443       66.249.79.158:62256     TIME_WAIT   timewait (2.30/0/0)
tcp        0      0 141.164.48.62:443       222.121.168.2:4079      ESTABLISHED off (0.00/0/0)
tcp        0      0 141.164.48.62:443       121.151.167.84:47722    ESTABLISHED off (0.00/0/0)
tcp6       0      0 :::25                   :::*                    LISTEN      off (0.00/0/0)
tcp6       0      0 ::1:6010                :::*                    LISTEN      off (0.00/0/0)
tcp6       0      0 :::*****                :::*                    LISTEN      off (0.00/0/0)
udp        0      0 127.0.0.53:53           0.0.0.0:*                           off (0.00/0/0)
udp        0      0 141.164.48.62:68        0.0.0.0:*                           off (0.00/0/0)Code language: PHP (php)

또는 netstat 명령대신 요즘 권장된다는 ss 명령을 사용해 봅니다. 조금 더 단순하게 보여줍니다. 간단한 대신 정보가 제한적이네요.

~#ss -ltp
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 4096 127.0.0.53%lo:domain 0.0.0.0:* users:((“systemd-resolve”,pid=556,fd
LISTEN 0 100 0.0.0.0:smtp 0.0.0.0:* users:((“master”,pid=1279035,fd=13))
LISTEN 0 128 127.0.0.1:6010 0.0.0.0:* users:((“sshd”,pid=3070675,fd=10))
LISTEN 0 511 0.0.0.0:https 0.0.0.0:* users:((“nginx”,pid=2951636,fd=7),(“
LISTEN 0 128 0.0.0.0:** 0.0.0.0:* users:((“sshd”,pid=2994821,fd=3)) LISTEN 0 70 127.0.0.1:mysql 0.0.0.0:* users:((“mysqld”,pid=2787110,fd=25)) LISTEN 0 511 0.0.0.0:http 0.0.0.0:* users:((“nginx”,pid=2951636,fd=6),(” LISTEN 0 100 [::]:smtp [::]:* users:((“master”,pid=1279035,fd=14)) LISTEN 0 128 [::1]:6010 [::]:* users:((“sshd”,pid=3070675,fd=9)) LISTEN 0 128 [::]:** [::]:* users:((“sshd”,pid=2994821,fd=4))

이러한 명령을 통해서 확인해보면 제가 열어 놓은 포트는 아래와 같습니다.

  • 도메인 네임 서버 TCP 53
  • 도메인 네임 서버 UDP 53
  • SSH (secure shell) 서버 TCP *(ipv4)
  • SSH (secure shell) 서버 TCP *(ipv6)
  • 웹서버 TCP 443
  • 웹서버 TCP 80
  • SMTP(mail sending) 서버 TCP 25(ipv4)
  • SMTP(mail sending) 서버 TCP 25(ipv6)
  • MySQL 서버 tcp 3306
  • X11 forwarding TCP 6010(ipv4)
  • X11 forwarding TCP 6010(ipv6)
  • BOOTP(부트스트랩 프로토콜) 클라이언트, DHCP로도 사용 UDP 68

이러한 포트중에서 눈에 띄는 것이 ipv4와 ipv6가 모두 사용되고 있다는 것이고, X11 forwarding을 위해 TCP 6010이 열고 있다는 점 정도네요. BOOTP(부트스트랩 프로토콜) 클라이언트는 사용하는지 모르겠어 우선은 그냥 두기로 했습니다.

즉 ipv4만 사용하고 ipv6는 사용 중지하며, X11 forwarding을 위해 TCP 6010도 사용하지 않기로 하죠.

ipv6 비활성화

우분투 서버에서 ipv6 비활성화 방법은 아래와 같이 세가지 방법이 있습니다.

sysctl.conf 설정 수정

먼저 /etc/sysctl.conf 설정을 수정하는 것입니다.

nano  /etc/sysctl.conf Code language: PHP (php)

이 파일 편집 화면에서 아래와 같은 명령을 추가합니다.

net.ipv6.conf.all.disable_ipv6=1
net.ipv6.conf.default.disable_ipv6=1Code language: PHP (php)

grub 설정 수정

위 방법을 사용해도 좋지만 아래에 설명하는 grub를 사용하는 방법이 더 낫다는 평가가 있습니다.

nano /etc/default/grubCode language: PHP (php)

grub 파일 편집 상태에서 아래와 같은 명령을 추가합니다.

GRUB_CMDLINE_LINUX_DEFAULT="ipv6.disable=1"
GRUB_CMDLINE_LINUX="ipv6.disable=1"Code language: PHP (php)

위와 같이 설정을 추가한 후 update-grub 명령을 실행한 후 서버를 부팅하면 적용이 됩니다. ipv6를 사용하지 않을려면 서버 부팅이 필요하군요.

update-grubCode language: PHP (php)

ufw 설정 수정

그런데 우분투에서 보통 그렇듯이 방화벽으로 ufw를 사용한다면 /etc/default/ufw 파일을 수정 설정하면 됩니다.

nano /etc/default/ufwCode language: PHP (php)

이 파일 편집 화면을 보면 IPV6=yes로 되어 있는데요. 이를 no로 변경하면 됩니다. 이 파일 편집 상태를 저장하면 바로 UFW(Uncomplicated Firewall)가 작동하면서부터 이러한 세팅은 적용이 됩니다.

6010 포트 비활성화

이번에는 ssh daemon의 X11 forwarding을 위해 TCP 6010 포트를 사용하지 않는 방법을 살펴보죠.

리눅스 운영체제에서 GUI 방식의 SSH 사용 시 사용한다고 하는데요. 주로 원격으로 떨어져 있는 서버에 접속해 GUI 기반 어플리케이션을 실행시 사용합니다.

우분투가 윈도우즈처럼 GUI 방식으로 발전하기 때문에 SSH 접속 시 이러한 GUI를 지원하기 위한 기능이라고 볼 수 있는데요. 이는 X윈도우 기반 기능이므로 서버와 클라이언트 모두 X윈도우즈 기반이러야 제대로 작동한다고 합니다.

그렇기때문에 웹서버에서는 사용할 일이 거의 없기 때문에 이를 시용할 필요가 없지 않을까요? 사용 포트에 대한 베스트 프랙티스들을 보니 6010 포트는 리스트에 없더군요.

X11 forwarding는 우분투 20.04에서 기본 yes로 설정되어 있기 때문에 /etc/ssh/sshd_config에서 설정을 no로 변경해야 합니다.

nano /etc/ssh/sshd_config Code language: PHP (php)

파일의 맨 아래쪽에 아래와 같은 내용을 볼 수 있는데요. 이중 X11Forwarding yes를 no로 변경하면 됩니다.

#AllowAgentForwarding yes
#AllowTcpForwarding yes
#GatewayPorts no
X11Forwarding yes --> no로 변경
#X11DisplayOffset 10
#X11UseLocalhost yes
#PermitTTY yes
PrintMotd no
#PrintLastLog yes
#TCPKeepAlive yes
#PermitUserEnvironment no
#Compression delayedCode language: PHP (php)

마찬가지로 이 설정 변경 효과는 서버 재시작해야 작동하는 것 같네요.

설정이 제대로 작동하면 ssh 로그인 시 아래와 같은 경고 표시가 뜹니다.

WARNING! The remote SSH server rejected X11 forwarding request.
Welcome to Ubuntu 20.04 LTS (GNU/Linux 5.4.0-40-generic x86_64)Code language: PHP (php)

리눅스 서버 접속 IP 허용 & 사용자 접속 허용하기

0

오늘은 리눅스 서버 보안을 위해서 서버 접속 ip 허용 또는 특정 사용자 접속 허용하는 방법에 대해서 살펴봅니다. 서버 관리를 위해서 글로벌 각지에서 서버에 접속할 필요가 없다면 접속 ip 범위를 최소화하는 것이 필요합니다.

여러번 설명하지만 우분투를 비롯한 리눅스 서버 보안을 위해서 아래와 같은 사항들이 고려됩니다.

  1. ssh 접속 포트를 22번 포트가 아닌 다른 포트 사용
  2. ssh 접속 ip를 특정 ip대로 제한
    . 한국에서만 접속 가능토록 한국 ip로만 한정
    . 집과 이동중인 태블릿 접속 ip대로만 한정
  3. root 사용 중지 및 특정 사용자_ID만 사용
    보안을 위해 사용자_ID를 굉장히 길고 복잡하게 만들어 해커가 사용자_ID 파악을 어렵게 만듬
    그리고 root 사용 중지
  4. 사용자 비밀번호를 보안이 높도록 만들고, 가능하면 정기적 변경
  5. ssh key를 통한 접속으로 사용자_ID와 비밀번호를 통한 ssh 접속 방법이 한계 극복

어떤 ip를 접속 허용할까

서버 보안을 위해서 특정 ip를 허용한다면 어떤 ip를 허용해야할까요? 이를 확인하는 좋은 방법 중의 하나는 그동안 접속했던 ip를 확인해보는 것입니다.

그동안 접속했던 ip는 아래와 같은 명령어를 사용해 확인할 수 있습니다.

~# cat /var/log/auth* | grep Accepted | awk '{print $9"\t"$11"\t"$14}' | sort | uniqCode language: PHP (php)

저의 경우 지난 2개월동안 접속했던 ip를 확인해보니 아래처럼 5개가 확인됩니다.

root    124.50.187.***  ssh2
root    211.36.134.***  ssh2
root    211.36.135.***  ssh2
root    211.36.142.***  ssh2
root    211.36.151.***  ssh2
root    211.36.158.***  ssh2Code language: PHP (php)

이 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에서는 작동하지 않더군요.

PermitRootLogin noCode language: PHP (php)

우분투에서 root 사용 중지는 아래 글을 참고하시면 좋을 것 같습니다.

리눅스 서버 root 사용 중지로 리눅스 서버 보안 강화하기

0

강력한 리눅스 서버 보안 방법 중의 하나인 리눅스 서버 root 사용 중지로 서버 root 로그인 시도를 원천적으로 막음으로서 서버 보안 강화 및 서버 최적화 방법에 대해서 살펴보겠습니다.

대부분 서버의 가장 강력한 권한을 갖는 사용자는 Super User라고 할 수 있는 root 입니다. root만 장악하면 서버 전반을 좌지우지할 수 있기 때문에 해커의 중요한 타겟이 됩니다.

여기에 이 Super user 사용자 이름은 root로 만천하에 알려져 있기 때문에 강력한 컴퓨팅에 의한 스크립트 실행으로 다양한 비밀번호를 대입해 보면서 강제적인 roor 로그인을 시도할 수 있습니다.

그렇기 때문에 여기서는 우분투를 비롯한 리눅스 서버에서 roor를 사용하지 않고 서버를 운영하는 방법에 대해서 살펴보도록 하겠습니다. 아래 내용들은 우분투 20.04를 기준으로 설명합니다.

우분투를 비롯한 리눅스 서버 보안을 위해서 아래와 같은 사항들이 고려됩니다.

  1. ssh 접속 포트를 22번 포트가 아닌 다른 포트 사용
  2. ssh 접속 ip를 특정 ip대로 제한
    예를들어 한국에서만 접속 가능토록 한국 ip로만 한정하든지 또는 집과 태블릿 접속 ip대로만 한정
  3. root 사용 중지 및 특정 사용자_ID만 사용
    보안을 위해 사용자_ID를 굉장히 길고 복잡하게 만들어 해커가 사용자_ID 파악을 어렵게 만듬
    그리고 root 사용 중지
  4. 사용자 비밀번호를 보안이 높도록 만들고, 가능하면 정기적 변경
  5. ssh key를 통한 접속으로 사용자_ID와 비밀번호를 통한 ssh 접속 방법이 한계 극복

우분투 서버 설치 시 기본 설정은 root 미사용

기본적으로 우분투를 설치하면 root를 사용할 수 없도록 되어 있습니다. 왜냐하면 root의 비밀번호가 설정되지 않았기 때문이죠.

그래서 root를 사용하려면 root 비밀번호를 설정한 후 사용할 수 있습니다.

그러나 Vultr와 같은 가상 서버 호스팅에서 우분투 서버를 설치하면 ip 주소가 정해지고, 기본 사용자로 root로 설정되고, root의 비밀번호도 정해집니다.

그래서 ssh 이용 시 주어진 ip주소와 root 사용자 그리고 주어진 root 비밀번호를 사용하게 됩니다.

서버 보안에서 강력히 권고하는 한가지 사항은 root를 사용하지말고 별도 사용자를 등록하고 별도 사용자에게 일부 root 권한을 행사하도록 하는 것입니다.

제가 Vultr에 root 사용자를 사용하지 않기위해 없애는 것이 가능한지 질문을 했는데요. Vultr에서는 불가능하다고 답변이 왔습니다.

그런데 구글링을 해보면 우분투에서 root를 사용하지 않을 수 있고, 오히려 root를 사용하지 말라고 권고가 많습니다. 그리고 위해서 한번 설명했지만 우분투는 기본적으로 root를 비활성한 채 설치된다고 합니다.

그래서 조금 더 고민해보고 구글링에 구글링을 거듭하다가 내린 결론은 제 질문이 잘못되었다는 것을 알 수 있었습니다.

  • root 계정 자체를 없애는 것은 불가능
  • 대신 root 비밀번호를 없애서 root가 없는 것처럼 우분투 서버 운영 가능
    root 비밀번호 설정을 없애는 것은 root로 로그인 상태에서 passwd -dl root 명령으로 가능
  • 따라서 root 비밀번호를 없애고, 특정 사용자를 root 수준의 권한을 준다면(sudo) root없이도 모든 작업이 가능

새로운 사용자 등록 및 sudo 권한 등록

사용자 등록 후 이 사용자에게 sudo 권한을 부여하는 방법은 아래와 같습니다.

위에서도 간단히 설명했지만 가능하는 한 사용자_ID를 복잡하고 유추하기 어렵게 만들어 외부 해커가 사용자_ID 파악을 어렵게 만들고, 더 나아가 비밀번호를 강화해 해킹 가능성을 낮추는 것이 목표입니다.

사용자 등록 및 sudo 그룹 등록

먼저 사용자 등록을 합니다.사용자는 가능하면 어렵고 쉽게 유추할 수 없는 이름을 사용합니다.

adduser 신규_사용자_IDCode language: PHP (php)

이 신규_사용자_ID에 sudo 권한 부여를 위해서는 아래와 같이 sudo 그룹에 이 사용자_ID를 추가해 줍니다.

usermod -aG sudo 신규_사용자_IDCode language: PHP (php)

이 명령은 root로 로그인 했을 때나 신규_사용자_ID로 로그인때나 사용 가능했습니다.

여기까지 진행되었다면 신규_사용자_ID로 sudo를 사용할 수 있습니다.

참고로 일부에서는 사용자 추가 시 동시에 sudo 기능을 추가할 수 있도록 아래와 같이 사용자 추가 시 sudo를 추가해주는 방법을 소개하는데요.

이 방법(아래 명령어)은 우분투 20.04에서는 작동하지 않았습니다.

sudo adduser 신규_사용자_ID sudoCode language: PHP (php)

우분투 서버 root 사용 중지하기

이번에는 우분투 서버에서 root 사용 중지하는 방법에 대해서 알아봅니다.

앞서 설명한대로 root 사용자를 아예 없애는 것은 불가능합니다. 다만 root 비밀번호를 없애서 root를 사용하지 않토록 하는 방법이 있죠.

이는 root로 로그인 상태에서 아래와 같은 명령을 사용합니다. 아니면 특정 사용자로 로그인 상태라면 root 권한(sudo -i 옵션을 사용)을 획득한 상태에서 사용할 수 있겠죠.

불필요한 혼란을 방지하기 위해서 root 로그인 상태에서 아래 명령어를 사용합니다.

sudo passwd -dl rootCode language: PHP (php)

여기서 -l은 root 비밀번호를 lock한다는 의미에서 사용하는 옵션으로 보입니다. 이러면 이후부터는 root로 ssh 로그인이 안되고 root 사용이 안됩니다.

그러면 root를 다시 살리려면 어떻게할까요?

이미 root를 사용할 수 없도록 만들었기 때문 root를 사용하려면 아래 명령어를 사용합니다. 그리고 새로운 root 비밀번호를 설정할 수 있습니다.

sudo -i passwd rootCode language: PHP (php)

결국 sudo 권한을 획득한다는 것은 조금 불편하지만 언제든지 root 권한을 획득 할 수 있다는 것을 의미합니다.

그렇게 때문에 개인 사용자나 작은 스타트업에서는 sudo 권한을 가진 사용자를 만든다면 굳이 root 사용자가 필요없다는 결론에 이르고 이것이 더 보안에 유리한 것이 아닐까 싶습니다.

특정 사용자를 root처럼 사용하기

기업의 IT팀이 아니라면 root를 두고 여러 사용자를 등록시켜 일정 권한을 주는 방식이 바람직하기 때문에 root도 필요하고 각기 다른 낮은 사용 권한을 가진 사용자들도 필요합니다.

그러나 개인이 운영하는 서버이거나 작은 스타트업이라면 서버 운영 시 여러 사용자를 둘 필요는 없습니다.

단 한명의 사용자면 충분하죠. 귀찮은 경우 root를 그냥 사용하기도 합니다. 그렇지만 root를 그대로 사용하는 하는 것은 해킹 위험에 더 노출됩니다.

서버 공략 시 사용자로 root를 설정하고 여기에 다양한 비밀번호를 대입해 서버 로그인을 시도하기 때문입니다.

어짜피 서버에서는 root에 준하는 권한을 가진 한 사람은 반드시 필요하기 때문에 root를 사용중지하고 대신 특정 사용자_ID에 root에 준하는 권한을 주는 것이 더 효율적입니다.

해커는 root가 아닌 특정 사용자_ID를 찾아야 하고 거기서 또 비밀번호를 찾아야하기 때문에 그만큼 해킹 확률을 낮출 수 있습니다.

거기에 fail2Ban과 같은 보안 프로그램과 결합한다면 더욱 더 해킹 가능성을 낮출 수 있습니다.

  1. ssh 접속 포트를 22번 포트가 아닌 다른 포트 사용
  2. ssh 접속 ip를 특정 ip대로 제한
    예를들어 한국 ip대로만 한정하든지 또는 집과 태블릿 접속 ip로만 한정
  3. root를 사용하지 않고 특정 사용자_ID만 사용
    보안을 위한 사용자_ID를 굉장히 길고 복잡하게 만드는 경우도 있는 어짜피 ssh나 sftp 사용 시 사용자_ID를 등록해 놓기 때문에 길고 복잡한 사용자_ID가 불편하지는 않음

특정 사용자_ID에게 임시로 root 권한을 부여하는 것은 아래 명령으로 가능합니다.

sudo -iCode language: PHP (php)

이러면 사용자_ID 비밀번호를 묻는데 비밀번호를 입력하면 마치 root처럼 사용할 수 있습니다.

사용자를 root처럼 이용하기

아시다시피 sudo 기능을 사용하려면 사용자_ID 비밀번호를 다시 입력해야 합니다. 보안을 위해서이지만 매넌 비밀번호를 입력하기가 귀찮을 수 있겠죠.

보안을 조금 포기하고 편리함에 더 방점을 둔다면 sudo 사용을 위해서 비밀번호 입력하지 않아도 되도록 만들 수 있습니다.

아래 visudo 명령을 사용하면 sido 사용자 권한을 조정할 수 있습니다.

sudo visudo

또는 

nano /etc/sudoersCode language: PHP (php)

그러면 /etc/sudoers 파일을 편집할 수 있는데요. 여기에서 root에만 허용되어 있는 NOPASSWD 권한을 사용하려는 사용자_ID에도 부과합니다.

즉 User privilege specification 부분에 사용자_ID ALL=(ALL) NOPASSWD: ALL을 추가합니다. 아래와 같은 모습이 됩니다.

root ALL=(ALL) NOPASSWD: ALL
사용자_ID ALL=(ALL) NOPASSWD: ALLCode language: PHP (php)
서버 보안, 일반 사용자를 root 사용자처럼 만들기-사용자 권한 부여

물론 이러한 명령어는 한번 더 비밀번호 체크 기능을 없애면서 보안이 더 낮아지는 효과가 있겠지만 사용자 입장에서는 로그인 후 매번 sudo 사용을 위해 비밀번호를 입력하는 불편함을 줄일 수는 있습니다.

편의성을 높이느냐 아니면 보안을 유지하느냐는 선택의 문제입니다.

uid와 gid를 root처럼 만든다.

다름으호 할 것은 사용자 ID와 그룹 ID를 root처럼 만드는 것입니다.

nano /etc/passwdCode language: PHP (php)

여기 맨 위를 보면 root 설정 내용을 볼 수 있는데요. root는 uid와 gid 값이 0입니다. 일반 사용자는 값이 1000으로 책정되어 있습니다.

여기서 사용자의 uid와 gid를 root처럼 0으로 변경합니다.

서버 보안, 일반 사용자를 root 사용자처럼 만들기-uid와gid를 root처럼 0으로 변경

root 그룹에 일반 사용자 추가

다음으로 할 것은 root 그룹에 일반 사용자 계정을 추가하는 것입니다.

nano /etc/groupCode language: PHP (php)

여기 맨위가 root 그룹을 나타내는데요. 여기서 맨위 root:x:0: 다음에 일반 사용자 계정을 추가하면 됩니다.

서버 보안, 일반 사용자를 root 사용자처럼 만들기- root 그룹에 일반 사용자 계정 추가

이어면 일반 사용자 계정으로 마치 root처럼 사용할 수 있습니다. 그러면 이제는 root를 굳이 사용할 필요가 없기 때문에 root 사용 중지시키면 됩니다.

sudo passwd -dl rootCode language: PHP (php)

참고

우분투 20.04와 PHP 8 기반 워드프레스 설치 방법

가상 서버를 운영하고픈 勇者에게 전하는 가상 서버 운영 입문 노하우 – Vultr 가상서버호스팅(VPS)를 중심으로

워드프레스 최적화를 위한 18개월간의 고민, 그 노하우를 담다.

도쿄 리젼과 비교해 본 Vultr 서울 리젼 사용기

가성비가 뛰어난 Vultr 가상서버호스팅(클라우드호스팅,VPS) 사용기

우분투 서버 보안 자동 업데이트 및 업데이트 메일 통보 방법

0

사이트 운영이든 프로그램이나 앱 사용 시 보안 업데이트는 반드시 적용해야합니다. 우부투 서버 운영 시 우분투 서버 보안 자동 업데이트 및 업그레이드 방법 및 이 결과 메일 통보 방법에 대해 살펴봅니다.

서버 보안 업데이트의 필요성

인터넷이 발전하면서 인터넷 사용자가 증가하고, 인터넷을 이용한 비지니스가 크게 융성해지고 있기 때문에 이에 편승한 해킹 위험이 크게 증가하고 있습니다.

테스트 또는 교육용으로 해킹하는 경우도 있겠지만 대부분은 무엇인가 이득을 노리는경우가 많죠. 그리고 이는 사용자가 많아질수록, 관련된 비지니스가 커질수록 시도는 증가하기 마련입니다.

이렇게 해킹 위험이 증가하고 프로그램등이 빠르게 변하면서 그동안 생각하지 못했던 위약점들이 나타나고 예전에 몰랐던 취약점을 새롭게 발견하기도 합니다.

그렇기 때문에 이러한 취약점들은 보완하는 보안 업데이트는 굉장히 자두 이루어집니다. 서버나 사이트를 안전하게 지키려면 이런 보안 업데이트를 바로바로 실행해 주는 것이 필요합니다.

실제로 해깅등은 이러한 업데이트를 무시하고 방치된 서버나 사이트에서 주로 일어나고 있다는 것도 보안 업데이트 및 업그레이드의 중요성을 웅변해 준다고 할 수 있습니다.

그리고 이런 업데이트 및 업그레이드 시 반드시 사전 백업이 필수적입이죠. 어떤 일이 닐어날지 모르기 때문이죠.

그런데 전업으로 서버 운영을 하는 담당자가 있다면 매일 일정 시간에 안정적을 상황을 파악해 업데이트 및 업그레이드를 진행 후 문제가 없는지 확인할 수 있지만.. 대부분의 경우는 그렇지 않고 서버에 접속할 여유가 적습니다.

그렇기 때문에 최소한 보안 업데이트 및 업그레이드는 자동으로 이루어지고 이를 메일로 통보받는 것이 필요합니다. 그리고 이러한 업데이트는 가능하면 사용하지 않는 새벽 시간에 이루어 지도록 설정하는 것이 필요할 것입니다.

  • 보안 업데이트 전에 사전 백업
  • 보안 업데이트는 자동 실행
  • 보안 업데이트로 시스템 부팅이 필요한 경우, 시스템 부팅은 사용자가 적은 새벽 시간에 진행
  • 보안 업데이트 내용은 메일로 송부

우분투 서버 보안 자동 업데이트 방법

여기서는 운분투 서버 운영 시 보안 자동 업데이트 방법에 대해서 살펴봅니다.

어느 운영체제나 마찬가지겠지만 리눅도도 필요가 있으면 이를 지원하는 다양한 방법들을 제공되고 있습니다.

우분투 리눅스에서는 보안 업데이트가 발생 시 관련 내용만 업데이트하도록 하는 unattended-upgrades라는 프로그램이 있습니다. 오늘은 이를 이용해 우분투 서버 자동 업데이트 방법에 대해서 알아보도록 하겠습니다.

unattended-upgrades 프로그램 설치

먼저 서버 업데이트 및 업그레이드를 통해서 최신 상태로 만듭니다.

sudo apt update && sudo apt upgradeCode language: PHP (php)

다음으로는 위에서 이야기한 unattended-upgrades 프로그램을 설치합니다. 이는 아래 명령을 사용합니다. 별다른 메일 프로그램이 설치되어 있지 않으면 postfix를 설치합니다.

sudo apt install unattended-upgrades apt-listchanges bsd-mailxCode language: PHP (php)

지메일 릴레이 서비를 이용해 우분투 서버에서 메일 발송 시스템 구축에 대해서는 아래 글을 참조하시기 바랍니다.

설치를 시작하다가 중간에 이 프로그램이 서버 용량을 사용할 것인데 설치를 진행할 것인지 질문합니다. 사용하겠다는 용량은 기껏 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 'sudo apt autoremove' to remove them.
Suggested packages:
  www-browser x-terminal-emulator
The following NEW packages will be installed:
  apt-listchanges bsd-mailx
0 upgraded, 2 newly installed, 0 to remove and 31 not upgraded.
Need to get 150 kB of archives.
After this operation, 588 kB of additional disk space will be used.
Do you want to continue? [Y/n] YCode language: PHP (php)

설정 보안 업체이트 허용

다음으로는 강제 보안 업데이트를 허용합니다. 아래와 같은 명령을 사용하면 중간에 unattended-upgrades를 사용하도록 설정할지 질문하는 팝업이 뜹니다.

sudo dpkg-reconfigure -plow unattended-upgradesCode language: PHP (php)

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 세부 옵션

이제 unattended-upgrades 세부 옵션을 정합니다. 이는 아래 파일을 편집합니다.

nano /etc/apt/apt.conf.d/50unattended-upgradesCode language: PHP (php)

저는 아래 몇가지를 조정했습니다.

  1. 업데이트 결과를 받을 이메일 주소 설정(73번째 줄)
  2. 보안 업데이는 종종 우분투 서버 리부팅이 필요할 경우가 있습니다. 이 경우 관리자 확인없이 리부팅 허용(93번째 줄)
  3. 서버 리부팅이 필요한 경우 리부팅 시간을 위험이 적은 새벽 시간으로 설정(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에서 설정합니다.

 nano /etc/apt/listchanges.confCode language: PHP (php)

여기 설정은 매우 간단한데요. 아래와 같은 항목으로 구성되어 있고 이중 메일 주소를 설정하면 됩니다.

[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)

테스트

아래와 같은 명령을 사용해 제대로 작동하는지 확인해 봅니다.

sudo unattended-upgrades --dry-runCode language: PHP (php)

그러면 아래와 같은 형식으로 변경된 내용을 출력해 줍니다. 물론 근래에 업데이트 및 업그레이드를 통해서 업데이트할 내용이 없다면 아무런 메세지를 보내지 않습니다.

# sudo unattended-upgrades --dry-run
apt-listchanges: Reading changelogs...
apt-listchanges: Reading changelogs...
/usr/bin/dpkg --status-fd 10 --no-triggers --unpack --auto-deconfigure /var/cache/apt/archives/snmp_5.8+dfsg-2ubuntu2.1_amd64.deb /var/cache/apt/archives/libsnmp35_5.8+dfsg-2ubuntu2.1_amd64.deb 
/usr/bin/dpkg --status-fd 10 --configure --pending 
apt-listchanges: Reading changelogs...
apt-listchanges: Reading changelogs...
/usr/bin/dpkg --status-fd 10 --no-triggers --unpack --auto-deconfigure /var/cache/apt/archives/glib-networking-common_2.64.2-1ubuntu0.1_all.deb /var/cache/apt/archives/glib-networking_2.64.2-1ubuntu0.1_amd64.deb /var/cache/apt/archives/glib-networking-services_2.64.2-1ubuntu0.1_amd64.deb 
/usr/bin/dpkg --status-fd 10 --configure --pending 
apt-listchanges: Reading changelogs...
apt-listchanges: Reading changelogs...
/usr/bin/dpkg --status-fd 10 --no-triggers --unpack --auto-deconfigure /var/cache/apt/archives/libsnmp-base_5.8+dfsg-2ubuntu2.1_all.deb 
/usr/bin/dpkg --status-fd 10 --configure --pending 
apt-listchanges: Reading changelogs...
apt-listchanges: Reading changelogs...
/usr/bin/dpkg --status-fd 10 --no-triggers --unpack --auto-deconfigure /var/cache/apt/archives/libseccomp2_2.4.3-1ubuntu3.20.04.2_amd64.deb 
/usr/bin/dpkg --status-fd 10 --no-triggers --configure libseccomp2:amd64 
/usr/bin/dpkg --status-fd 10 --configure --pending 
apt-listchanges: Reading changelogs...
apt-listchanges: Reading changelogs...
/usr/bin/dpkg --status-fd 10 --no-triggers --unpack --auto-deconfigure --recursive /tmp/apt-dpkg-install-m8fgGO 
/usr/bin/dpkg --status-fd 10 --configure --pending 
apt-listchanges: Reading changelogs...
apt-listchanges: Reading changelogs...
/usr/bin/dpkg --status-fd 10 --no-triggers --unpack --auto-deconfigure /var/cache/apt/archives/linux-libc-dev_5.4.0-40.44_amd64.deb 
/usr/bin/dpkg --status-fd 10 --configure --pending Code language: PHP (php)

참고

이는 우분투 서버 보안 자동 업데이트 방법은 아래 글을 참조해 테스트 결과를 기반으로 재정리 했습니다.

아래 참고글은 우분투 18.04를 기준으로 했지만 저는 우분투 20.04에서 테스트해 보았습니다.

How to set up automatic updates for Ubuntu Linux 18.04

그리고 메일을 실행시마다 보내는 것은 아니고 문제가 발생하거나 패키지 업그레이드(problems or packages upgrades)가 발생시에만 보내게 됩니다.