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으로도 구분할 수 있다.

 

설정 예시

```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/;
        }

```

 

location 지정 (반드시 docs 참고)

 

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

```nginx

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

```

 

 

 

Client IP 구하기 : X-Forwarded-For와 X-Real-IP

Proxy / VPN

 

LB, Proxy, Cache Server 등을 거치면 srcIp가 변경되기 때문에 원래의 Client IP를 가져오기 위한 방법이 필요하다.

이를 위해 X-Forwarded-For 필드가 존재하며, 경로를 지날 때 마다 우측 끝에 하나 씩 IP를 추가하여 Origin src IP를 확인할 수 있도록 하는 표준 헤더다.

 

e.g., Client IP가 1.1.1.1 이면?

nginx는 XFF 우측 끝에 1.1.1.1을 붙여준다. `` X-Forwarded-For: 1.1.1.1``

tomcat에서 받을 때는, 보통 맨 앞에 있는 IP를 클라이언트로 가져오게 된다. (최초의 IP가 클라IP 일거라는 가정)

 

e.g., 근데 유저가 임의로 헤더를 조작해 Client가 XFF에 미리 3.3.3.3을 실어서 보내는 경우라면?

nginx는 역시 XFF 우측 끝에 1.1.1.1을 붙여준다. `` X-Forwarded-For: 3.3.3.3, 1.1.1.1``

tomcat은 맨 앞에 있는 IP를 ClientIP로 가져온다 = 3.3.3.3

 

따라서... 위변조가 가능하기 때문에 X-Real-IP 를 함께 사용한다.

nginx의 X-Real-IP는 바로 직전 proxy server의 IP를 나타낸다.

`` Client - Nginx - Tomcat`` 으로 연결된 구조라면 (직전 IP == Client IP)

 

tomcat 설정 예시

```xml

Valve className="org.apache.catalina.valves.RemoteIpValve" remoteIpHeader="x-real-ip"/>

```

 

참고

 

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' 카테고리의 다른 글

SonarQube  (0) 2020.12.08
NGINX  (0) 2020.12.04
[tomcat] 톰캣, jvm 실행 인자  (0) 2020.03.05
[CI/CD] 젠킨스 Jenkins  (0) 2019.05.09
[Gradle] build, jar 배포, IntelliJ 설정  (0) 2019.04.10
vagrant  (0) 2018.11.02