跳转至

Caddy V2 介绍

Caddy 2 是一个强大的、企业级的、开源的 Web 服务器,具有用 Go 编写的自动 HTTPS。也是当前唯一默认自动使用 TLS 的 Web 服务器。 使用 Caddy 2 轻松部署和扩展 HTTPS 。(静态文件、反向代理、负载平衡等)。

安装 Caddy

apt 安装 caddy

sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install caddy

ansible

- hosts: caddy_huawei_shanghai
  remote_user: root
  tasks:
    - name: Sync Caddyfile
      copy:
        src: caddy_huawei_shanghai/Caddyfile
        dest: /etc/caddy/Caddyfile

    - name: caddy add-package github.com/caddy-dns/dnspod
      shell: "caddy list-modules|grep dnspod ; [ $? -eq 0 ] || (echo 不存在,正在安装 ; caddy add-package github.com/caddy-dns/dnspod ; systemctl restart caddy)"

    - name: Reload
      shell:
        chdir: /etc/caddy
        cmd: caddy reload

docker compose 方式管理

1. 创建工作目录

mkdir /root/code/Caddy/

2. 编写 compose 文件

/root/code/Caddy/compose.yaml
services:
  caddy:
    image: swr.cn-east-3.myhuaweicloud.com/china/caddy:2.7.5
    # image: caddy:<version>
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
      - "443:443/udp"
    volumes:
      - ./caddy:/etc/caddy/
      - ./site:/srv
      - ./caddy_data:/data
      - ./caddy_config:/config

Note

数据目录不能被视为缓存。它的内容不是短暂的,也不是仅仅为了表演。Caddy 将 TLS 证书、私钥、OCSP 订书钉和其他必要信息存储到数据目录中。如果不了解其含义,则不应将其清除。

3. 编写 alias 快捷命令

/root/code/Caddy/compose.yaml
# caddy 相关
caddy_dir="/root/code/Caddy/"
alias caddy-edit='vim ${caddy_dir}caddy/Caddyfile'
alias caddy-reload='docker compose -f ${caddy_dir}compose.yaml exec caddy /bin/sh -c  "caddy fmt --overwrite /etc/caddy/Caddyfile && caddy reload -c /etc/caddy/Caddyfile"'

source ~/.zshrc

编写 Caddyfile 文件

官网:V1 与 V2 区别

https://caddyserver.com/docs/v2-upgrade

caddy-edit

打印 log 日志

# 打印错误日志
{
        log {
                # Write logs to a file (with log rolling, which is enabled by default):
                output file /var/log/caddy/access.log
                level ERROR
        }
}

静态文件、后端负载均衡

linuxnbg.com {
        encode gzip
        root * /code/linuxnbg/dm-blog/public
        file_server

        reverse_proxy /api/* {
        to  localhost:9000 localhost:3000
        lb_policy round_robin
  }
}

端口监听 文件 并反向代理

:7777 {
        encode gzip
        root * /code/linuxnbg/dm-blog/public
        file_server
}

linuxnbg.com {
    reverse_proxy <host>:7000
}

重定向至 其他域名 例如百度

baidu.linuxnbg.com {
    redir https://www.baidu.com
}

将 linuxnbg.com 重定向至 www.linuxnbg.com

linuxnbg.com {
        redir https://www.linuxnbg.com
}

www.linuxnbg.com {
        root * /code/linuxnbg/dm-blog/public
        file_server
}

www.linuxnbg.com 重定向至 linuxnbg.com

www.linuxnbg.com {
        redir https://linuxnbg.com
}

linuxnbg.com {
        root * /code/linuxnbg/dm-blog/public
        file_server
}

负载均衡示例

linuxnbg.com {
        reverse_proxy  {
        to  localhost:9000 localhost:3000
        lb_policy round_robin
        }
}
# lb_policy 参数
first:选取第一个可用的上游
random:随机选取一个可用的上游
least_conn:选取当前请求数最少的上游,这个比较适合长连接的场景
ip_hash:根据IP的Hash值选取一个固定的上游
random_choose :随机选取2个或者更多个上游,然后再从中选择负载最小的,n通常为2
header:这个是根据请求头的Hash选取一个固定的上游,和 ip_hash 很像,只不过它是根据指定的请求头的值进行Hash,然后选取上游的。所以这里的用法是 header <request_header_name> ,要指定一个请求头。
uri_hash:这个也和 ip_hash 很像,只不过它是根据请求的URI进行Hash,然后选取一个上游。
round_robin:这个策略是循环迭代,挨个使用一个个上游,每个上游都可以被用到,轮着来。
cookie [ []] :如果你理解了以上几个基于Hash的负载均衡策略,那么这个 cookie 的也会很好理解,其实它就是通过cookie的值的hash来选取一个上游。在这里 name 表示要获取cookie值的 name ,默认是 lb , secret 是用于Hash的密钥,使用的是Hamc256算法

dnspod

  • 安装 dnspod

caddy list-modules|grep dnspod ; [ $? -eq 0 ] || (echo 不存在,正在安装 ; caddy add-package github.com/caddy-dns/dnspod ; systemctl restart caddy)

  • 使用 dnspod
# 定义
(dnspod) {
        tls {
                dns dnspod <,>
                # solvers mosca.dnspod.net # 腾讯云、dnspod 升级后、需要手动指定
                resolvers SHELF.DNSPOD.NET
        }
}

*.linuxnbg.com {
        import dnspod
        @docs host docs.linuxnbg.com
        handle @docs {
                reverse_proxy 10.0.16.12:8000
        }
        @gitea host gitea.linuxnbg.com
        handle @gitea {
                reverse_proxy 10.0.16.12:3000
        }
        @jenkins host jenkins.linuxnbg.com
        handle @jenkins {
                reverse_proxy 10.0.16.12:9000
        }
}

linuxnbg.com:90 {
    import dnspod
    handle {
        reverse_proxy 192.168.0.218:9999
    }
}

华为云 obs 静态建站

2020.linuxnbg.com {
        reverse_proxy linuxnbg.obs.cn-north-9.myhuaweicloud.com {
                header_up Host {upstream_hostport}
        }
        header -content-disposition # 去掉下载参数
        rewrite / /2020/index.html  
        uri replace /js/ /2020/js/
        uri replace /css/ /2020/css/
}

Caddy的简单认证

生成 hash-password 字符串

将生成的字符串贴在 Caddyfile 的 basicauth 块中。

caddy hash-password

Caddyfile 编写

one.example.com {
  basicauth / {
    user hashed-password
  }
  reverse_proxy service1:8001
}

two.example.com {
  basicauth / {
    user hashed-password
  }
  reverse_proxy service2:8002
}

three.example.com {
  basicauth / {
    user hashed-password
  }
  reverse_proxy service3:8003
}

Caddy 重用配置块

Caddy 还支持“snippets”,您可以在其他地方“导入”的可重用配置块,所以让我们稍微清理一下基本身份验证。这个和上面基本一样,但是现在用户和密码只在一个地方,更容易管理。

(basic-auth) {
  basicauth / {
    user hashed-password
  }
}

one.example.com {
  import basic-auth
  reverse_proxy service1:8001
}

two.example.com {
  import basic-auth
  reverse_proxy service2:8002
}

three.example.com {
  import basic-auth
  reverse_proxy service3:8003
}

因为我们仍然需要为每项服务分别进行身份验证。如果只有某种方式可以登录一次,并让 Caddy 为每项服务识别一次身份验证

(basic-auth) {
  basicauth / {
    user hashed-password
  }
}

# a snippet to check if a cookie token is set. if not, store the current page as the referer and redirect to auth site
(proxy-auth) {
  # if cookie not = some-token-nonsense
  @no-auth {
    not header_regexp mycookie Cookie myid=<regex-to-match-id>
    # https://github.com/caddyserver/caddy/issues/3916
  }

  # store current time, page and redirect to auth
  route @no-auth {
    header Set-Cookie "myreferer={scheme}://{host}{uri}; Domain=example.com; Path=/; Max-Age=30; HttpOnly; SameSite=Strict; Secure"
    redir https://auth.example.com
  }
}

# a pseudo site that only requires basic auth, sets cookie, and redirects back to original site
auth.example.com {
  route / {
    # require authentication
    import basic-auth

    # upon successful auth, set a client token
    header Set-Cookie "myid=some-long-hopefully-random-string; Domain=example.com; Path=/; Max-Age=3600; HttpOnly; SameSite=Strict; Secure"

    #delete the referer cookie
    header +Set-Cookie "myreferer=null; Domain=example.com; Path=/; Expires=Thu, 25 Sep 1971 12:00:00 GMT; HttpOnly; SameSite=Strict; Secure"

    # redirect back to the original site
    redir {http.request.cookie.myreferer}
  }

  # fallback
  respond "Hi."
}

one.example.com {
  import proxy-auth
  reverse_proxy service1:8001
}

two.example.com {
  import proxy-auth
  reverse_proxy service2:8002
}

three.example.com {
  import proxy-auth
  reverse_proxy service3:8003
}