Reverse Proxy

실 운영 환경에서는 80, 443 빼고는 inbound를 막아두는 경우가 많아서 

iptables 써서 80 -> xxxx로 포워딩하거나, nginx써서 80 -> xxxx로 포워딩한다. 

 

후자를 더 많이 사용하는데 그 이유는

  • nginx를 쓰면 설정이 좀 귀찮기는 하지만, 한 장비에 여러 인스턴스를 띄우고 이들을 domain(혹은 location)으로 구분하므로 모두 80으로 받을 수 있다.
    • jenkins나 sonarqube 등 설정 파일에서 home location (e.g., /jenkins)을 설정할 수 있도록 지원하는 것이 이 것 때문.
  • 어차피 server IP가 변경될 상황을 대비해서 도메인을 쓰긴 써야하니 그럴 바에 nginx로 리버스 프록시하는게 낫다.
    • IP로 hook 같은거 막 등록해놨다가 서버 이전이나 인스턴스 분리하는 순간이 오면... 다 찾아가서 바꿔줘야 하니 난감함.

 

proxy_pass 설정하면?

```

a.b.c:80, d.e.f:80 모두 nginx에서 받아서, 

a.b.c:80 -> localhost:8080

d.e.f:80 -> localhost:9001

```

로 포워딩이 가능하다. (nginx, 인스턴스들 모두 같은 물리장비에 위치)

domain 뿐만 아니라 location으로도 구분할 수 있다.

 

configure 예시

./configure \
--prefix=/home1/user1/apps/nginx-1.20.2  \
--user=user1 \
--group=user1 \
--error-log-path=/home1/user1/logs/nginx/error.log \
--http-log-path=/home1/user1/logs/nginx/access.log \
--without-http_scgi_module \
--without-http_uwsgi_module \
--without-http_fastcgi_module \
--with-http_ssl_module \
--with-http_v2_module \
--with-http_stub_status_module \

 

설정 예시

설정 예시는 기본적으로 설치하면 딸려오는 nginx.conf에서 주석 처리 되어 있는 부분을 참고하는게 제일 좋다.

 

```nginx

    server {

        listen       80;

        server_name  aaaa.bbbb.ccc.com;

 

        location /http_stub_status {

            stub_status on;

            allow 127.0.0.1;

            deny all;

        }

 

        location ~ /WEB-INF/ {
            access_log off;
            log_not_found off;
            deny all;
        }

 

        location / {
            if ($request_method !~ ^(GET|POST|PUT|DELETE)$ ) {
                return 444;
            }

            add_header X-XSS-Protection "1; mode=block";
            proxy_set_header Connection "";
            proxy_set_header Host $host;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_set_header X-Forwarded-By $server_addr:$server_port;

            proxy_connect_timeout 3600;
            proxy_read_timeout 3600;
            proxy_send_timeout 3600;
            proxy_pass http://localhost:8080/;
        }

```

NGINX even provides a $proxy_add_x_forwarded_for variable to automatically append $remote_addr to any incoming X-Forwarded-For headers.
https://www.nginx.com/resources/wiki/start/topics/examples/forwarded/

 

location 지정 (반드시 docs 참고)

 

특정 url은 일부 ip만 허용하고 나머지는 block

```nginx

location ~* ^/somepath(\/.*)?$ {
  allow 10.0.0.0/8;
  deny all;
}

```

 

참고

 

SSL 인증서 관리

인증서 발급 & 인증 받기

인증서는 주기적으로 갱신되어야 하고, 갱신된 최신 인증서를 사용해야 하므로 nginx도 주기적인 reload 필요하다.

 

보안 관련 설정

add_header Strict-Transport-Security "max-age=31536000; preload";
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
  • HSTS 설정
    • https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security
    • 브라우저에게 해당 서버는 아예 80 요청 받지 않는다고 헤더에 추가해 response. 브라우저는 이를 max-age 동안 기억해 놓고 앞으로 해당 호스트에는 443으로만 접근함.
      • 따라서 최초 접근 이후로는 브라우저 단에서 443 통신 강제.
    • 매번 80 요청 들어왔을 때 443으로 redirect 하도록 처리하는 것 보다 안전.
      • 최초 접근 시 80에서 redirect 할 때, MITM을 통해 의도했던 443 site가 아니라 malicious site로 이동할 가능성이 있기 때문.
    • preload 설정?
      • 브라우저는 preload 리스트를 보고, 이 리스트에 존재하는 호스트로는 HSTS header response를 받은 적이 없더라도 443으로만 접근함.
      • 해당 사이트에 최초 접근하는 순간 까지도 443 통신할 수 있다는 장점이 있음.
  • X-Content-Type-Options
    • 브라우저가 리소스를 해석할 때, 설정된 MIME type 대로만 해석 가능하도록 제약 설정.
    • e.g., text/css 일 때만 css로 해석 가능하고, javascript 일 때만 javascript로 해석 가능하다. MIME type이 image이면 javascript로 해석하지 않는다.
  • X-XSS-Protection

 

Upstream ( 부하 분산, load balancing )

https://opentutorials.org/module/384/4328  

 

Rate Limiting

https://yobi.navercorp.com/phoenix_tf/posts/11  

https://www.nginx.com/blog/rate-limiting-nginx/  

https://docs.nginx.com/nginx/admin-guide/security-controls/controlling-access-proxied-http/   

 

  • NGINX는 요청 한도를 ms 단위로 쪼개서 관리한다.
    • 즉, 5r/s이면 1초에 5개를 보장하는게 아니라 200ms 당 1개씩 요청을 수용한다.
    • 따라서 200ms 안에 요청 5개가 한꺼번에 오면 1개만 처리하고 4개는 503 응답 나간다.
  • 1r/s인데 1초 안에 요청이 2개 들어왔다면? 뒤에 들어온건 503이 나가겠지만, 이렇게 넘치는 요청을 버리지 않고 대기 Queue에 넣고 싶을 수 있음. 이 옵션이 바로  burst
  • 10r/s, burst=20으로 설정되어 있는데 100ms 안에 요청 25개가 들어오는 경우,
    • 1개는 바로 업스트림으로 전달
    • 20개는 대기큐로 간 다음 100ms 마다 1개 씩 꺼내서 업스트림으로 전달
    • 4개는 503 거절
  • nodelay 옵션은 대기큐에 있는걸 100ms 마다 꺼내서 전달하면 대기큐 맨 마지막에 있는 애는 너무 늦게 서빙되니까, 애초에 처음에는 대기큐에 안넣고 burst 개수 만큼 바로바로 전송해버리자. 라는 것임.
    • 페이지 로딩 시 정적 리소스가 여러개 필요할 수 있는데 이런 애들 하나 당 rate에 설정된 시간만큼 씩 기다려야 한다면 페이지 로딩이 무지 느리다. 
    • 10r/s, burst=20, nodelay인데 100ms 안에 25개가 들어오는 경우,
    • 21개 즉시 전달하고, 대신 20개의 대기슬롯에 taken표시. 
    • taken 표시 되어 있는 슬롯은 못쓴다. 100ms 마다 1개씩 슬롯 taken 표시 해제.
  • delay 옵션은 읽어보는게 나을 듯.

 

Illustration of rate-limiting behavior with rate=5r/s burst=12 delay=8

 

rate limit for each uri

```nginx

limit_req_zone $server_name zone=slow:10m rate=10r/s;

limit_req_zone $server_name zone=fast:10m rate=100r/s;

 

이렇게 설정하면?

모든 요청에 대해서, slow, fast zone에 카운트가 추가되고...

 

모든 요청이 10r/s를 넘어가는 경우 => slow 지정된 것들은 block된다.

모든 요청이 100r/s를 넘어가는 경우 => fast 지정된 것들이 이제서야 block됨.

 

그니까 카운트하는건 모든 요청에 대해서라 이렇게는 안될 것 같고...

 

각 uri 별로 다 다르게 주고 싶다면

limit_req_zone $uri zone=slow:10m rate=10r/s;

limit_req_zone $uri zone=fast:10m rate=100r/s;

 

각 uri 별로 카운트를 따로 함. (한 uri에 대해서 slow, fast 둘 다 카운트가 올라가긴 한다)

 

한 uri에 대한 요청이 10r/s를 넘어가는 경우 => slow로 지정된 location이라면 block

ㅇㅇ 이렇게 하면 될 듯.

```

근데 WAS 단에서도 설정하는게 있는데... 어디서 하는게 더 나은건지

https://stackoverflow.com/questions/39002090/spring-boot-limit-on-number-of-connections-created

https://docs.bmc.com/docs/brid91/en/tomcat-container-workload-configuration-825210082.html  

https://stackoverflow.com/questions/60502584/tomcat-what-happens-when-number-of-connections-exceeds-number-of-threads  

 

'DevOps & Server' 카테고리의 다른 글

ELK 구축 부터 log 파싱, 적재 까지 (with SpringBoot) (OpenSearch)  (0) 2021.09.11
SonarQube  (0) 2020.12.08
[CI/CD] 젠킨스 Jenkins  (0) 2019.05.09
vagrant  (0) 2018.11.02
ansible 앤서블 설명  (0) 2018.10.30