레이블이 Apache Httpd인 게시물을 표시합니다. 모든 게시물 표시
레이블이 Apache Httpd인 게시물을 표시합니다. 모든 게시물 표시

process 파일 이용하기

# 리눅스는 모든게 파일이다. 프로세스도 /proc/프로세스ID 파일로 확인할 수 있다.
# 현재 프로세스가 실행중이면 커맨드라인 파악
cat /proc/$$/cmdline

# 특정 (httpd) 프로세스의 쉘 환경변수 중 ysoftman 값 파악
sudo cat /proc/$(ps -e -o pid,command | command grep -i httpd | head -n1 | awk '{print $1}')/environ | tr '\0' '\n' | grep -iE ^ysoftman

# 참고로 현재 프로세스(pid)의 stdout 을 보기 위해 
# 새 터미널에서 cat /proc/{pid}/fd/1 를 실행후 입력하면 문자가 개별적으로
# pid 가 실행중인 /dev/pts/1 과 cat 이 실행중인 /dev/pts/2 중 한쪽에만 출력된다.


httpd content-encoding 헤더 누락 문제

# nginx proxy -> apache httpd 웹서버 구성에서
# apache httpd 2.4 버전업과 brotli(br) 압축을 적용했다.
# 다음과 같이 nginx 에 br 인코딩 헤더로 요청을 준다.
# 참고로 -I 로 헤더만 보는경우 curl 은 HEAD method 로 요청하기 때문에
# 실제 GET 요청에서 응답받는 헤더를 보려면 GET method 로 지정해야 한다.
curl 'http://ysoftman-nginx/test?param=ysoftman'-H 'accept-encoding:br' -I -X GET

# 다음과 같이 content-encoding: br 응답헤더를 볼 수 있다.
HTTP/2 200
date: Thu, 03 Oct 2019 13:13:34 GMT
content-type: text/html; charset=utf-8
vary: Accept-Encoding
content-encoding: br
server: ysoftman-nginx
cache-control: private, no-cache, max-age=0
expires: -1

# gzip 요청도 테스트 해보면
curl 'http://ysoftman-nginx/test?param=ysoftman'-H 'accept-encoding:gzip' -I -X GET

# 다음과 같이 content-encoding: gzip 응답헤더를 볼 수 있다.
HTTP/2 200
date: Thu, 03 Oct 2019 13:09:34 GMT
content-type: text/html; charset=utf-8
vary: Accept-Encoding
content-encoding: gzip
server: ysoftman-nginx
cache-control: private, no-cache, max-age=0
expires: -1

# 문제
# nginx 에서 packetbeat 을 설치해 httpd 의 응답 헤더를 수집해 elasticsearch(es) 보냈다.
# kibana 로 content-encoding 헤더가 br 또는 gzip 을 카운트 하려는데
# http.response.headers.content-encoding 헤더 자체가 없는 로그가 있다.
# "accept-encoding" 요청이 없으면 plain text 로 헤더가 없는게 맞지만
# "accept-encoding: gzip" 요청은 있어야 한다.
# http.response.headers.content-encoding: br 은 잘 남고 있었다.
# 원인
# curl 이나 실제 브라우저에서 테스트 해보면
# content-encoding: br 또는 gzip 응답이 확인된다.
# 하지만 nginx 가 아닌 httpd 로 요청하면
# gzip 요청에 대해선 content-encoding 응답헤더가 없었다.
curl 'http://ysoftman-httpd/test?param=ysoftman'-H 'accept-encoding:gzip' -I -X GET

# gzip 처리를 nginx 가 해줘 nginx 의 packetbeat 은
# httpd 로 부터의 응답에서 "content-encoding: gzip" 찾을 수 없었던 것이다.
# 찾아보니 httpd.conf 에서 응답 필터 처리시
# 다음과 같이 brotli 만 응답 필터링한게 문제였다.
SetOutputFilter BROTLI_COMPRESS

# 해결
# SetOutputFilter 는 주석처리하고
# 다음과 같이 AddOutputFilterByType 로
# deflate(gizp에서 사용하는 무손실 압축 알고리즘), brotli 둘다 사용할 수 있게 했다.
# 먼저 추가한 필터가 우선순위를 가져 br 먼저 설정해야 한다.
AddOutputFilterByType BROTLI_COMPRESS text/html text/plain text/xml text/css text/javascript application/x-javascript application/javascript application/json application/x-font-ttf application/vnd.ms-fontobject image/x-icon

AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css text/javascript application/x-javascript application/javascript application/json application/x-font-ttf application/vnd.ms-fontobject image/x-icon

# 그리고 AddOutputFilterByType 사용하려면 filter 모듈을 추가해야 한다.
LoadModule filter_module modules/mod_filter.so

# 이제 httpd 요청시에도 "content-encoding: gzip" 헤더를 받을 수 있고,
# packetbeat 에서도 잘 수집돼 -> es -> kibana 로 content-encoding 값을 구분 할 수 있다.

# 참고
https://httpd.apache.org/docs/current/mod/mod_filter.html#AddOutputFilterByType
https://www.tunetheweb.com/performance/brotli/

URL 공백 인코딩

URL 인코딩시 함수나 프로그래밍 언어에 따라 공백을 %20 또는 + 로 변경할 수 있는데,
사실 URI 스펙 상으로 + 는 reserved word 이고 공백은 %20 이다.
https://tools.ietf.org/html/rfc3986#section-2.2

javascript encodeURI()/encodeURIComponent() URL 인코딩에서도
공백은 %20 으로 처리한다.
테스트 코드
https://github.com/ysoftman/test_code/blob/develop/js/encoding.html

libapreq2(apr_util.h) apreq_escape() 로 URL 인코딩을 하면 공백을 + 로 처리하기 때문에 별도로 + 를 %20 으로 변경하는 작업이 필요하다.
테스트 코드
https://github.com/ysoftman/test_code/blob/develop/apr_test/apr_test.cpp

httpd 버전별 unique_id 차이

# apache httpd 에서 요청구분을 위해 unique key 를 구할 때
# mod_unique_id 를 다음과 같이 사용한다.
# 사용 예시
https://github.com/omnigroup/Apache/blob/526ef6bb9f1988be6583cecea96598b6cb1b0d6b/httpd/modules/metadata/mod_unique_id.c#L358
*id = apr_table_get(r->subprocess_env, "UNIQUE_ID");

# 그런데 httpd 2.2 vs 2.4 에 따라 그 길이가 다르다.
2.2 mod_unique_id => 112bit(32비트 IP 주소, 32비트 pid, 32비트 시간, 16비트 카운터)
https://github.com/omnigroup/Apache/blob/526ef6bb9f1988be6583cecea96598b6cb1b0d6b/httpd/modules/metadata/mod_unique_id.c#L82
2.4 mod_unique_id => 144bit(32비트 IP 주소, 32비트 pid, 32비트 시간, 16비트 카운터, 32비트 쓰레드 인덱스)

# 참고로 버전별 설명이 제대로 되어 있지 않아 주의해야 한다.
# 2.2 2.4 영어 설명에는 모두 144bit 설명되어 있고
https://httpd.apache.org/docs/2.4/en/mod/mod_unique_id.html
https://httpd.apache.org/docs/2.2/en/mod/mod_unique_id.html

# 2.2 2.4 한글 번역은 최신이 아니라 112bit 로 예전 2.2 로 설명되어 있어 있다.
https://httpd.apache.org/docs/2.4/ko/mod/mod_unique_id.html
https://httpd.apache.org/docs/2.2/ko/mod/mod_unique_id.html

httpd AH00141 에러

# 201907 현재 최신 버전 apache httpd 사용해서 빌드했다.
# httpd 2.4.39
# apr 1.7.0
# apr-util 1.6.1
# httpd 빌드시 --with-apr=${HOME}/apr 로 옵션사용
# 빌드 후 httpd 실행시 특정 서버에서 다음과 같은 에러가 발생한다.
[:crit] [pid 478] (38)Function not implemented: AH00141: Could not initialize random number generator

# 잘되는 서버(A)
# 에러 발생하는 서버(B)

# 확인 1
# apr 문제로 보고 잘되는 서버와 libapr-1.so 가 사용하는 shared library 를 살펴봤다.
# libuuid.so 사용 여부가 다르긴 하다.
# B 서버에서는 make 에서 -luuid 가 추가된다.
# A 서버
ldd libapr-1.so
linux-vdso.so.1 =>  (0x00007fff695fc000)
librt.so.1 => /lib64/librt.so.1 (0x00007ff7836ee000)
libcrypt.so.1 => /lib64/libcrypt.so.1 (0x00007ff7834b6000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x00007ff78329a000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007ff783096000)
libc.so.6 => /lib64/libc.so.6 (0x00007ff782cd2000)
libfreebl3.so => /lib64/libfreebl3.so (0x00007ff782acf000)
/lib64/ld-linux-x86-64.so.2 (0x00005560922d1000
# B 서버
ldd libapr-1.so
linux-vdso.so.1 =>  (0x00007ffcd53f8000)
libuuid.so.1 => /lib64/libuuid.so.1 (0x00007f8dc4c56000)
librt.so.1 => /lib64/librt.so.1 (0x00007f8dc4a4d000)
libcrypt.so.1 => /lib64/libcrypt.so.1 (0x00007f8dc4816000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f8dc45fa000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007f8dc43f5000)
libc.so.6 => /lib64/libc.so.6 (0x00007f8dc4032000)
/lib64/ld-linux-x86-64.so.2 (0x00007f8dc50a6000)
libfreebl3.so => /lib64/libfreebl3.so (0x00007f8dc3e2f000)

# 확인 2
# A -> B  로 libapr-1.so.0.7.0(libapr-1.so 가 링크)를 복사했지만 에러가 발생한다.
# B -> A  로 libapr-1.so.0.7.0(libapr-1.so 가 링크)를 복사했지만 실행된다.
# 그래서 apr 빌드의 문제는 아니다.

# 확인 3
# libapr 에 포함된 함수중에 random 관련 함수가 실행될 수 없는 문제인것 같다.
nm libapr-1.so | grep random
42:000000000023aed0 b all_random
218:0000000000023d90 T apr_generate_random_bytes
442:000000000002a460 T apr_random_add_entropy
443:000000000002a370 T apr_random_after_fork
444:000000000002a750 T apr_random_barrier
445:000000000002a0c0 t apr_random_bytes
446:000000000002a210 T apr_random_init
447:000000000002a730 T apr_random_insecure_bytes
448:000000000002a780 T apr_random_insecure_ready
449:000000000002a710 T apr_random_secure_bytes
450:000000000002a760 T apr_random_secure_ready
451:000000000002a400 T apr_random_standard_new
912:000000000002a070 t random_cleanup

# 확인 4
# apr v1.7.0 설치 디렉토리의 lib 를 잠깐 lib2 로 변경해 놓으면 실행된다.
# httpd 빌드시 사용된 라이브러리 및 옵션을 확인해봤다.
# 실제 서버에서 로딩하는 APR 버전이 빌드시 사용된 APR 버전보다 낮은
# centos 기본 내장된 /usr/lib64/libapr-1.so.0.4.8 을 사용하는것으로 보인다.
./httpd -V
Server version: Apache/2.4.39 (Unix)
Server built:   Jul 16 2019 18:31:52
Server's Module Magic Number: 20120211:84
Server loaded:  APR 1.4.8, APR-UTIL 1.6.1
Compiled using: APR 1.7.0, APR-UTIL 1.6.1
Architecture:   64-bit
Server MPM:     prefork
  threaded:     no
    forked:     yes (variable process count)
Server compiled with....
 -D APR_HAS_SENDFILE
 -D APR_HAS_MMAP
 -D APR_HAVE_IPV6 (IPv4-mapped addresses enabled)
 -D APR_USE_PROC_PTHREAD_SERIALIZE
 -D APR_USE_PTHREAD_SERIALIZE
 -D SINGLE_LISTEN_UNSERIALIZED_ACCEPT
 -D APR_HAS_OTHER_CHILD
 -D AP_HAVE_RELIABLE_PIPED_LOGS
 -D DYNAMIC_MODULE_LIMIT=256
 -D HTTPD_ROOT="/home/ysoftman/apache-2.4.39"
 -D SUEXEC_BIN="/home/ysoftman/apache-2.4.39/bin/suexec"
 -D DEFAULT_PIDLOG="logs/httpd.pid"
 -D DEFAULT_SCOREBOARD="logs/apache_runtime_status"
 -D DEFAULT_ERRORLOG="logs/error_log"
 -D AP_TYPES_CONFIG_FILE="conf/mime.types"
 -D SERVER_CONFIG_FILE="conf/httpd.conf"

# 결론
# 구글링 해보면 환경변수 PATH 에 깨진 문자가 포함되었거나
# 커널에서 제공하는 랜럼 디바이스 파일 /dev/random, /dev/urandom 이 없는 경우
# 라고하는데 두 경우 특별한 문제점을 찾을 수 없었다.
# 대신 apr 1.5.2 버전을 다운받아 빌드하면 잘 동작한다.
# 참고로 나와 같은 에러가 발생한다는 버그 리포팅이 있다.
https://bz.apache.org/bugzilla/show_bug.cgi?id=63388

apache httpd forbidden(403) 에러

# apache httpd v2.2 forbidden(403) 에러가 발생한다.
Forbidden
You don't have permission to access /search on this server.

# httpd.conf 는 다음과 같이 설정되어 있다.
# 참고로 Options 값은 한줄에 모두 명시해야 된다. 아니면 마지막 Options 값만 처리됨
DocumentRoot "/home/ysoftman/httpd/html/"
<Directory />
    Options ExecCGI
    AddHandler cgi-script .cgi .pl .py .pyc
    AllowOverride None
    <Limit POST>
       Order allow,deny
       Allow from all
    </Limit>
    <LimitExcept GET>
       Require valid-user
    </LimitExcept>
</Directory>

# 문제가 되는 부분은 Options ExecCGI 이다.
# Options 지시자 설명
https://httpd.apache.org/docs/2.2/mod/core.html#options
https://httpd.apache.org/docs/2.4/mod/core.html#options
# 방법1
# ExecCGI(핸들러로 등록된 스크립트 파일을 실행 허용) 를 사용안함 처리하면 된다.
Options -ExecCGI

# 방법2
# FollowSymLinks(심볼릭 링크 파일을 따라 가도록 허용) 옵션을 같이 준다.
# FollowSymLinks 는 httpd 2.4 부터 default다.
Options +FollowSymLinks +Exec

apache httpd 모듈 사용자 요청 scheme 파악

# apache httpd 에 사용 할 C/C++ 모듈 예)mod_ysoftman.so 개발시
# ap_hook_handler 를 통해 request_rec* req (사용자 요청 레코드)를 받을 수 있다.
# scheme(http, ftp,..) 종류를 파악해보자
# httpd.h 에 명시된 매크로 사용 하면 된다.(v2.2 이상)
ap_run_http_scheme(req)

# 그런데 gtest 빌드시 다음 에러가 발생한다.
undefined reference to `ap_run_http_scheme'

# gtest 컴파일은 -lapreq2 -lapr-1 라이브러리만 링크하게 되는데
# ap_run_http_scheme 와 같이 ap_ 로 시작하는 함수들은
# httpd daemon 루틴으로 httpd 데몬 실행 환경에서만 사용할 수 있다.
# 다음 exp 파일에서 각각 라이브러리에서 사용하는 함수들을 확인할 수 있다.
apache/modules/httpd.exp
apache/lib/apr.exp, aprutil.exp

# httpd library 를 -l 링크하지 않으면 ap_xxx 함수들을 찾을 수 없게 된다.


##########


# 삽질~
# ap_xx 대신 apr(apache runtime) 라이브러리에서 비슷한 함수을 찾아 봤다.
# apr 라이브러리에서는 apr_uri_parse() 로 scheme 를 알 수 있다.
# 하지만 req->uri, unparsed_uri,.. 등을 찍어보면 scheme 정보가 없다.
fprintf(stderr, "ysoftman unparsed_uri %s\n", req->unparsed_uri);
fprintf(stderr, "ysoftman uri %s\n", req->uri);
fprintf(stderr, "ysoftman the_request %s\n", req->the_request);
fprintf(stderr, "ysoftman path_info %s\n", req->path_info);
fprintf(stderr, "ysoftman args %s\n", req->args);
fprintf(stderr, "ysoftman protocol %s\n", req->protocol);
fprintf(stderr, "ysoftman method %s\n", req->method);
fprintf(stderr, "ysoftman content_type %s\n", req->content_type);
fprintf(stderr, "ysoftman user %s\n", req->user);
fprintf(stderr, "ysoftman ap_auth_type %s\n", req->ap_auth_type);

# 따라서 다음과 같이 req->pool 에서 찾는게 아니라 scheme 이 포함된 url 스트링을 주고 이를 파싱하는데만 쓸 수 있다.
apr_uri_t parsed_uri;
apr_status_t ret = apr_uri_parse(req->pool, "http://ysoftman.com", &parsed_uri);
if (ret != APR_SUCCESS)
{
    return "";
}
fprintf(stderr, "ysoftman parsed_uri.scheme %s\n", parsed_uri.scheme);


##########


# 해결방법 - gtest 에서 ap_run_http_scheme 을 찾지 않도록 자체 구현하면 된다.
# gtest 코드에서 다음과 mockup 함수로 선언하고
# MOCK_CONST_METHOD + 파라미터 개수 + (함수명, 리턴타입())
MOCK_CONST_METHOD0(scheme, std::string());

# cpp 파일에 C 스타일로 컴파일도록하고
# 의미없는 ap_run_http_scheme() 를 구현해놓는다.
#ifdef __cplusplus
extern "C" {
#endif
char *ap_run_http_scheme(request_rec *r)
{
return NULL;
}

#ifdef __cplusplus
}
#endif

apache httpd 2.4 AH00076 에러

# apache httpd 2.4 를 윈도우(WSL:windows subsystem for linux)에서 실행하면
sudo /home/ysoftman/httpd/bin/apachectl start

# 다음과 같은 워닝이 발생한다.
[core:warn] [pid 1510] (92)Protocol not available: AH00076: Failed to enable APR_TCP_DEFER_ACCEPT

# httpd.conf 맨 마지막에 다음을 추가해 주자.
# 참고 http://httpd.apache.org/docs/2.4/mod/core.html
AcceptFilter http none
AcceptFilter https none

access log - get null 400 에러

# tomcat 에러 중 다음과 같이 path 부분이 null 이 찍히는 경우가 있다.
# 참고로 로그는 apache httpd mod_log_config 를 사용한다.
https://httpd.apache.org/docs/2.4/ko/mod/mod_log_config.html

# /var/lib/tomcat8/conf/server.xml 에서 로그 포맷 설정
# %{Foobar}i - Foobar 헤더
# %h - 원격 호스트
# %l - 원격 identid가 제공한 로그인명, 기본값(-)
# %u - 원격 auth가 제공한 사용자명, 기본값(-)
# %t - 표준 영어 시간
# %r - 요청 첫번째 줄
# %s - 상태코드
# %b - http 헤더를 제외한 전송 바이트 수
# %h %l %u %t "%r" %s %b 포맷

# tomcat access log 확인
sudo tail -f /var/log/tomcat8/localhost_access_log.2019-01-31.txt

# 브라우저에서 '아이유' 요청하면 URL 인코딩 되고 다음과 같이 정상 동작(로깅)
127.0.0.1 - - [31/Jan/2019:18:15:30 +0900] "GET /%EC%95%84%EC%9D%B4%EC%9C%A0 HTTP/1.1" 404 1110

# curl 로 다음처럼 한글이 URL인코딩 없이 요청하는 경우 null 로 되고
# tomcat server.xml 의 protocol 기본 설정이 HTTP/1.1 로 돼있어
# HTTP/1.0 을 사용해도 로그에 HTTP/1.1 로 기록된다.
curl "http://localhost:8080/아이유" --http1.0 -s > /dev/null

# path 가 null 로 되어 처리할 수 없게 된다.
127.0.0.1 - - [31/Jan/2019:18:15:30 +0900] "GET null HTTP/1.1" 400 800

# tomcat 에러 로그를 확인을 위해 모든 로그를 확인해보자.
sudo vi /var/lib/tomcat8/conf/logging.properties
java.util.logging.ConsoleHandler.level = ALL

# tomcat 재시작
sudo service tomcat8 restart

# 에러 로그를 확인해보면
sudo tail -f /var/log/tomcat8/catalina.out

java.lang.IllegalArgumentException: Invalid character found in the request target. The valid characters are defined in RFC 7230 and RFC 3986

# 관련 내용을 구글링 해보니 tomcat 7.0.73, 8.0.39, 8.5.7 이상에서 발생하고
# http connector 에 relaxedQueryChars 를 설정하라고 한다.
https://stackoverflow.com/questions/41053653/tomcat-8-is-not-able-to-handle-get-request-with-in-query-parameters

# tomcat 버전 확인하면 8.5.30.0 이다.
sudo bash /usr/share/tomcat8/bin/version.sh

# 다음과 같이 특수문자를 처리해봤지만 한글 utf8코드는 처리 되지 않았다.
sudo vi /var/lib/tomcat8/conf/server.xml

<Connector port="8080" protocol="HTTP/1.1"
                connectionTimeout="20000"
                redirectPort="8443" relaxedQueryChars="[]|{}^&#x5c;&#x60;&quot;&lt;&gt;" />

# relaxedQueryChars 특수문자는 명시할 수 있지만 non-alphabet 범위의 문자코드는 어떻게 명시할 방법이 없어, client 가 URL 인코딩을 해줘야 한다.

apache httpd rewrite

# httpd rewrite 사용하기

LogLevel warn rewrite:trace3
LoadModule rewrite_module modules/mod_rewrite.so
<IfModule rewrite_module>
    RewriteEngine On

    # RewriteCond 조건에 맞으면 RewriteRule 을 수행한다.
    # localhost 로 요청이 오면
    # RewriteCond %{HTTP_HOST} localhost
    # test.ysoftman.com 호스트로 요청이 오면
    RewriteCond %{HTTP_HOST} test\.ysoftman\.com

    # uri 가 /aaa 가 아니면
    RewriteCond %{REQUEST_URI} !(^/aaa$)

    # 쿼리 문자열 중 param=123 같은 파라미터가 있으면
    # param=value 를 그룹매칭 %1는 param=xxx
    # RewriteCond %{QUERY_STRING} (param=[^&]*)
    # value 만 그룹매칭 %1는 xxx
    RewriteCond %{QUERY_STRING} param=([^&]*)

    # 위 여러개의 RewriteCond 중 마지막(RewriteRule 바로이전) 패턴 매칭 결과를
    # %0 ~ %N(9까지) 로 사용할 수 있다.
    # %0 은 매칭된 전체이고, %1부터는 (xxx) 그룹을 나타낸다.
    # 첫번째 매칭 그룹은 %1 두번째 매칭 그룹은 %2 ... 가 된다.
    # 위 조건에 의해 param=123 의 경우 %1 는 123 이 된다.

    # RewriteRule 의 패턴 패칭 결과는 $0 ~ $9 로 사용할 수 있고,
    # $0 은 매칭된 전체이고, $1부터는 (xxx) 그룹을 나타낸다.
    # 첫번째 매칭 그룹은 $1 두번째 매칭 그룹은 $2 ... 가 된다.
    # /api/apple/lemon/ 인 경우 $1=apple  $2=lemon

    # 자주 사용되는 RewriteRule 플래그
    # R : redirect, 302 상태로 브라우저에게 라디이렉트하도록 한다. fully-qualified URL (http://servername/ 를 포함하는) 로도 설정할 수 있다. R=301 로 하면 301 로도 응답할 수 있다.
    # F : forbidden , 430 으로 응답한다.
    # G : gone, 410 상태로 응답한다.
    # L : last, 이 후 더이상 룰 매칭이 되지 않도록 한다. RewriteRule 이 여러개 사용할 겨우 더이상 L 이후로는 더이상 처리되지 않는다.
    # NC : nocase, 매칭시 대소문자 구별하지 않는다.
    # NE : NoEscape, % 로 이스케이프 되는것을 막는다.

    # test.ysoftman.com/api/apple/lemon?param=123 의 요청인 경우
    # RewriteCond 조건이 참이되어
    # test.ysoftman.com/ysoftman-apple-lemon?test=123 로 라다이렉트 시킨다.
    RewriteRule ^/api/([^/]*)/([^/]*) /ysoftman-$1-$2?test=%1 [NE,R,NC,L]

    # 또는 https://...로 다른 호스트로 리다이렉트 할 경우
    # RewriteRule ^/(.*) https://aaa.bbb.ccom%{REQUEST_URI} [NE,R,NC,L]
</IfModule>


#####


# 이스케이프 처리
# apache httpd 에서는 path 에 url 인코딩 없이 한글이 오면 특수문자로 취급해 escape(\)+hexadecimal(hex) 코드로 처리한다.

# 브라우저를 사용하면 자동 url 인코딩 된다.
http://localhost:8080/%EC%95%84%EC%9D%B4%EC%9C%A0
# httpd access_log
"GET /%EC%95%84%EC%9D%B4%EC%9C%A0 HTTP/1.1" 404 207

# curl 로 url 인코딩 없이 요청하면
curl http://localhost:8080/아이유
# httpd access_log 에는 아이유(utf-8, 한글자3byte) 총 9byte 가 hex-escape 된다.
"GET /\xec\x95\x84\xec\x9d\xb4\xec\x9c\xa0 HTTP/1.1" 404 207

# rewrite 모듈 사용시 \x 가 %로 이스케이프되는데 이를 막으려면
# mod_rewrite 모듈의 NE 플래그를 사용해야 한다.
https://httpd.apache.org/docs/2.4/rewrite/flags.html#flag_ne

# 에러 로그에 rewite 확인
tail -f logs/error_log

# NE 플래그 없을때
redirect to http://localhost:8080/ysoftman-%ec%95%84%ec%9d%b4%ec%9c%a0 [REDIRECT/302]

# NE 플래그 있을때
redirect to http://localhost:8080/ysoftman-\xec\x95\x84\xec\x9d\xb4\xec\x9c\xa0 [REDIRECT/302]


apache httpd core dump 생성하기

# apache httpd 프로세스에 크래시되면 core dump 파일 생성하기
# httpd.conf 수정
vi httpd.conf
CoreDumpDirectory  /ysoftman

# core dump 디렉토리 권한 주기
chmod 0777 /ysoftman

# core 덤프 사이즈 제한 없애기
# limits.conf 파일 수정 (시스템 rebooting 필요)
sudo vi /etc/security/limits.conf
* soft core unlimited

# 또는 .basrhc 또는 .zsrhc 에 추가
ulimit -c unlimited

# core unlimited 확인
ulimit -a

# sysctl 설정 확인
sudo sysctl -a | grep kernel.core

# core 파일명에 pid 명시되도록 설정
sudo sysctl kernel.core_uses_pid=1

# core 기본 파일이름
sudo sysctl kernel.core_pattern=core

# docker container 의 경우 다음과 같이 읽기 전용 파일이라고 에러가 발생한다.
sysctl: setting key "kernel.core_uses_pid": Read-only file system

# docker container 에서는 sysctl 로 확인만 가능하다.
# 다음과 같이 /etc/sysctl.conf 파일에 명시해야 한다.
RUN echo 'kernel.core_uses_pid = 1' >> /etc/sysctl.conf

# docker run --sysctl 옵션은 사용할 수 있지만
# docker run --sysctl kernel.core_uses_pid=1 사용하면 다음 에러가 난다.
# kernel* 부분들을 run 시 설정할 수 없는것 같다.
invalid argument "kernel.core_uses_pid=1" for "--sysctl" flag: sysctl 'kernel.core_uses_pid=1' is not whitelisted

# httpd 재시작
bin/apachectl stop
bin/apachectl start

# httpd process 에 SEGV(segmentation fault) signal 주어 코어 파일 생성
kill -11 123456

# 참고
https://www.kernel.org/doc/Documentation/sysctl/kernel.txt
https://www.centos.org/forums/viewtopic.php?t=44865
https://docs.docker.com/engine/reference/commandline/run/#configure-namespaced-kernel-parameters-sysctls-at-runtime
https://github.com/moby/moby/issues/7040

apache httpd Permission denied, AH00035 에러 해결하기

centos 7 + apache httpd 2.4 로 도커를 구성해 컨테이너 실행시 아파치 웹서버가 기본 설정 동작하도록 하였다.
테스트로 http://localhost:10000 로 접속하니 다음과 같은 에러가 발생하였다.
참고로
로컬 10000 포트와 도커 컨테이너의 80 포트를 연결하였고,
/home/ysoftman/httpd 에 apache httpd 가 구동중이다.


Forbidden
You don't have permission to access / on this server.


도커 컨테이너의 apache httpd error_log 에는 다음과 같이 기록되었다.


[core:error] [pid 56] (13)Permission denied: [client 172.17.0.1:40368] AH00035: access to / denied (filesystem path '/home/
ysoftman/httpd') because search permissions are missing on a component of the path


문제 원인은 httpd 가 실행되고 있는 경로(/home/ysoftman/httpd)가 httpd.conf 에서 다음과 같이 기본 daemon 으로는 실행(접근)할 수 있는 권한이 없어서였다.

User daemon
Group daemon

apache httpd 는 httpd.conf 에서 설정된 User/Group 의 권한으로 실행되는데
내가 만든 계정은 user(ysoftman) group(programmer)로 /home/ysoftman 에서 daemon 계정은 실행할 수 있는 권한이 없다.
namei 로 DocumentRoot 경로를 확인해보면 다음과 같이 경로상의 모든 디렉토리에 x 권한이 있지만 ysoftman 에만 설정되어 있지 않았다.
참고로 namei 프로그램은 경로상의 모든 이름(디렉토리,파일)에 각각에 대해 -m(mode) -o(owner) -l(list format) 로 옵션으로 정보를 보여준다.

namei -mol /home/ysoftman/httpd/htdocs/
f: /home/ysoftman/httpd/htdocs/
drwxr-xr-x root     root       /
drwxr-xr-x root     root       home
drwx------ ysoftman programmer ysoftman
drwxr-xr-x ysoftman programmer httpd
drwxr-xr-x ysoftman programmer htdocs



[해결방법1]
httpd.conf 의 User Group 을 현재 httpd 가 실행되는 /home/ysoftman 에 맞게 변경해주면 된다.

User ysoftman
Group programmer


[해결방법2]
만약 User/Group 을 daemon 을 그대로 사용한다면 다음과 같이 실행권한을 /home/ysoftman 에서 모든 사용자가 실행할 수있도록 실행 권한을 추가하면 된다.

chmod +x /home/ysoftman

확인해보자

namei -mol /home/ysoftman/httpd/htdocs
f: /home/ysoftman/httpd/htdocs
drwxr-xr-x root     root       /
drwxr-xr-x root     root       home
drwx--x--x ysoftman programmer ysoftman
drwxr-xr-x ysoftman programmer httpd
drwxr-xr-x ysoftman programmer htdocs



해결방법1 또는 2를 적용하고 http://localhost:10000 로 접속하면

It works!

nginx 요청 url인 긴경우 에러 해결하기

# nginx 웹서버 요청시 아래와 같이 아주 긴 문자열을 포함시켜 요청을 하게 되면
# 요청이 제대로 전달되지 않는 문제가 발생하는 경우가 생긴다.
http://server.ysoftman.com/test?param1=aaaaa ... zzzzz (1000자 이상)

# nginx 에서 set 으로 사용자 변수(로그필터링을 위해 변수를 사용하는등)를
# 사용하는 경우 다음과 같은 에러가 발생하기도 한다.
unsing uninitialized 'myvar1' ... variable while logging request....

# 참고 http://nginx.org/en/docs/debugging_log.html
# nginx 빌드시 다음과 같이 디버그 사용 옵션을 준다.
./configure --with-debug

# 설정에서 로그 레벨을 debug 로 명시하면
# nginx 에서 발생하는 구체적인 에러를 볼 수 있다.
error_log /path/to/log debug;

# debug 로깅으로 다음과 같은 에러 메시지가 찍힌다.
client exceeded http2_max_field_size limit while processing HTTP/2 connection

# 참고 http://nginx.org/en/docs/http/ngx_http_v2_module.html#directives
# http2 헤더 와 필드 값을 늘려 줘야 한다.
http2_max_field_size 16k;  # default 4k
http2_max_header_size 32k;  # default 16k

# 그리고 다음과 같이 URI 이 너무 길다는 에러 응답을 받는다면
414 Request-URI Too Large
# 참고 http://nginx.org/en/docs/http/ngx_http_core_module.html#large_client_header_buffers
# 다음과 같이 클라이언트 헤더 버퍼를 늘려주자.
large_client_header_buffers 4 8k;

# 414 에러가 apache httpd 에서 발생한다면 LimitRequestLine 설정을 늘려 주자.
LimitRequestLine 16000 # default 8190

웹서비스 robot.txt human.txt

웹서비스에서 web crawler 같은 로봇들의 활동을 제어하는 방안으로 robot.txt 을 루르 경로에 만들어 두고 사용한다.(권고 사항으로 강제되지는 않는다)

robot.txt 과 같은 방법으로 웹서비스에 기여한 사람들을 human.txt 파일로 만들어 두기도 한다.
예)

client ip 를 nginx 에서 apache 로 전달하기

# 다음과 같이 apache httpd 이전에 nginx 에거 사용자 요청을 받는 경우,
# apache httpd 에서의 remote ip 는 nginx ip 가 된다.
client(10.10.10.10) --> nginx (20.20.20.20) --> apache httpd (30.30.30.30)

# apache httpd  에서 client ip 를 파악하기 위해선 nginx.conf 에서 다음과 같이
# 프록시(apache httpd)에 전달시 헤더에 X-Real-IP 나 X-Forwarded-For 값을 설정한다.
# $proxy_add_x_forwarded_for 는 $remote_addr 가 포함된 클라인트 요청 헤더다.
vi nginx.conf
server{
    location / {
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_pass http://30.30.30.30:PORT;
    }
}

# apache httpd 2.2 에선 mod_rpaf 를 설치하고
# rpaf 모듈을 사용하여 nginx 가 보낸 헤더를 사용할 수 있도록 다음과 같이 설정한다.
vi httpd.conf
LoadModule rpaf_module modules/mod_rpaf-2.0.so
<IfModule mod_rpaf.c>
    RPAFenable On
    RPAFsethostname On
    PAFheader X-Forwarded-For
    RPAFproxy_ips 20.20.20.20
</IfModule>

# apache httpd 2.4 부터는 RemoteIPHeader 를 제공하여 있어 다음과 같이 설정에 추가한다.
vi httpd.conf
LoadModule remoteip_module modules/mod_remoteip.so
<IfModule remoteip_module>
    RemoteIPHeader X-Forwarded-For
    RemoteIPTrustedProxy 123.123.123.123
    RemoteIPTrustedProxy 10.0.0.0/8
</IfModule>

참고
http://nginx.org/en/docs/http/ngx_http_proxy_module.html
https://chrismorris.org/passing-ip-from-nginx-to-apache/
https://github.com/y-ken/mod_rpaf
https://httpd.apache.org/docs/current/mod/mod_remoteip.html

apache http 2.2 -> 2.4 업그레이드

# apache http 2.2 -> 2.4 업그레이드
# 참고 https://httpd.apache.org/docs/2.4/upgrading.html
# api 변경 사항
# 참고 https://httpd.apache.org/docs/2.4/developer/new_api_2_4.html
# 2.4 부터 apr, apr-util 별도로 우선 설치해야 한다.(Apache Httpd 2.4 구성하기 포스트 참고)


#####


# 커스텀 아파치 모듈을 사용하는 경우
# undefined symbol: ap_rputs 등의 에러 발생
# 2.4 에서는 api 가 변경된것도 많아서 수정해서 다시 컴파일 수행(apr-1.5.2, apr-util-1.5.4 header, library 사용)

# 2.2
conn_rec->remote_ip
conn_rec->remote_addr

# 2.4
# useragent
request_rec->useragent_ip
request_rec->useragent_addr

# client
conn_rec->client_ip
conn_rec->client_addr

# apr 버전 체크
#include <apr_version.h>
#if (APR_MAJOR_VERSION >= 1 && APR_MINOR_VERSION >= 5)


#####


# httpd.conf 변경 사항
# 이름 변경
MaxClients -> MaxRequestWorkers
MaxRequestsPerChild -> MaxConnectionsPerChild

# 모든 접근 제한
# 2.2
Order deny,allow
Deny from all

# 2.4
Require all denied

# 모든 접근 허용
# 2.2
Order allow,deny
Allow from all

# 2.4
Require all granted

# 특정 호스트에 대해서만 접근 허용
# 2.2
Order deny,allow
Deny from all
Allow from example.org

# 2.4
Require host example.org

# 특정 ip 에 대해서만 접근 허용
# 2.2
Order deny,allow
Deny from all
Allow from 10.10.10.11 10.10.10.12

# 2.4
Require ip 10.10.10.11 10.10.10.12

# 제거된 지시자
# 2.2
# 0(최소)~9(최대) 출력
RewriteLogLevel 0

# 2.4
LogLevel info rewrite:trace1

# 2.2
RewriteLog /home/ysoftman/logs/apache/rewrite_log

# 2.4
별도의 rewritelog 파일은 지정할 수 없고,
LogLevel 를 사용하면 error_log 에 [rewrite: 로 시작하는 에러가 남겨진다.

# 2.2
# Allow/deny 로 ip 필터링 - 조건1
# 인증(id,password 물어봄) - 조건2
# Satisfy All 이면 조건1, 조건2 을 모두 만족해야 한다.
# Satisfy Any 이면 조건1, 조건2 둘 중 하나만 만족하면 된다.
Satisfy All 또는 Satisfy Any
# 2.4
Satisfy 지시자는 사용하지 않고, Satisfy All 기능이 디폴트로 됐다.

# 효과없어 무시되는 지시자
# 2.2
DefaultType
NameVirtualHost
# 2.4
효과 없음

# Invalid command 'User', perhaps misspelled or defined by a module not included in the server configuration 에러 발생시 다음 모듈 명시한다.
LoadModule unixd_module modules/mod_unixd.so

# Invalid command 'Require', perhaps misspelled or defined by a module not included in the server configuration 에러 발생시 다음 모듈 명시
LoadModule authz_core_module modules/mod_authz_core.so

# Unknown Authz provider: valid-user 에러 발생시 다음 모듈을 명시
LoadModule authz_user_module modules/mod_authz_user.so

# 위 모듈들은 configure 시 다음과 같이 옵션으로 모듈(.so) 빌드해서 생성한다.
--enable-mods-shared="unixd authz_core authz_user"

apache httpd.conf MPM 설정

# MPM(Multi Processing Module)
# 아파치 요청 처리를 프로세스(prefork) or 쓰레드(worker) 기반으로 할것이냐의 차이로 설정은 비슷하다.


참고 https://httpd.apache.org/docs/trunk/ko/programs/httpd.html
# 빌드시 같이 컴파일된 모듈(.c 정적모듈) 확인
# 참고로 동적모듈(LoadModule ... .so)은 웹서버 시작시 에러 없으면 정상 작동
httpd -l

# 버전 및 컴파일 옵션 확인
httpd -V (대문자 V)


# worker
# - n 개의 워커 쓰레드로 두고 각 쓰레드마다 요청 처리
# - 많은 요청을 처리할수 있지만, 쓰레드 하나가 전체 웹서버를 죽게 만들 수 있는 위험
# - 사용하려면 설치시 --with-mpm=worker
# httpd.conf 설정
<ifModule worker.c>
# 처음 시작시 생성할 쓰레드 개수
StartServers         2

# 여유분으로 최소 유지하는 쓰레드 개수
MinSpareThreads     25

# 여유분으로 최대 유지하는 쓰레드 개수
MaxSpareThreads     75

# 요청을 동시에 처리할 수 있는 쓰레드 개수
MaxRequestWorkers  150

# MaxRequestWorkers 가 생성할 수 있는 최대 쓰레드 개수
ServerLimit         16

# 프로세스당 쓰레드 개수
ThreadsPerChild     25
</IfModule>

# prefork
# n 개의 아피치 자식 프로세스로 각 프로세스마다 요청 처리(1process = 1thread)
# 메모리소요가 많이 되지만 안정적
# 사용하려면 설치시 --with-mpm=prefork(디폴트)
# httpd.conf 설정
<IfModule prefork.c>
# 처음 시작할때 생성할 프로세스 개수
StartServers        15

# 여유분으로 최소 유지하는 프로세스 개수
# 부하가 없어도 이값 위로 프로세스 개수를 유지하려고 한다.
# 절대적인것은 아니다.
MinSpareServers     10

# 여유분으로 최대 유지하는 프로세스 개수
# 부하가 많아 이값을 넘어서면 최대한 이 값아래로 프로세스 개수를 유지하려고 한다.
# 절대적인것은 아니다.
MaxSpareServers     50

# 요청을 동시에 처리할 수 있는 프로세스 최대 개수
# 2.3.13이전에는 MaxClients 라는 이름 사용하며 계속 지원되고 있다.
MaxRequestWorkers      1000

# MaxRequestWorkers 가 생성할 수 있는 최대 프로세스 개수
ServerLimit     1000

# 자식프로세스가 최대 처리하는 요청 개수
# 이 개수를 넘어가면 자식 프로세스는 죽는다.
# 0은 무한대로 자식 프로세스가 죽지 않는다.
MaxRequestsPerChild 2000
</IfModule>

# 참고
http://httpd.apache.org/docs/current/mod/mpm_common.html
https://httpd.apache.org/docs/2.4/mod/worker.html
https://httpd.apache.org/docs/2.4/mod/prefork.html

apache httpd log rotation

Apache httpd 로그파일이 시간이 갈수록 커지는데 일정 시간/사이즈로 파일을 분리하여 관리 할 수 있다.(log rotation)
apache httpd rotatelogs 설명
https://httpd.apache.org/docs/2.4/ko/programs/rotatelogs.html

apache httpd 로그 설명
https://httpd.apache.org/docs/2.4/ko/logs.html

##########

# rotatelogs 를 사용하는 경우
# Apache httpd 에서는 rotatelogs 를 제공한다.
# 이를 이용하면 특정 시간/사이즈 마다 로그 파일을 분리(백업)할 수 있다.
vim httpd.conf

# 24시간마다 로그 파일 구분
CustomLog "|bin/rotatelogs /www/logs/accesslog 86400" common

# 5M 크기가 넘어서면 로그 파일 구분
ErrorLog "|bin/rotatelogs /www/logs/error%Y%m%d%H%M.log 5M"


##########

# cronolog 를 사용하는 경우
# cronolog 는 yymmdd 등의 템플릿 설정에 따라 로그 파일을 구분한다.
# 맥에서 설치
brew install cronolog

# centos 에서 설치
sudo yum install cronolog
vim httpd.conf

# 현재 시간으로 로그 파일 구분
CustomLog "|/usr/sbin/cronolog /www/logs/access%Y%m%d%H%M.log"

# 심볼릭 링크를 추가하는 경우
ErrorLog "|/usr/sbin/cronolog -S /www/logs/error.log /www/logs/error%Y%m%d%H%M.log"

Apache httpd 에서 https http2 brotli 사용하기

# 2019-07-16 최신 버전 기준으로 내용 수정
# HTTP 2 는 기존 HTTP 1.x 의 비효율적인 속도를 개선한 버전으로 Multiplexed Strem, Header Compression 등의 기술이 포함되어 있다.
# 참고
# http://www.bloter.net/archives/210122
# https://icing.github.io/mod_h2/howto.html
# http://httpd.apache.org/docs/trunk/new_features_2_4.html
# http://httpd.apache.org/docs/2.4/programs/configure.html#installationdirectories

# apr 설치
wget http://apache.mirror.cdnetworks.com/apr/apr-1.7.0.tar.gz
tar zxvf apr-1.7.0.tar.gz
cd apr-1.7.0
./configure --prefix=${HOME}/apr
make -j8 && make install

# apr-util 설치
wget http://apache.mirror.cdnetworks.com//apr/apr-util-1.6.1.tar.gz
tar zxvf apr-util-1.6.1.tar.gz
cd apr-util-1.6.1
./configure --prefix=${HOME}/apr-util --with-apr=${HOME}/apr --with-crypto
make -j8 && make install

# openssl 설치
wget https://github.com/openssl/openssl/archive/OpenSSL_1_1_1c.tar.gz
tar zxvf OpenSSL_1_1_1c.tar.gz
cd openssl-OpenSSL_1_1_1c
./config --prefix=${HOME}/openssl -fPIC
make -j8 && make install

# nghttp2 설치
# 아파치 httpd 에서는 mod_http2 모듈을 로딩하여 HTTP 2 처리를 하게 된다.
# 자세한 내용은 https://httpd.apache.org/docs/2.4/mod/mod_http2.html
wget https://github.com/nghttp2/nghttp2/releases/download/v1.39.1/nghttp2-1.39.1.tar.gz
tar zxvf nghttp2-1.39.1.tar.gz
cd nghttp2-1.39.1
./configure --prefix=${HOME}/nghttp2 OPENSSL_CFLAGS="-I${HOME}/openssl/include" OPENSSL_LIBS="-L${HOME}/openssl/lib -lssl -lcrypto"
make -j8 && make install

# brotli 설치
# gzip 보다 향상된 압축방식이다.
# cmake 로 빌드하기 때문에 없으면 설치하자.
sudo yum install cmake
wget https://github.com/google/brotli/archive/v1.0.7.tar.gz
tar zxvf v1.0.7.tar.gz
cd brotli-1.0.7

# 빌드 결과물이 현재 위치에 생성되기 때문에 out 디렉토리를 생성하고 빌드하자.
mkdir -p out
cd out
../configure-cmake --prefix=${HOME}/brotli
make -j8 && make install

# httpd 설치
# 아파치 httpd 2.4.17 (부터 http2 지원) 이후 버전을 설치하자.
wget http://apache.tt.co.kr//httpd/httpd-2.4.39.tar.gz
tar zxvf httpd-2.4.39.tar.gz
cd httpd-2.4.39
./configure \
--prefix=${HOME}/apache-httpd \
--with-apr=${HOME}/apr \
--with-apr-util=${HOME}/apr-util \
--enable-brotli \
--with-brotli=${HOME}/brotli \
--enable-http2 \
--with-nghttp2=${HOME}/nghttp2 \
--enable-ssl \
--with-ssl=${HOME}/openssl \
--with-mpm=prefork \
--enable-mods-shared="unixd authz_core authz_user rewrite authz_host include log_config mime_magic headers unique_id setenvif mime status dir alias"
make -j8 && make install

# 참고
# apr, apr-util 소스를 별도로 빌드하지 않고
# httpd/srclib/apr, httpd/srclib/apr-util 이름으로 압축을 해제하면
# --with-apr --with-apr-util 옵션을 사용하지 않고 httpd 빌드할 수 있다.
# libapreq 설치
wget http://mirror.apache-kr.org//httpd/libapreq/libapreq2-2.13.tar.gz
tar zxvf libapreq2-2.13.tar.gz
cd libapreq2-2.13
./configure --with-apache2-apxs=${HOME}/apache-httpd/bin/apxs

# ${HOME}/apache-httpd/modules/mod_apreq2.so 로 설치된다.
make -j8 && make install

# http2 를 사용할 수 있도록 설정파일에 다음을 추가한다.
sudo vi conf/httpd.config
LoadModule http2_module modules/mod_http2.so
<IfModule http2_module>
LogLevel http2:info
</IfModule>

# h2 -> TLS(SSL) https 로 요청되는 경우
# h2c -> http 로 요청되는 경우
Protocols h2 h2c http/1.1

# 아파치 httpd 시작시 nghttpd 라이브러리를 참조할 수 있도록 환경변수 스크립트 수정
# 기존 경로에 openssl 과 nghttp2 를 추가해 준다.
vi ${HOME}/apache-httpd/bin/envvars
LD_LIBRARY_PATH="${HOME}/apache-httpd/lib:${HOME}/openssl/lib:${HOME}/nghttp2/lib:$LD_LIBRARY_PATH"
export LD_LIBRARY_PATH

# brotli 설정 추가
sudo vi conf/httpd.config
LoadModule brotli_module modules/mod_brotli.so
SetOutputFilter BROTLI_COMPRESS

# 아파치 httpd 시작~
sudo ${HOME}/apache-httpd/bin/apachectl -k start

# gzip brotli 압축 사용시 사이즈 보기
curl -H "Accept-Encoding: gzip" -o out.gz https://ysoftman.com/test
curl -H "Accept-Encoding: br" -o out.br https://ysoftman.com/test

# gzip 압축해제
gzip -d -k out.gz

# brotli 압축해제
# brew install brotli 설치
# https://github.com/google/brotli/blob/master/c/tools/brotli.md

웹 액세스 로그 시각화 툴 LogStalgia

apache, nginx 와 같은 웹 서버의 accesslog 를 visualization(시각화)해주는 툴이다.
윈도우, 리눅스, 맥 모두에서 동작한다. (콘솔환경은 안됨)
다운로드 http://logstalgia.io/

# 로그파일 기준으로 플레이(실행)
logstalgia access.log

# 실시간으로 시각화 하기
tail -f /var/log/apache2/access.log | logstalgia --sync