# Web Server

## Overview <a href="#overview" id="overview"></a>

웹 서버는 HTTP 를 사용하여 사용자에게 웹 페이지를 제공해준다.

앞으로 설치할 소프트웨어들의 웹 페이지에 접속하기 위해 웹 서버를 설치해야 한다. 웹 서버는 nginx(엔진엑스)를 골랐다. 예전엔 apache로 많이 설치했는데 기능이 추가되고 추가되면서 무거워졌다. nginx는 apache에서 널리사용되는 기능만 모아 개발되었다. 즉 apache는 현존하는 모든 기능을 사용할 수 있지만 무겁고 nginx는 단순하면서도 성능이 좋다.

> [2018년 11월에 nginx가 apache의 마켓 점유율을 재쳤다.](https://news.netcraft.com/archives/2018/12/17/december-2018-web-server-survey.html#more-26943)

## Nginx 설치 <a href="#nginx" id="nginx"></a>

```
$ sudo apt-get install nginx
```

설치된 nginx를 확인한다.

```
$ nginx -v
nginx version: nginx/1.14.0 (Ubuntu)
```

## 방화벽 설정 <a href="#undefined" id="undefined"></a>

```
$ sudo iptables -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT
$ sudo iptables -A INPUT -p tcp -m tcp --dport 443 -j ACCEPT
$ sudo netfilter-persistent save
$ sudo netfilter-persistent reload
```

HTTP의 기본 포트인 80번 포트와 HTTPS의 443번 포트의 접근을 허용하도록 한다.

## 서버블록 설정 <a href="#undefined" id="undefined"></a>

Apache에 `VirtualHost`가 있다면 Nginx에는 [Server Block](https://www.nginx.com/resources/wiki/start/topics/examples/server_blocks/)이 있다. `Server Block`은 하나의 머신에서 여러개의 웹 사이트를 운영할 수 있게 해준다.

`Server Block` 파일은 `/etc/nginx/sites-available`에 저장한다. 이 디렉토리는 말 그래도 사용가능한 서버블록들을 저장하는 경로이고, 실제로 웹 사이트를 활성화시키기 위해선 `/etc/nginx/sites-enabled`에 링크시켜놔야 한다.

이 포스트에서는 서버블록을 만들고 서비스하는 방법을 다룰 것이다.

`nginx`를 설치하면 `default`라는 서버블록 설정파일이 존재한다. `default` 서버블록은 사용하지 않을 것이므로 `/etc/nginx/sites-enabled`에 링크되어있는 `default`파일을 지운다.

```
$ sudo rm /etc/nginx/sites-enabled/default
```

새로운 서버블록 설정 파일을 만든다. `/etc/nginx/sites-available`에 생성하는 파일명은 자신의 도메인이 [`example.com`](http://example.com/)이라면 [`example.com`](http://example.com/) 이라고 이름짓는 것을 추천한다.

```
$ sudo touch /etc/nginx/sites-available/example.com
```

`/etc/nginx/sites-available/example.com` 파일을 열어 아래 내용을 추가한다.

```
$ sudo vi /etc/nginx/sites-available/example.com
```

아래 내용을 그대로 입력한다. **default\_server**는 해당 포트에 대한 기본 접속 서버블록을 명시하는 것이다. 도메인이 명시되어 있지않다면 기본 서버블록으로 향하게 된다. 브라우저에서 홈 서버의 IP주소 혹은 호스트명 [*example.com*](http://example.com/) 으로 접속한다면 `/var/www/example.com/htdocs` 안의 `index.html`이 불려질 것이다.

```
server {
    listen 80 default_server;
    listen [::]:80 default_server;

    root /var/www/example.com/htdocs;

    index index.html;

    server_name example.com;

    access_log /var/log/nginx/example.com.access.log;
    error_log /var/log/nginx/example.com.error.log;


    location / {
        try_files $uri $uri/ =404;
    }
}
```

VIM에서 [`example.com`](http://example.com/)을 자신의 도메인으로 치환은 `:%s/example.com/yourdomain.com/gi` 으로 할 수 있다.

`:wq`로 저장 후 종료한다.

## 서버블록의 index 페이지 작성 <a href="#index" id="index"></a>

웹 사이트의 root 디렉토리를 만들고 index 페이지를 만들어 놓는다.

```
$ sudo mkdir -p /var/www/example.com/htdocs
```

index 페이지를 작성한다.

```
sudo vi /var/www/example.com/htdocs/index.html
```

아래 내용을 입력한 후 저장 후 종료한다.

```
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
</head>
<body>
    it worked.
</body>
</html>
```

권한을 변경하여 보안성을 높인다.

```
$ sudo chown -R www-data: /var/www/example.com/htdocs
```

## 서버블록 활성화 <a href="#undefined" id="undefined"></a>

해당 서버블록을 활성화시키기 위해 링크를 걸어준다.

```
$ sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/example.com
```

`nginx -t` 명령을 통해 설정에 이상이 없는지 확인할 수 있다.

```
$ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
```

이상이 없다면 nginx를 재시작한다.

```
$ sudo systemctl restart nginx
```

브라우저를 통해 자신의 웹 사이트에 접속이 되는지 확인해본다.

## HTTPS 활성화 <a href="#https" id="https"></a>

HTTPS 통신을 위해서 SSL/TLS 인증서 발급이 필요하다. 앞선 글에서 SSL/TLS 인증서를 발급해주는 Let’s Encrypt 에서 SSL/TLS 인증서를 발급받았다.

## DH Param 키 생성 <a href="#dh-param" id="dh-param"></a>

TLS 프로토콜 자체의 기본 설계상 취약점이 존재하므로 TLS용 디피-헬만 키를 사용하여 통신에 안전성을 높인다.

```
$ sudo mkdir -p /usr/local/etc/ssh/certs
$ sudo openssl dhparam -out /usr/local/etc/ssl/certs/dhparam.pem 2048
```

`/etc/nginx/sites-available/example.com` 파일을 열어 모든 내용을 삭제하고 아래 내용을 입력해준다.

```
server {
    listen 80 default_server;
    server_name example.com www.example.com;

    # Redirect all HTTP requests to HTTPS with a 301 Moved Permanently response.
    return 301 https://$server_name$request_uri;
}
server {
    listen 443 ssl http2;
    server_name example.com www.example.com;

    # SSL configuration
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
    ssl_dhparam /usr/local/etc/ssl/certs/dhparam.pem;

    ssl_protocols       TLSv1.2;
    ssl_prefer_server_ciphers on;
    ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';

    # enable session resumption to improve https performance
    ssl_session_cache shared:SSL:50m;
    ssl_session_timeout 1h;
    ssl_session_tickets off;

    # fetch OCSP records from URL in ssl_certificate and cache them
    #ssl_stapling on;
    #ssl_stapling_verify on;
    # verify chain of trust of OCSP response using Root CA and Intermediate cert
    #ssl_trusted_certificate /etc/nginx/ssl/star_forgott_com.crt;

    # Security Headers
    add_header X-Frame-Options SAMEORIGIN;
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Content-Type-Options nosniff;

    # HSTS (ngx_http_headers_module is required) (31536000 seconds = 1 year)
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload";

    resolver 8.8.8.8 8.8.4.4 valid=300s;

    # include snippets/snakeoil.conf;

    root /var/www/example.com/htdocs;

    index index.html;

    server_name example.com;

    access_log /var/log/nginx/example.com.access.log;
    error_log /var/log/nginx/example.com.error.log;

    location / {
        # First attempt to serve request as file, then
        # as directory, then fall back to displaying a 404.
        try_files $uri $uri/ =404;
    }
}
```

> 설정은 <https://mozilla.github.io/server-side-tls/ssl-config-generator/> 과 <https://gist.github.com/jult/395ad9fd3e9773a54a67aaf689beab27> 등을 참고하였다.

`ssl_protocols`에 `TLSv1.2`만 허용했는데 `TLSv1.1`이하는 보안에 취약해서 사용하지 않을 것을 권고하고 있다. `resolver`는 인증서 발급업체와 통신하는 네임서버이다. 구글에서 제공하는 네임서버로 셋팅해 주었다.

설정이 끝났으면 `nginx -t`를 통해 문법검사를 하고 재시작한다.

```
$ sudo nginx -t
$ sudo systemctl restart nginx
```

브라우저로 [http://exsample.com](http://exsample.com/) 에 접속하면 자동으로 [https://exsample.com](https://exsample.com/) 로 리다이렉트된다.

## 인증서 갱신 후 nginx 재시작 <a href="#nginx" id="nginx"></a>

Let’s Encrypt 에서 발급받는 인증서의 경우 유효기간이 90일이고 자동으로 인증서를 갱신하도록 cronjob에 등록되어 있다. 인증서가 갱신되면 nginx를 재시작해주어야 한다.

`/etc/letsencrypt/renewal-hooks/deploy/01-reload-nginx` 파일을 연다.

```
$ sudo vi /etc/letsencrypt/renewal-hooks/deploy/01-reload-nginx
```

아래 내용을 입력하고 저장 후 종료한다.

```
#!/bin/bash
/bin/systemctl reload nginx
```

실행 권한도 추가한다.

```
$ sudo chmod u+x /etc/letsencrypt/renewal-hooks/deploy/01-reload-nginx
```

이제 certbot에 의해 인증서가 갱신되면 `01-reload-nginx` 가 실행되면서 nginx가 재시작될 것이다.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://jkpark.gitbook.io/ubuntu/server/hosting/web-server.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
