CentOS7-based Kubernetes Cluster Setup using Ansible (en)

1. Introduction

This guide will take you through the process for a multi-node kubernetes cluster on CentOS 7 using Ansible automation tool. The setup will have one master and two minions.

2. Cluster Setup

Here are the system spec and network information.

Read more...

CentOS7-based Kubernetes Cluster Setup using Ansible (ko)

1. 소개

본 매뉴얼은 CentOS 7에서 Ansible 자동화 도구를 사용하여 multi-node kubernetes cluster의 과정을 설명합니다. master 1개, minion 2개를 설정하겠습니다.

2. 클러스터 설정

시스템 스펙과 네트워크 정보는 다음과 같습니다.

Read more...

Kubernetes Raspberry Pi 소형 IDC 구축

개요

가상머신이 아닌 실제 하드웨어 규모로 컨테이너 기술을 실험하고 싶었다. 가장 큰 목적으로는 이동하며 kubernetes가 어떻게 작동하는지 시뮬레이션 가능하도록 하는 것이었다.

목적에 적합한 컴팩트한 사이즈와 전력 소비가 적고 저렴한 rapberry pi를 사용하기로 결정하였고 소형 IDC 구상을 시작하였다. 국내에는 rpi를 이용하여 cluster 구성을한 사례를 찾아 볼 수 없었다.

해외 rpi cluster 구축 사이트를 참조하여 구상을 하던 중 아크릴 판을 이용하여 케이스를 제작한 문서를 참고하여 구축하게 되었다.

Read more...

Kubernetes Internal DNS

kubernetes dns is for service objects. DNS is served as a pod in the cluster.

Set up kubelet to use DNS

Modify kubelet definition file(/etc/kubernetes/kubelet) on all minions. Add the following options.

Read more...

Kubernetes TLS Setup Howto

Secure communication on Kubernetes cluster.

Kubernetes apiserver supports both insecure HTTP and secure HTTPS/TLS protocol.

The insecure HTTP with port 8080 is the default setup but as the name indicates, it is not secure. So we need to set up secure HTTPS communication on KUBIC cluster. Let’s see how to do it.

Read more...

Kubernetes TLS Setup Howto (ko)

Kubernetes 클러스터의 보안 통신

Kubernetes api서버는 비보안의 HTTP와 보안 HTTPS/TLS 프로토콜 모두를 지원합니다.

8080 포트를 사용하는 비보안 HTTP는 기본으로 설정되지만, 이름에서 알수 있듯 안전하지 않습니다. 따라서 KUBIC 클러스터에서 안전한 HTTPS 통신을 설정해야 합니다. 그 방법을 기술 합니다.

Read more...

kube-proxy load balance test (en)

1. What is kube-proxy?

Kubernetes is an orchestration tool for containers. kube-proxy is a component of kubernetes running on kubernetes minion. Its role is the network proxy and load balancer for each container. It uses iptables statistic extension module with random mode and probability settings.

Read more...

kube-proxy load balance test (ko)

1. kube-proxy란?

Kubernetes는 컨테이너를 위한 통합 도구입니다. kube-proxy의 특징은 다음과 같습니다.

  • kube-proxy는 kubernetes minion에 실행되는 kubernetes의 구성 요소입니다.
  • kube-proxy는 각각의 컨테이너에서 네트워크 프록시 및 load balancer 역할을 합니다.
  • kube-proxy는 랜덤 모드 및 확률 설정을 포함한 iptables 통계 확장 모듈을 사용합니다.

Read more...

django로 csrf token의 post request 전달하는 방법

운영중인 서버의 was(fcgi)가 가끔 비정상적으로 동작한다.

crontab에 등록하여 일주일에 한번씩 was(fcgi를 restart하고 있지만, 그래도 문제가 발생한다. restart 주기를 더 짧게 설정하는 방법보다 문제가 발생했을때 restart하는것이 효율적일것이다. curl을 이용하여 실질적으로 login하는 과정을 거쳐 main page(/bom/register/)가 response status 200이 아니면 restart 하게끔 script 작성하고자 한다.

curl을 이용한 login

현재 서버의 login page는 http://xxx.xxx.xxx/login/ 이다. 해당 page를 body(post)값으로 username과 password 변수를 같이 넘겨준다면 login이 될것이다.

# curl http://xxx.xxx.xxx/login/ -d "username={ID}&password={PW}"

그런데 403 FORBIDDEN이 뜬다. 원인은 CRSF verification failed다.

<h1>Forbidden <span>(403)</span></h1>
  <p>CSRF verification failed. Request aborted.</p>

CSRF

사이트 간 요청 위조(또는 크로스 사이트 요청 위조, 영어: Cross-site request forgery, CSRF, XSRF)는 웹사이트 취약점 공격의 하나로, 사용자가 자신의 의지와는 무관하게 공격자가 의도한 행위(수정, 삭제, 등록 등)를 특정 웹사이트에 요청하게 하는 공격을 말한다.

예를 들자면 아래와 같은 공격이다.

  1. 사용자가 xxx.xxx.xxx에 login을 한다. sessionid등의 쿠키값을 정상적으로 발급받아 login된다.
  2. 공격자가 mail이나 게시판등을 이용해 악의적인 http request의 주소를 사용자쪽으로 전달한다. 예를 든다면 http://xxx.xxx.xxx/changepassword?password=abc와 같은 request를 서버로 전송하게끔.
  3. 사용자가 해당 주소를 실행하여 원하지 않는 request를 전송하게 된다.
  4. 서버입장에선 로그인을 거친 정상적인 client가 request를 수행한것이니 해당 프로세스를 수행한다.

위 문제를 해결 하기 위해선 changpassword 페이지의 form전달값에 특정한 값을 추가하면 된다.

예를 들어 사용자로 부터 captcha를 값을 입력받게하여, 정상적인 페이지에서의 요청인지 확인하면 되는것이다. django에서는 간단히 crsf token을 발행하여 처리하게 한다.

CSRF TOKEN in DJANGO

form를 가진 django template에 아래와 같이 crsf_input을 설정한다.

<form action="" method="post">{{ csrf_input }}
<input name="username" id="username" type="text" />
<input name="password" id="password" type="password" />

이렇게 되면 아래와 같은 client에서는 아래와 같은 response를 받게 된다.

<form action="" method="post"><input type='hidden' name='csrfmiddlewaretoken' value='d7f6f683188d35958b0f453f6849a8d7' />

csrfmiddlewaretoken라는 이름의 hidden변수에 random 생성된 token값이 날라온다. http header에도 동일한 token을 cookie로 저장하게끔 되어 있다.

Set-Cookie: csrftoken=d7f6f683188d35958b0f453f6849a8d7; expires=Mon, 10-Jul-2017 08:32:17 GMT; Max-Age=31449600; Path=/

따라서 request시 cookie와 body에 위와 동일한 token값을 같이 전송해야 한다. django에서 해당 token값이 없거나 잘못되었을 경우 위처럼 403을 띄우게 되는것이다.

CURL을 이용하여 CRSF token 전송

CSRF token을 받아와야하니 한번의 login page request로는 불가능하다. 아래와 같은 과정이 있어야 할것이다.

  1. http://xxx.xxx.xxx/login/ 요청후에 csrf token값 저장
  2. 전달받은 csrf token값을 username, password와 같이 전달. cookie도 마찬가지.

curl을 이용하여 csrf token값을 저장해보자.

# curl -c cookie.txt http://xxx.xxx.xxx/login/

-c 옵션으로 set-cookie를 처리할 수 있다. 즉 cookie.txt가 생성되고 csrf token값이 저장된다. 파일을 열어보면 아래와 같다.

# Netscape HTTP Cookie File
# http://curl.haxx.se/docs/http-cookies.html
# This file was generated by libcurl! Edit at your own risk.
xxx.xxx.xxx FALSE   /   FALSE   1499671503  csrftoken   d7f6f683188d35958b0f453f6849a8d7

이제 해당 cookie값과 post값으로 csrf token값을 전달하면 된다. -b 옵션으로 저장된 cookie을 서버로 전달할수 있다.

# curl -b cookie.txt http://xxx.xxx.xxx/login/ -d "username={ID}&password={PW}&csrfmiddlewaretoken=d7f6f683188d35958b0f453f6849a8d7"

이제 정상적으로 login이 됨을 확인할수 있다. 그런데 /bom/register/ 페이지가 정상적으로 열리지 않는다. login후에 sessionid을 cookie로 처리해야하는데, 그 과정이 없으니 login이 되지 않은 것과 동일한 것이다. 위 login request시 sessionid도 response header로 아래와 넘어 온다.

Set-Cookie: sessionid=a37046e1944553ee8dc2af0bc5c483fc; Path=/

그러니 위의 sessionid도 같이 cookie로 저장해야한다. 위와 동일하게 -c 옵션을 줘서 sessionid도 저장하게한다.

# curl -b cookie.txt http://xxx.xxx.xxx/login/ -d "username={ID}&password={PW}&csrfmiddlewaretoken=d7f6f683188d35958b0f453f6849a8d7" -c cookie.txt

cookie.txt파일을 열어보면 csrftoken값과 sessionid가 같이 저장 됨을 확인할 수 있다.

# Netscape HTTP Cookie File
# http://curl.haxx.se/docs/http-cookies.html
# This file was generated by libcurl! Edit at your own risk.

xxx.xxx.xxx FALSE   /   FALSE   1499676993  csrftoken   97154f650b5e4e5a63fbec267072cc38
xxx.xxx.xxx FALSE   /   FALSE   0   sessionid   a37046e1944553ee8dc2af0bc5c483fc

이제 위 sessionid cookie를 이용하여 /bom/register/ page에 접근하면 된다.

# curl -b cookie.txt http://xxx.xxx.xxx/bom/register/

이제 위의 response가 200인지만 확인하면 된다.

TIP

firefox나 chrome에는 개발자 도구(F12)등이 포함되어 있다. 이곳에서 request, response의 header/body값과 cookie값등을 모두 확인할 수 있다.

이를 이용하면 실제 브라우져와 동일하게 http request를 요청하게 처리할 수 있을것이다. 위의 경우 curl로 진행했지만, python httplib 도 header와 body, cookie을 동일하게 처리하면 된다.

Script

위 내용을 토대로 http://xxx.xxx.xxx 사이트의 로그인 시도를 주기적으로 시도하여 문제 발생시(status code 400 이상일 경우) was와 mysql을 재시작하는 스크립트를 작성하였다. 해당 스크립트는 일반 사용자 계정으로 crontab에 등록되어 5분간격으로 실행된다.

crontab의 아래와 같이 오분마다 스크립트를 실행하도록 추가하였다.

$ crontab -e
*/5 * * * * ~/check_login.sh 2>&1

check_login.sh 스크립트 내용은 아래와 같다.

#!/bin/bash

USERID="sungmin" <- 사이트의 로그인 아이디를 입력
USERPW="sungmin" <- 사이트의 로그인 패스워드를 입력
DATE=$(date +%m%d%H%M)
LOG="~/log/check_login_${DATE}.log"

curl -c cookie.txt http://xxx.xxx.xxx/login/ -s > /dev/null

TOKEN=$(grep csrftoken cookie.txt | awk '{print $NF}')

curl -b cookie.txt -d "csrfmiddlewaretoken=${TOKEN}&username=${USERID}&password=${USERPW}" http://xxx.xxx.xxx/login/ -c cookie.txt

RESULT=$(curl -b cookie.txt http://xxx.xxx.xxx/bom/register/ -I -s | head -n1 | awk '{print $2}')
echo ${RESULT}

if [ ${RESULT} -gt 399 ]; then <- status code 결과 값이 400 이상일 경우 아래 명령을 실행
        echo "## Web Status code" > $LOG
        echo ${RESULT} >> $LOG
        echo "## Memory Status" >> $LOG
        free -m >> $LOG
        echo "## Process Check" >> $LOG
        ps -eo pid,rsz,vsz,cmd | grep python | grep -v grep >> $LOG
        echo "## Load average" >> $LOG
        w | head -n1 >> $LOG
        echo "Nginx & WAS & MySQL Restart" >> $LOG
        sudo /etc/init.d/nginx restart >> $LOG 2>&1
        sleep 1
        sudo /etc/init.d/mysql restart >> $LOG 2>&1
        sleep 1
        sudo -u sungmin ~/script/runserver restart >> $LOG 2>&1
        sleep 1
        echo ${RESULT} >> $LOG 2>&1
        mail -s "Server Error & Process Restart" sungmin@xxx.xxx < $LOG
fi

log는 문제 발생 시간에 mysql과 was가 재시작되며 그 전의 메모리 사용률, was 프로세스의 메모리 사용률과 갯수, load average를 log 파일로 기록한다.(로그파일 위치 : ~/check_login/log/)

아래는 mysql을 종료하고 스크립트가 구동되어진 후 로그를 출력. 이후 서비스 정상 확인.

$ cat check_login.log
## Memory Status
             total       used       free     shared    buffers     cached
Mem:          1982        399       1583          0         23        251
-/+ buffers/cache:        124       1857
Swap:          952          0        952
## Process Check
 4277 24672  87860 /usr/bin/python manage.py runfcgi host=127.0.0.1 port=8080
 4278 23888  87860 /usr/bin/python manage.py runfcgi host=127.0.0.1 port=8080
 4279 23908  87860 /usr/bin/python manage.py runfcgi host=127.0.0.1 port=8080
 4280 23908  87860 /usr/bin/python manage.py runfcgi host=127.0.0.1 port=8080
 4281 46904 158356 /usr/bin/python manage.py runfcgi host=127.0.0.1 port=8080
 4282 23908  87860 /usr/bin/python manage.py runfcgi host=127.0.0.1 port=8080
## Load average
 02:52:05 up 5 min,  2 users,  load average: 0.10, 0.12, 0.06
## Nginx & WAS & MySQL Restart
sudo: unable to resolve host test
Restarting nginx: nginx.
sudo: unable to resolve host test
Stopping MySQL database server: mysqld.
Starting MySQL database server: mysqld ..
Checking for tables which need an upgrade, are corrupt or were
not closed cleanly..
Stop django server : .Done
Start django server : Done.

Let`s Encrypt 무료 인증서 설치 방법

Let’s Encrypt란?

Lets’ Encrypt는 HTTPS를 사용하기 위해 SSL을 구매해야 하는 부분이 HTTPS 보급에 방해된다고 생각해서 SSL을 무료로 제공해서 HTTPS를 보급하기 위해 작년 말에 만들어졌다.

초기에는 Mozilla, Cisco, Akamai, EFF, id entrust 등이 모여서 ISRG(Internet Security Research Group)라는 새로운 SSL 인증기관을 만들어서 올해 SSL을 무료로 제공하겠다고 발표했다.

지금은 이 Lets’ Encrypt에 Facebook, 워드프레스를 만드는 Automattic, shopify 등 많은 회사가 스폰서로 참여하고 있다.

Getting the Let’s Encrypt client

let’s encrypt는 web server에 ssl 설정을 가장 단순하게 자동적으로 설정할 수 있다.

이를 위해서 client를 받아야되는데 github repository(https://github.com/certbot/certbot)에서 다운 받을 수 있다. git이 설치되어 있지 않다면 아래 명령어로 설치를 진행한다.

$ sudo apt-get install git
$ sudo yum install git

git 설치가 되었다면 아래 명령어로 다운 받는다.

$ git clone https://github.com/letsencrypt/letsencrypt

다운되어진 letsencrypts 디렉토리로 이동한다.

$ cd letsencrypt

client와 함께 제공되는 letsencrypt-auto 스크립트를 구동하여 인증서를 생성할 수 있다. 인증서 생성은 관리자 권한이 필요하기에 sudo를 사용한다. 구동하기 전 let’s encrypt 의 인증 방식인 Standalone plugin 은 서버 인증을 위해서 80포트를 이용하기 때문에 nginx, apache 와 같이 80 포트를 이용하는 서비스가 있다면 서비를 일시적으로 중지해야 한다.

$ sudo /etc/init.d/nginx restart
$ sudo netstat -nlp | grep 80

80 포트로 운영되는 서비스가 없는 것을 확인하였다면 아래 스크립트를 실행하여 인증서를 생성한다.

$ sudo ./letsencrypt-auto certonly --standlone

관련 패키지가 설치된 뒤 이메일 주소를 입력하는 창을 확인 할 수 있다. 긴급 공지나 키를 복구하기 위해 사용되는 이메일 주소를 입력한다.

이용약관에 동의한다. Agree Enter

마지막으로 도메인 주소를 입력한다. 도메인 주소는 FQDN 표기법으로 입력한다.

정상적으로 생성이 완료될 경우 congratulations! 메시지와 함께 인증서가 생성된 것을 확인할 수 있다. 아래 내용을 보면 인증서가 2016-10-20 에 만료된다고 나와 있다. Let’s encrypt의 인증서는 90일 동안 유효한 인증서이므로 90일마다 새로 갱신을 해야 한다. 자동 갱신 방법에 대해서는 아래에서 추가하도록 하겠다.

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at
   /etc/letsencrypt/live/example.com/fullchain.pem. Your cert
   will expire on 2016-10-20. To obtain a new or tweaked version of
   this certificate in the future, simply run letsencrypt-auto again.
   To non-interactively renew *all* of your certificates, run
   "letsencrypt-auto renew"
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

인증서가 생성된 해당 경로로 이동하면 아래와 같은 인증서를 볼 수 있다.

  • cert.pem : 도메인 인증서
  • chain.pem : Let’s Encrypt chain 인증서
  • fullchain.pem : cert.pem 과 chain 인증서 합본
  • privkey.pem : 개인키

Nginx SSL 설정

nginx 웹서버에 경우 fullchain.pem과 privkey.pem 인증서를 사용한다.

신규 vhost 파일을 생성한다.

$ sudo vi /etc/nginx/site-available/example

443 포트를 listen 하며 server_name에 도메인 주소를 입력합니다. 아래 설정을 참고하여 생성한다.

listen 443 ssl;

server_name example.com www.example.com;

ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';

location ~ /.well-known {
        allow all;
}

추가적으로 80 포트에서 들어오는 주소를 443 https로 보내주는 설정을 추가할 수 있다.

server {
    listen 80;
    server_name example.com;
    return 301 https://$host$request_uri;
}

설정이 완료되었다면 nginx configtest 를 진행한 뒤 문제가 없을 경우 restart하여 설정을 적용한다.

$ sudo /etc/init.d/nginx configtest
Testing nginx configuration: nginx.
$ sudo /etc/init.d/nginx restart

Let’s Encrypt 인증서 자동 갱신

위에서 정상적으로 인증서를 발급하게 되면 만료기간이 언제인지 확인 할 수 있다. 작성한 내용이지만 90일 동안 인증서가 유효하기에 90일이 지나면 새로 갱신을 해야 한다. 우선 갱신하는 방법은 아래와 같다.

위에서 받은 client 폴더로 이동하여 letsencrypt-auto 스크립트로 갱신이 가능하다.

$ sudo ./letsencrypt-auto renew

아래와 같이 스크립트를 만들어 매달 1일날 해당 스크립트를 구동하여 자동으로 갱신이 되도록 crontab에 설정하였다. 유효기간이 30일 남았을 때부터 갱신이 가능하기에 매주 일요일 새벽 2시 해당 스크립트를 구동한다.

# vi /etc/nginx/letsencrypt/renew.sh

#!/bin/sh
/etc/init.d/nginx stop
if ! /etc/nginx/letsencrypt/letsencrypt-auto renew > /var/log/letsencrypt-renew.log 2>&1 ; then
        echo Automated renew failed;
        cat /var/log/letsencrypt-renew.log
        exit 1
fi
/etc/init.d/nginx start
# crontab -l
0 2 * * 0 /etc/nginx/letsencrypt/renew.sh