Configuring Linux Web Server


우리가 웹 애플리케이션에 접근할 때마다, 브라우저는 이미지나 파일 등을 포함해 여러 요청을 인터넷 상의 서버에 전달합니다. 그래서 웹 서비스 운영에 있어서 서버를 어떻게 설정하고 운영하는지에 대한 이해가 필요합니다. 이번 글을 통해서는 이에 대해 살펴볼거에요.

우선 리눅스 파일시스템 등 기초적인 개념을 살펴보겠습니다. 리눅스의 유저 관리, Permission System, firewall과 같은 보안에 대한 개념도 알아볼게요. 그리고, 웹서버와 SQL 데이터베이스를 이용해 웹 애플리케이션을 직접 구동해 보도록 할게요.

서버를 처음부터 직접 구성해 보고, 각 기능을 직접 컨트롤 해 보는 것은 기초적이지만 중요하며, 자신이 만든 웹 애플리케이션이 어떻게 세상과 소통하는지에 대해 이해할 수 있습니다.


Contents

  • What's a Virtual Machine?
  • Linux Distribution
  • Virtual(Linux) Box 설정
  • Linux Security

    • The Rule of Least Priviliege
    • Packages
    • User Management

      • Create a New User
      • Give Sudo Access
      • key based authentication
      • Generate key-pair
      • Install Public Key
    • Force Key Based Authentication
    • File Permissions

      • Owner and Group
      • 파일 모드 변경
      • chgrp and chown
    • Firewalls
  • Conclusion

What's a Virtual Machine?

Virtual Machine(VM)은 컴퓨터를 시뮬레이터 하는 컴퓨터 프로그램입니다. 여기서 VM 소프트웨어를 설치 후 guest operating system으로 VM에 Linux를 설치할 거에요. 참고로, 실제 컴퓨터에 설치된 Operating System(OS)은 host OS라고 합니다.

프로그래머가 Virtual Machine을 사용하는 몇 가지 이유가 있다고 합니다.

VM을 사용하면, 매일 작업하는 환경에서 영향을 받지 않는 독립된 환경을 구축할 수 있습니다. 프로그래머는 guest OS에 프로그램을 설치하고 다른 환경에 영향을 받지 않는 개별적인 설정을 할 수 있습니다.

VM은 또한 환경을 시뮬레이터 할 때도 사용할 수 있습니다. 대부분의 개발자는 Window나 Mac OS를 사용하는데, 가끔은 Linux와 같은 다른 환경에 배포해야 하는 경우도 있습니다. 이럴 때, Linux VM을 사용하면 현재의 host OS에서도 다른 환경의 OS에서 코드를 실행해 볼 수 있습니다.

Linux Distribution

많은 리눅스 버전은 무료로 사용할 수 있습니다. 오픈소스에서 free as in speech란 용어로 사용되기도 하는데, 이 용어는 소프트웨어를 무료로 사용할 수도 있고, 내부 소스코드를 볼 수도 있고 업데이트 후 재배포도 가능하다는 것을 의미합니다. 이와 비슷한게 free as in beer란 용어도 있습니다. 이는 무료로 소프트웨어를 사용할 수 있지만 어떠한 수정이나 내부 로직은 볼 수 없는 것을 의미합니다.

리눅스는 free as in speech이기 때문에 여러 버전의 리눅스가 있습니다.

Debian은 안정성, 신뢰성이 있는 것으로 잘 알려져 있습니다. 많은 Debian 서버가 리부트 없이 수년간 운영되고 있다고 합니다. 그렇기 때문에, 소프트웨어 업데이트 사이클이 매우 느립니다.

Ubuntu는 Debian에서 파생된 버전입니다. Ubuntu는 그 자체 만으로도 여러 버전이 있고, 서버나 데스크탑, 모바일 용으로 쉽게 사용할 수 있습니다. 이 블로그에서는 Ubuntu를 사용할 거에요.

redhat의 경우는 기업용으로 특화된 버전이며 유료로 금액을 지불하고 소프트웨어 사용과 기술 지원을 받을 수 있습니다.

CoreOS는 Clustered, containerized 앱에 특화된 distribution 입니다.

Virtual(Linux) Box 설정

리눅스 설치를 위해 Vargant를 먼저 설치하고 Ubuntu를 설치할 것입니다. 아래와 같이 일부 설정이 필요하지만, 설치 후 자신의 컴퓨터가 윈도우든 OSX든 상관없이 같은 리눅스 환경에서 작업을 할 수 있습니다.

  • Install Virtual Box Link
  • Install Vagrant Link
  • 작업할 임의의 폴더를 생성하고, vagrant init ubuntu/trusty64를 입력합니다.
  • 생성한 폴더에서 vagrant up 명령어로 Linux OS를 설치합니다.
  • 설치가 완료되면 vagrant ssh 명령어를 실행합니다.

Vagrant는 VM 설정을 할 수 있도록 해주며, host computer와 VM filesystem간 파일을 공유할 수 있도록 해 줍니다. 위 과정을 마치면 커맨드창에 아래와 같은 prompt를 볼 수 있고, 이제 Shell 명령어를 입력할 수 있습니다.

vagrant@vagrant-ubuntu-trusty-64:~$

위와 같이 설정을 하면 여러분의 컴퓨터 위에서 가상의 컴퓨터 한 대가 작동하고 있습니다. 가상화 단계를 제외하면, 이렇게 우리가 설정한 환경이 실제 웹서버를 구축하는 것과 크게 다르지 않습니다.

AWS를 예로 들면 가상화된 컴퓨터 한 대를 받고 우리는 그 위에 서버를 설정 후 SSH를 통해 로그인 할 수 있습니다. 여기서 우리가 연습한 방식과 유사합니다. 가상화된 독립된 컴퓨터에 SSH로 접근해 작업을 할 수 있고, 이 작업이 여러분의 실제 컴퓨터에 영향을 주지는 않습니다.

다시 ssh로 접속하면 아래와 같이 홈 디렉토리에 위치합니다.

홈 디렉토리는 개인적인 데이터를 저장할 수 있는 공간이지만, OS에서 일부 파일을 기본적으로 추가해 놓습니다.

참고로, ls -al 출력 후 d로 시작하는 것은 폴더이고, dash로 시작하는 것은 파일을 나타냅니다. 웹서버 및 데이터베이스를 구축하며 root 폴더의 내용도 일부 수정해야 하기 때문에, root 폴더의 내용도 조금 살펴볼게요.

cd /를 입력하면 root 폴더로 이동합니다.

etc 폴더에는 설정 파일이 위치합니다. 웹서버와 데이터베이스 설정 시 etc 폴더 내 파일을 일부 수정해야 합니다.

var 폴더에는 시스템, 애플리케이션 로그와 같이 크기가 커지는 파일이 위치합니다.

bin 폴더는 모든 유저가 접근할 수 있고 실행할 수 있는 ls 명령어와 같은 애플리케이션 바이너리가 위치합니다.

sbin 폴더는 시스템 유지와 관련된 파일이 위치하고 root 유저만 접근할 수 있습니다.

usr 폴더는 user program을 위한 폴더입니다. bin과 다른점은 bin은 부팅과 시스템 유지를 위해 필요한 것이 저장돼 있고 usr에는 그렇지 않은 것이 저장돼 있습니다.

Linux Security

웹서버 런칭 시 애플리케이션과 유저 보호를 위해 보안을 시경써야 합니다. 보안과 관련해 유저, 패키지, 소프트웨어 관리, 유저 인증 등에 대해 살펴보고 리눅스의 File Permission과 Firewall에 대해서도 살펴보겠습니다.

The Rule of Least Priviliege

user나 특정 애플리케이션이 목적하는 작업만 할 수 있는 최소한의 권한만 가지도록 하는 것이 보안상 좋습니다. 앞서 리눅스 설정 및 ssh 접속 후 vagrant란 유저명으로 로그인이 됩니다. 이 계정으로 권한이 없는 폴더에 접근하면 permission denied란 메세지를 전달 받습니다.

~$ ls -al /home/ubuntu/.ssh
ls: cannot open directory /home/ubuntu/.ssh: Permission denied

위 폴더는 ubuntu 유저나 root 유저만 접근할 수 있습니다. root 계정으로 접근하기 위해서는 super user에 대한 이해가 필요합니다.

Super User

모든 리눅스는 root라는 계정이 있고, 이 계정으로는 해당 OS에서 어떠한 작업도 가능합니다. 일반적으로 리모트 리눅스 접근 시 root 계정으로 접근은 하지 못 합니다. 다른 일반 계정으로 접근 후 필요 시 별도의 명령어를 통해 루트 계정 권한을 사용할 수 있습니다. 위 예에서 sudo를 ls 앞에 붙이면 출력된 내용을 확인할 수 있습니다. 모든 유저가 super user 권한으로 작업을 할 수 있는 것은 아니고, 유저 생성 시 권한을 부여할 수 있습니다.

이와 같이 서버 공격자가 알 수 있는 유저명을 배제함으로써, 서버 공격에 대한 보안을 좀 더 어렵게 할 수 있습니다.

참고로, 가능하면 su 명령어는 최소한으로 사용하는 것이 좋습니다. su 명령어를 사용하면 root 계정의 권한으로 계속 작업을 하기 때문에, 실수로 명령어를 잘 못 입력하면 OS에 데미지를 줄 수도 있습니다.

Packages

OSX의 경우 필요한 소프트웨어를 앱스토어에서 다운로드 할 수 있죠. 리눅스에는 package source list가 있습니다. cat /etc/apt/sources.list를 입력하면 이용 가능한 package 리스트를 확인할 수 있습니다.

위 리스트를 최신의 상태로 업데이틑 하려면, sudo apt-get update 명령어를 이용할 수 있습니다. 이 명령어로 소트프웨어 자체를 업데이트 하지는 않습니다. /etc/app/sources.list에 있는 저장소에서 이용 가능한 소프트웨어와 버전을 확인하는 과정을 해당 명령어를 통해 수행합니다.

소프트웨어 자체를 업데이트 하기 위해서는 sudo apt-get upgrade 명령어를 실행합니다. root 계정의 권한이 필요하기 때문에 sudo를 또 붙여야 합니다.

https://packages.ubuntu.com 에서 이용 가능한 ubuntu 패키지를 검색할 수 있습니다. finger 패키지를 설치 후 실행해 보면, 홈디렉토리, 이용중인 Shell 종류와 같은 유저 정보를 확인할 수 있습니다.

앞서 사용한 finger는 어떻게 유저에 대한 정보를 알 수 있었을까요? 대부분의 정보는 유저에 대한 정보를 담고 있는 파일인 /etc/passwd에서 확인할 수 있습니다. cat 명령어를 이용해 해당 파일의 내용을 확인해 보겠습니다.

vagrant@vagrant-ubuntu-trusty-64:/$ cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync

// 생략...

vagrant:x:1000:1000::/home/vagrant:/bin/bash
colord:x:106:112:colord colour management daemon,,,:/var/lib/colord:/bin/false
statd:x:107:65534::/var/lib/nfs:/bin/false
puppet:x:108:114:Puppet configuration management daemon,,,:/var/lib/puppet:/bin/false
ubuntu:x:1001:1001:Ubuntu:/home/ubuntu:/bin/bash

출력 내용의 각 라인은 개별 유저 엔트리를 나타냅니다. 각 라인은 콜론으로 구분된 필드로 구성 돼 있고, 필드별 유저 관련 정보가 저장 돼 있습니다.

username:password:UID:GID:UID info:home directory:command/shell
  • username: 유저 로그인 명
  • password: 비밀번호 (암호화 시 x로 표기)
  • user ID (UID): 시스템 상 유저 아이디. 0은 root이며, 1 ~ 99까지는 predefined 유저로 할당됨. 100 ~ 999까지 다른 시스템 상 유저를 위해 사용할 수 있음.
  • group ID (GID): 그룹 ID (/etc/group)
  • user ID info: 유저명, 이메일, 전화번호 등 유저 관련 메타데이터
  • home directory: 로그인 시 유저가 접근하는 경로
  • command/shell: 사용하는 command 또는 shell의 절대 경로

암호화된 비밀번호를 위 경로에 저장해도 하드웨어가 암호화된 비밀번호를 푸는데 너무 느려 큰 문제가 없었다고 합니다. Ubuntu에서는 암호화된 비빌번호 대신 x로 표기합니다.

User Management

모든 리눅스에는 root 계정이 존재하는 것을 누구나 알고 있기 때문에, 앞서 root 계정으로는 로그인이 불가능 하도록 하고, sudo 권한이 있는 유저로 로그인 하는 것이 보안을 위한 일반적인 패턴이라 언급했습니다. vagrant 자체에서 vagrant라는 계정을 설정해 로그인 할 수 있도록 이 과정을 처리해 주었습니다. 모든 호스팅 업체에 이 같은 설정이 있는 것은 아니기 때문에, 직접 어떻게 하는지 살펴보겠습니다.

Create a New User

adduser 명령어로 유저를 추가해 줍니다.

유저를 추가했으니, 해당 유저 계정으로 리눅스 서버에 ssh로 접속할 수 있습니다.

ssh student@127.0.0.1 -p 2222

접속할 IP 주소에 localhost 주로를 기입하고, port 넘버는 2222로 입력합니다. vagrant에서 기본적으로 local machine이 접속할 포트를 2222로 설정해 주기 때문이에요. 위 명령어를 입력하고 student 계정의 비밀번호를 입력하면 리눅스 서버로 로그인이 됩니다.

새로 생성한 student란 계정으로 sudo 권한의 명령어를 실행하면, 이 계정은 권한이 없기 때문에 에러 메세지가 출력됩니다.

Give Sudo Access

sudo 명령어에 대한 권한이 있는 유저 리스트는 etc/sudoers 파일에 위치합니다. sudo 권한이 있는 계정으로 접속 후 해당 파일을 읽으면 아래와 같은 내용을 확인할 수 있습니다.

visudo 명령어로 이 파일에 직접 유저를 추가할 수도 있지만, 기본 설정 외의 추가 사항은 별도의 파일로 관리하는 것이 더 좋습니다. 또한, 배포판이 수정 되어도 커스텀 설정을 잃지 않을 수 있습니다. 하단에 보면 #includedir /etc/sudoers.d란 명령어가 있는데, sudoers.d란 별도 폴더의 내용을 해당 내용에 포함한다는 명령어 입니다.

이 sudoers.d 폴더에서 student 계정을 추가해 sudo 권한을 허가할 수 있습니다. /etc/suders.d/vagrant 파일을 student란 파일명을 복사 후 파일 내용을 아래와 같이 수정 후 저장해 주면 student 계정으로 sudo 명령어를 사용할 수 있습니다.

sudoers 관련 내용을 더 알고 싶으면 하기 여기를 참고해 주세요.

key based authentication

비밀번호 방식으로 로그인을 할 수 있지만, 그리 좋은 방법은 아니고 보안에도 안 좋을 수 있습니다. 인증 관련 해서는 다른 글에 좀 더 구체적으로 작성했습니다. 비대칭 암호화 방식(공개 키 암호화 방식)은 별도의 파일을 서버와 클라이언트에 각각 두고 인증을 진행하는 방식입니다.

이 방식에서 서버는 임의의 메세지를 클라이언트로 전달합니다. 클라이언트는 전달 받은 임의의 메세지를 private key로 암호화 하고, 암호화 된 메세지를 서버로 전달합니다. 서버는 Public key로 메세지를 해독 후 그 값이 자신이 보낸 메세지와 같은지 확인해 인증을 완료하는 방식입니다.

Generate key-pairs

private-key는 자신 말고 다른 누군가에게 노출이 되면 안 됩니다. 때문에, 로컬 머신에서 키 페어를 생성해야 합니다. ssh-keygen을 통해서 key-pair를 생성할 수 있습니다.

ssh-keygen으로 키 페어를 생성하면 위 사진과 같이 두 개의 파일이 생성됩니다. .pub 파일이 공개키로 서버에서 가지고 있을 파일입니다.

Install Public Key

키를 생성했으니, 다음으로 새로 생성한 공개키를 서버에 위치시켜야 합니다. 이 작업을 하는 여러 방법이 있고 애플리케이션도 존재하지만, 직접 해 보도록 하겠습니다.

  1. 우선 student 계정으로 리눅스에 접속 후 .ssh 폴더를 생성합니다. 키와 관련된 파일은 이 폴더에 저장해야 합니다.
  2. 폴더 내 공개키를 저장할 파일을 생성합니다. (touch .ssh/authorized_keys)
  3. 에디터로 생성한 파일을 열고, 공개키를 복사 후 붙여넣기 합니다.
  4. 다른 유저가 해당 파일에 접근할 수 없도록 file permissions을 설정해 줍니다. file permission 관련해서는 뒤쪽에서 좀 더 알아볼게요.

여기까지 설정을 모두 마쳤고, 이제 비밀번호가 아닌 ssh를 이용해 아래와 같이 student 계정으로 리눅스 서버에 로그인 할 수 있습니다.

Force Key Based Authentication

리눅스 서버에 student라는 별도 계정을 생성했고, ssh로 로그인 할 수 있도록 공개키 방식을 설정 완료 했습니다. 이제 비빌번호로 로그인 할 필요가 없으니, 공개키 방식으로만 로그인 하도록 설정해 줄 수 있습니다. 이 설정은 /etc/ssh/sshd_config 파일에서 쉽게 설정할 수 있습니다.

수정한 설정을 적용하기 위해 sudo service ssh restart 명령어로 서비스를 재시작하면, 이제 ssh로만 로그인을 진행할 수 있습니다.

File Permissions

앞서 공개키를 서버에 설치하며, 공개키가 있는 폴더와 공개키 파일의 파일 퍼미션을 수정했습니다. 파일과 디렉토리의 접근권은 읽기 권한, 쓰기 권한, 실행 권한으로 정의할 수 있습니다. 홈 디렉토리에서 ls 명령어로 각 파일, 폴더의 접근권을 확인할 수 있습니다.

위에서도 잠깐 언급했듯이, 10개의 문자 중 첫 문자는 파일 종류를 의미하고, 나머지 9개의 문자는 파일 모드로 불립니다. 이 파일 모드는 파일 소유자, 파일 소유 그룹, 기타 사용자에 대한 읽기, 쓰기, 실행 권한을 나타냅니다.

.bashrc 파일의 속성 -rw-r--r--을 예로 살펴보겠습니다. 첫 부분 rw-는 파일의 오너가 읽고, 쓰기 가능함을 나타냅니다. 만약 오너가 실행 가능한 파일이있다면 rwx로 나타납니다. 다음으로 r--는 그룹 내 유저가 읽기만 가능함을 의미합니다. 그리고, 마지막 r--는 기타 사용자가 해당 파일의 읽기만 가능함을 나타냅니다.

Owner and Group

파일 속성 다음으로 두 개의 컬럼에 student student 값이 있는 것 확인할 수 있습니다. 이 두 컬럼은 각각 owner와 group을 의미합니다. 두 개가 같은 값이지만 의미하는 바는 다릅니다. 위에서 유저를 생성할 때 리눅스 시스템에서 같은 유저명으로 그룹명을 자동으로 생성해 줍니다. ..의 owner와 group은 root root 로 돼 있는데 이것은 해당 엔트리가 부모 폴더를 가리키고 부모 폴더의 owner, group이 root이기 때문입니다.

chmod

파일 모드는 8진법 도는 문자 표기법으로 변경이 가능합니다. 8진숫의 각 숫자는 3자리의 2진수를 표현하기 때문에 파일 모드를 저장하기에 적합합니다. 각 파일 모드 별 매핑된 2진수, 8진수가 아래와 같이 있습니다.

8진수 - 2진수 변환이 불편해 보이면, r=4, w=2, x=1, 권한 없음은 0으로 표기 한다는 것을 알면 어느정도 사용할 수 있습니다. .bashrc의 파일 속성을 8진법으로 변경해 보면 644가 되는 것을 알 수 있습니다.

chgrp and chown

다시 홈 디렉토리의 .bash_history 파일 속성을 살펴보면 student 유저만 읽고 쓰기가 가능합니다. 해당 파일은 유저가 입력한 모든 명령어가 담겨 있기 때문에 보안상 해당 유저만 접근할 수 있습니다.

만약 .bashhistory 파일의 group을 `sudo chgrp root .bashhistory명령어로 변경한다고 해도, student 계정으로 파일을 읽을 수 있습니다. group은 해당 파일에 권한이 없기 때문입니다. 하지만,sudo chown root .bash_history`로 owner를 변경하면 student 계정으로 파일을 읽을 수 없습니다.

Firewalls

리눅스 서버에 대해 알아 보았고, 애플리케이션에 따라 웹 요청이나 데이터베이스 쿼리, 이메일 등을 처리할 수 있을 것입니다. 또한, 위에서 접속한 것과 같이 ssh 접속을 핸들링 할 수도 있습니다. 그리고 각 애플리케이션은 특정 포트에만 응답함으로써 요청 타입에 따라 핸들링 할 수 있습니다. 기본적으로 HTTP는 80포트, HTTPS는 443, SSH는 22, FTP는 21, POP3는 110, SMTP는 25가 default 입니다.

Firewall 애플리케이션을 이용해 어떠한 포트에 대한 요청을 우리의 서버가 핸들링 할 지를 설정할 수 있는데, 이에 대해 살펴보겠습니다. ubuntu에서는 firewall 설정을 위해 ufw을 사용할 수 있습니다. 세부 설정을 하기 전 우선 incoming 요청에 대한 전체 포트를 막고, outgoing은 열어두도록 하겠습니다.

다음으로 아래와 같이 명령어를 입력해 ssh와 http에 대한 설정을 합니다.

  • sudo ufw allow 2222/tcp
  • sudo ufw allow www
  • then, sudo ufw enable

우선적으로 ssh로 접속이 가능하도록 ssh 설정을 합니다. 일반적으로 sudo ufw allow ssh로 입력하면 되지만, 현재 구성한 vagrant에서 2222 포트로 ssh가 설정 돼 있기 때문에 2222 포트에 대한 모든 tcp 요청을 허락했습니다. 그 다음으로 http를 추가하고, 마지막으로 설정한 firewall을 적용시킵니다.

여기서 주의해야 할 점은 설정이 잘 못 이루어지면, 우리 서버에 대한 모든 ssh 접속이 차단될 수 있습니다. 일부 클라우드 프로바이더는 이 경우 재접근 방법을 제공하기도 하지만, 대부분은 그렇지 않으니 주의가 필요합니다. 따라서, firewall 설정은 초기해 해 주는 것이 좋습니다.

최종적으로 설정 후 상태에 대해 확인할 수 있습니다.

Conclusion

여기까지 리눅스 웹 서버 설정을 해 봤습니다. 여기서부터 웹 서버, 이메일 서버 등 다양한 서버를 추가해 볼 수 있습니다. 추가하는 서버 별 주요 차이점은 소프트웨어와 포트 정도 입니다.

Reference