• 돌아가기
  • 아래로
  • 위로
  • 목록
  • 댓글
가이드

caddy 를 이용한 역방향 프록시 설치 사용기 입니다.

미오뜨 1120

6

9

안녕하세요

요즘 caddy 를 사용하시는 분들이 많아 보여서 궁금하기도 하고 http/3도 경험해 볼 겸

traefik 에서 넘어와 봤습니다.

기대한대로 속도도 빠르고 설정은 넘사벽으로 간단해서 빠져들것 같습니다.

간단한 설치과정을 공유하고자 글을 올려요.

 

우분투 22.04에서 사용중입니다.

 

1. 도커 이미지 만들기

적당한 폴더를 만들고  Dockerfile 을 만들어서 아래와 같이 작성했습니다.

 

Dockerfile

FROM caddy:builder AS builder

RUN xcaddy build \
        --with github.com/caddy-dns/cloudflare \
        --with github.com/hslatman/caddy-crowdsec-bouncer/http \
        --with github.com/hslatman/caddy-crowdsec-bouncer/crowdsec \
        --with github.com/caddyserver/transform-encoder \
        --with github.com/corazawaf/coraza-caddy/v2@main

FROM caddy:latest

COPY --from=builder /usr/bin/caddy /usr/bin/caddy

빌드시 포함한 모듈

cloudflare

클라우드플레어에서 발급받은 도메인을 사용중이며 dns 방식으로 인증서를 자동으로 발급받기 위한 모듈입니다.

 

crowdsec

보안 관련 crowdsec 을 사용하기 위한 모듈입니다.

 

transform-encoder

access.log 로그를 apache 형식으로 전환하려고 설치했습니다.

 

coraza-caddy

OWASP Coreruleset 을 사용하기 위한 모듈입니다.

주의하실 점이 caddy 공홈에서 다운로드시 알려주는 github 주소를 따라서 설치 하면

이후 모듈 설명서와 동일하게 설정해도 오류가 발생하니, 위와같이 경로를 참조해 주셔야 합니다.

 

docker build --tag caddy:custom .

이렇게 입력하시면  이미지가 빌드 됩니다. 

참고로 golang, xcaddy 는 미리 설치되어 있어야 합니다. 

 

2. docker-compose.yml 작성하기

docker-compose.yml

version: "3.9"

services:

  caddy:

    image: caddy:custom

    container_name: caddy
    hostname: caddy
    env_file: .env

    restart: always
    networks: # 각자 사용중인 네트워크 설정을 적용하시면 됩니다.
      t2_proxy:
        ipv4_address: 192.168.x.xxx
    ports:
      - "80:80"
      - "443:443"
      - "443:443/udp"
    volumes:
      - $DOCKERDIR/appdata/caddy/Caddyfile:/etc/caddy/Caddyfile
      - $DOCKERDIR/appdata/caddy/caddy_config:/config
      - $DOCKERDIR/appdata/caddy/caddy_data:/data
      - $DOCKERDIR/appdata/caddy/ruleset:/ruleset
      - $DOCKERDIR/shared:/shared
      - $DOCKERDIR/logs/cloudserver/caddy/access.log:/var/log/access.log
    environment:
      - TZ=$TZ
    cap_add:
      - NET_ADMIN # udp buffer size error
    labels:
      - 'com.centurylinklabs.watchtower.enable=false'

내용설명

caddy:custom

이미지 이름은 빌드시 설정한 이름입니다.

 

.env

환경파일을 지정했습니다.

 

네트워크 설정은 각자 사용중인 네트워크 또는 설정 안하고 도커 기본 브릿지 네트워크를 사용하시면 됩니다.

443/ udp 포트는 http/3를 사용하기 위해 설정합니다.

 

기본으로 바인딩 된 볼륨 외에 ruleset 폴더는 coraza 규칙들을 저장하기 위한 폴더이며

shared 폴더는 내부망에서 https 로 역방향 프록시를 적용할때 필요한 인증서가 저장될 폴더입니다.

 

crowdsec 에서 로그를 파싱하기 위한 접근로그를 만듭니다.

access.log 과 Caddyfile 파일은 도커 생성시 자동으로 생성되지 않으니 미리 만들어 두세요.

 

net admin

설정은 보통 caddy 설치 하시면 로그에 udp 관련 오류가 잡히는데 호스트에서

관련 설정을 수정해 주더라도 도커로 실행되서 그런가 오류가 계속 발생해서 옵션을 주었습니다.

 

마지막 lables 설정은 커스텀 이미지라 watchtower 감시대상에서 제외하기 위한 설정입니다.

 

docker-compose.yml 이 설치된 폴더에 .env 파일을 만들고 사용할 환경변수를 저장해 줍니다.

 

.env

TZ="Asia/Seoul" 
DOCKERDIR="/home/변경/docker"

DOMAINNAME_CLOUD_SERVER=변경.com 

CLOUDFLARE_EMAIL=변경@gmail.com
CF_API_TOKEN=hcdXqKwsT7DLs변경

 

클플은 키가 아니라 토큰입니다. 사소해 보이지만 키넣고 안되서 고생했습니다. 메뉴얼은 꼼꼼히 봐야 되요 ㅠㅠ

발급 과정은 달소님이 잘 설명해 주셨습니다.

 

 이제 도커를 올려줍니다.

 docker-compose up -d

# 일반적으로 제가 자주 쓰는 형식입니다.

docker-compose -f custom.yml up -d --force-recreate 도커명

 

3. caddy 설정하기

caddy 의 설정 파일은 json 방식과 Caddyfile 방식으로 나눌 수 있습니다.

전자는 모든 caddy 설정이 가능하지만 복잡하고 후자는 대부분의 설정이 가능하지만 간단합니다.

저는 Caddyfile 방식으로 설정했습니다.

아래 올린 설정을 한번에 적용하기보다는 인증서 발급 > 프록시 설정 > 각종모듈 설정순으로 진행하시는게

오류 잡기 편하실 거에요. 설정을 적용하시고 싶으면

 docker-compose exec -w /etc/caddy caddy caddy reload

 입력하시면 도커를 재시작 할 필요 없이 간단히 적용됩니다.

 간혹 이쁘게 정렬 안시켰다고 에러가 뜨면 아래와 같이 해결하시면 됩니다.

docker-compose exec -w /etc/caddy caddy caddy fmt --overwrite

 이미 만들어둔 Caddyfile 을 편집합니다. 편의상 세부분으로 나누어 설명하나 하나의 파일 내용입니다.

 

Caddyfile - 공통설정

###################################
# common definition
###################################

(log) {
        log {
                format transform `{request>client_ip} - {request>user_id} [{ts}] "{request>method} {request>uri} {request>proto}" {status} {size}` {
                        time_format "02/Jan/2006:15:04:05 -0700"
                        time_local
                }
                output file /var/log/access.log
        }
}

(common) {
        encode zstd gzip
        tls {
                dns cloudflare {env.CF_API_TOKEN}
        }
        header {
                Strict-Transport-Security "max-age=31536000; includeSubdomains; preload"
                # Enable cross-site filter (XSS) and tell browser to block detected attacks
                X-XSS-Protection "1; mode=block"
                X-Content-Type-Options "nosniff"
                Referrer-Policy "same-origin"
                # Disallow the site to be rendered within a frame (clickjacking protection)
                X-Frame-Options "ALLOW-FROM *.변경.com"
                # Prevent search engines from indexing (optional)
                X-Robots-Tag "none, noarchive, nosnippet, notranslate, noimageindex"
                # Server name removing
                -Server
                # Content-Security-Policy "default-src 'none'; script-src 'self'; connect-src 'self'; img-src 'self'; style-src 'self';base-uri 'self';form-action 'self';frame-ancestors 변경.com *.변경.com"
                Permissions-Policy "geolocation=(), camera=(), microphone=(), payment=(), usb=(), vr=()"
        }
}

(authelia) {
        forward_auth authelia:9091 {
                uri /api/verify?rd=https://auth.변경.com/
                copy_headers Remote-User Remote-Groups Remote-Name Remote-Email
                @error status 4xx 5xx
                handle_response @error {

                        import waf
                        reverse_proxy error-pages:8080 {
                                header_up +X-Code {rp.status_code}
                        }
                }
        }
        import log
}

(reverse) {
        @{args[1]} host {args[1]}.{$DOMAINNAME_CLOUD_SERVER}
        handle @{args[1]} {
                reverse_proxy 192.168.x.xx:{args[0]}
        }
}

(reverse-inner) {
        @{args[2]} host {args[2]}.{$DOMAINNAME_CLOUD_SERVER}
        handle @{args[2]} {
                reverse_proxy {args[2]}{args[1]}{args[0]}
        }
}

(waf) {
        coraza_waf {
                load_owasp_crs
                directives `
                Include /ruleset/coraza-coreruleset/rules/@coraza.conf-recommended
                Include /ruleset/coraza-coreruleset/rules/@crs-setup.conf.example
                Include /ruleset/coraza-coreruleset/rules/@owasp_crs/*.conf
                SecRuleEngine On
                `
        }
}

(chain-auth) {
        crowdsec
        import common
        import authelia
}

(log)

일반적으로 사용시에는 로그포멧을 변경할 필요는 없습니다. 오히려 더 많은 정보를 json 방식으로 보여줍니다.

하지만 crowdsec 을 사용할 때 로그를 파싱하긴 하는데 정상적으로 규칙을 적용 하지 못합니다.

그래서 불편하지만 apache 형식으로 변환을 해주어야 정상동작 했습니다.

전반적인 crowdsec 설정은 여기서 다루기엔 너무 길어질 것 같아서 다른 글을 참고 해 주세요.

output 에 컴포즈 파일에서 설정한 도커 내부경로를 입력해 줍니다.

(common) / (authelia)

(common)

전송시 사용할 압축과 클라우드플레어 dns 방식을 이용한 인증서발급을 위한 설정이 적용되어있습니다.

보안 헤더 부분은 본인이 잘 아시는 방법으로 적용해 주시면 됩니다.

전 초보라서 블로거등에서 추천받은 대로 적용해서 사용중입니다.

 

(authelia)

2fa 인증기로 사용중인 authelia 관련 설정입니다. 안쓰시는분은 적용하실 필요가 없습니다.

사용 하시는 분을 위한 약간의 팁으로 제경우 4xx 5xx 에러페이지를 error-pages 라는 도커를 사용해서

이쁘게(?) 만들었는데 authelia 인증실패시 403 fobbiden 딸랑 한줄이 너무 심심해 보여서 페이지를 포워딩 시켜줬습니다. authelia 에서 에러 반환시 응답을 받아서 X-Code 헤더에 추가시켜준 후 에러페이지로 보내줍니다.

마지막으로 (authelia)의 설정은 모든 서브도메인에 적용되기 때문에

access.log 에 로그를 기록하도록 import 를 이용해 log 를 적용해 줍니다.

(reverse) / (reverse-inner)

(reverse)

caddy의 장점이 단 몇줄로 역방향 프록시가 가능하단 점인데요. 극대화 시키기 위해 단순화 시켰습니다.

적용 방법은 import reverse 도커외부포트 도커이름 이렇게 해주시면 됩니다.

 

(revers-inner)

docker-compose.yml 설정 중에 ports 는 도커 내부포트와 외부포트를 연결 시켜주는 설정입니다.

예를들어 8443:443 이라고 설정 하시면 도커내부의 443 포트는 호스트와 8443 으로 연결되게 됩니다.

이런 방법을 통해 호스트에서는 도커가 사용하는 포트로 부터 자유롭게 포트설정이 가능해 지는데요.

### 사족 이겠지만 ports 설정이 없는경우 외부로 열린 포트가 없으니

도커 브릿지 내부망이 아닌 외부에서는 접근 할 수 없습니다. ###

 

만약 caddy 가 다른 도커 예를들어 portainer (ports 9900:9000)와 동일한 네트워크에

물려있다면 (2.번 문단 networks 설정 참조 : t2_proxy) 서로간에는 portainer 의 내부포트(9000)를 이용해서

통신이 가능해 집니다. 뿐만아니라 192.168.x.xx:9900 이외에 portainer:9000 으로도 도커간 연결이 가능 합니다.

 

위 두 설정은 기본적으로 동일한 방법이나 변수가 하나 추가 됬는데, 그 이유는 {arg}:{arg} 이런 식으로 작성하면

오류가 납니다. : 문자를 일반적으로 코딩할때 범위 적용할때 쓰는 그런걸로 인식하나 봅니다.

어쩔수 없이 : 문자도 입력 받는 변수로 추가 해 줬습니다. (2.7.6 에서 버그수정 됨)

둘의 차이는 ports 설정을 통한 다른 도커 네트워크와의 연결인가 아니면

동일 네트워크에 속하는 도커로서 ports 설정이 없는 연결인가의 차이점입니다.

(waf) / (chain-auth)

(waf)

coraza-caddy 모듈을 사용하기 위한 설정입니다. 관련된 자세한 설명은 다른 게시물을 참조해 주세요.

제가 설명하기엔 지식이 모자라서... 죄송합니다. 단 설정과정에서 load_owasp_crs 이 옵션은 앞서 말씀드린 바와 같이 v2@main 브란치의 모듈을 불러와야 오류가 발생하지 않습니다.

사용되는 3개의 conf 는 github 에서 clone 해오시면 됩니다. 호스트에서 2.번 문단에서 바인드 시킨 ruleset 폴더로 가신다음 git clone https://github.com/corazawaf/coraza-coreruleset.git 을 실행 하시면 됩니다.

 

(chain-auth)

자주 쓰는 설정을 모아모아서 단순화 시켰습니다.

 

 

Caddyfile - server / modules


        # debug
        email {$CLOUDFLARE_EMAIL}
        servers {
                metrics
                trusted_proxies static 173.245.48.0/20 103.21.244.0/22 103.22.200.0/22 103.31.4.0/22 \
 141.101.64.0/18 108.162.192.0/18 190.93.240.0/20 188.114.96.0/20 197.234.240.0/22 \
 198.41.128.0/17 162.158.0.0/15 104.16.0.0/13 104.24.0.0/14 172.64.0.0/13 131.0.72.0/22 \
 2400:cb00::/32 2606:4700::/32 2803:f800::/32 2405:b500::/32 2405:8100::/32 2a06:98c0::/29 2c0f:f248::/32 \
 10.0.0.0/8 172.16.0.0/16 192.168.0.0/16 fc00::/7
        }

        order coraza_waf first
        order crowdsec first

        crowdsec {
                api_url http://crowdsec:8080
                api_key 09zP5A7변경
                ticker_interval 10s
                # disable_streaming
                # enable_hard_fails
        }

        # for metrics only 192./24 
        admin :2019
}

server / modules

debug

caddy 의 자체 로그 설정입니다. (엑세스 로그가 아님)

 

email

자동으로 인증서 발급을 위해 필요한 설정입니다.

 

metrics

api를 통한 정보를 보내줍니다. 아래 admin :2019 를 통해 api 접근이 가능하게 됩니다.

 

trusted proxy

클라우드플레어의 프록시를 신뢰하는 프록시로 추가 하고 내부망으로 예약된 주소도 포함시켜 줬습니다.

외부에서 접근하는 ip 를 정상적으로 표시하기 위해 설정 했습니다.

 

order

crowdsec 과 coraza 를 사용하기 위해 필요한 옵션입니다. crowdsec 사용안하시는 분은 패스하시면 됩니다.

 

 

Caddyfile - reverse proxy

###################################
# reverse proxy services
###################################

 

{$DOMAINNAME_CLOUD_SERVER} { 
        import waf
        import chain-auth
        reverse_proxy homepage:3000
}

auth.{$DOMAINNAME_CLOUD_SERVER} {
        import waf
        import chain-auth
        reverse_proxy authelia:9091
}

*.{$DOMAINNAME_CLOUD_SERVER} {
        import chain-auth

        @dsm host dsm.{$DOMAINNAME_CLOUD_SERVER}
        handle @dsm {
                reverse_proxy https://192.168.x.xx:5001 {
                        transport http {
                                # tls_insecure_skip_verify
                                # tls_server_name xxx
                                tls_trusted_ca_certs /shared/cert/domain.com/dsm.pem
                        }
                }
        }
        import reverse xxxx wyl
        import reverse xxxx ip1
        import reverse xxxx sh
        import reverse xxxx whoogle
        import reverse xxxx qbit

 

        import reverse-inner 80 : speedtest
        import reverse-inner 80 : heimdall
        import reverse-inner 80 : filerun
        import reverse-inner 2368 : ghost
        import reverse-inner 3000 : grafana
        import reverse-inner 5244 : alist
        import reverse-inner 5800 : handbrake
        import reverse-inner 7880 : goaccess
        import reverse-inner 8080 : miniflux
        import reverse-inner 8080 : shiori
        import reverse-inner 8080 : dozzle
        import reverse-inner 8080 : guacamole
        import reverse-inner 8443 : vscode
        import reverse-inner 9000 : portainer
        import reverse-inner 53842 : gokapi
        import reverse-inner 61208 : glances

        handle {
                abort
        }
}

reverse proxy

domain.com auth.domain.com *.domain.com 총 세개의 부분으로 구성했습니다.

domain.com 은 homepage 도커로 연결됩니다.

 

IMG_0080.png.jpg

 

처음 설정하시는 분들은 클라우드플레어 설정과 이부분만 간단히 설정 하시고

먼저 정상적으로 인증서가 발급되는지 확인하고 진행하시는게 오류잡기 편하실 거에요.

 

공통설정 부분에서 작성해 놓은 것이 요긴하게 쓰입니다. 필요한 설정들만 import 해서 사용하시면 됩니다.

 

dsm 설정을 보시면 내부적으로 https 통신이 사용되는 경우 쓸 수 있는 방법을 보여줍니다.

transport http 하단의 세개의 설정중에 제일 위 설정만 활성화 하면 유효성 인증을 불문하고 https 로 연결을 해주고,

아니면 위와같이 클라이언트의 인증서를 직접 caddy 에서 신뢰하라고 넣어 주실 수 도 있습니다.

traefik 의 경우 도커 내부에서 mkcert 같은 프로그램으로 루트 인증서를 만들어 두면 별다른 과정 없이 잘 동작 했는데 caddy 는 아직 조금 아쉽더라구요. opnsense, proxmox 역시 이 방법들을 활용하시면 연결이 가능 할 것 같습니다.

 

 

 우... 길었습니다. 완전 길어서 읽는 분들이 엄청 피곤하실 것 같네요. ㅠㅜ

 

소감은 앞서 말씀드린 것처럼 압도적인 편의성인 것 같습니다. 이것저것 신경 안써도 되고 단 몇줄로 해결 된다는게 오래전 nginx 와 bsd 로 온갖 삽질을 하던게 주마등처럼 지나 가네요. traefik 과 비교해서도 속도도 꿀리지 않는 것 같습니다.

구글이나 클플로 측정해도 비슷비슷합니다. 진입장벽도 낮으니 한번씩 도전해 보시길 바라요 ^^/

 

신고공유스크랩
9
미오뜨 글쓴이 2023.10.16. 17:21
빅커
감사합니다. 예전에 설치한 이후에 여러가지 위젯이 많아져서 설치하는 재미가 늘어났어요.
2등
zepinos 2023.10.16. 16:37
caddy 관리는 text 로만 하시나요? caddy proxy manager 나 caddy-ui 같은건 안쓰시나요?
profile image 3등
달소 2023.10.16. 23:44
와,, 에디터 접기까지 활용해주시다니,, 감동입니다.

내용 하나하나 알차고 완벽하네요!! 오픈소스 가이드 Caddy 쪽에 추가해도 괜찮을까요??
저도 따라해봐야겠네요 ㅎㅎ 감사합니다~~
reswns0210 2024.03.08. 21:53
안녕하세요, 글 보고 한번 해보고있는데 볼륨쪽에서 에러가 발생하는거같습니다. 이건 어떻게 해결해야될지 아실까요...?

flags: 0x5000: not a directory

댓글 쓰기 권한이 없습니다. 로그인

취소 댓글 등록

cmt alert

신고

"님의 댓글"

이 댓글을 신고하시겠습니까?

댓글 삭제

"님의 댓글"

삭제하시겠습니까?


목록

공유

facebooktwitterpinterestbandkakao story
번호 분류 제목 글쓴이 날짜 조회 추천
가이드 배드섹터 있는 HDD 복사 및 이미지 뜨기 : ddrescue, ddrescueview klayf 12시간 전14:16 114 +6
오픈소스 개인 구독 추적기 Wallos 9 달소 4일 전23:26 385 +3
질문 /var/www 폴더가 삭제되었습니다. ㅜ,.ㅜ 2 니속사정 6일 전20:42 309 +1
3912 가이드
image
klayf 12시간 전14:16 114 +6
3911 후기
image
kmw_ 12시간 전14:12 92 0
3910 질문
image
동도리군 13시간 전13:23 140 0
3909 잡담
normal
SNFAIUWQ 1일 전21:40 63 0
3908 잡담
normal
kmw_ 1일 전18:44 179 0
3907 질문
normal
newsted1 1일 전09:57 109 0
3906 질문
normal
초보입니다ㅠㅠ 2일 전01:44 141 0
3905 질문
normal
서버구축하자 3일 전21:59 88 0
3904 후기
image
달소 3일 전10:59 159 0
3903 질문
image
경호 3일 전10:34 249 0
3902
image
달소 4일 전23:26 385 +3
3901 잡담
image
달소 4일 전22:44 184 0
3900 질문
normal
맛밥 5일 전16:13 232 0
3899 질문
normal
니속사정 6일 전20:42 309 +1
3898 잡담
image
kmw_ 6일 전19:26 496 +1
3897 잡담
image
ljr10 6일 전18:44 274 0
3896 질문
image
ljr10 6일 전12:45 190 0
3895 잡담
image
purndal 24.05.06.00:52 230 +2
3894 질문
normal
스키피95 24.05.05.00:07 155 0
3893 질문
normal
고심분투 24.05.04.17:10 272 0