2

更新经过多次调试,我发现Get "https://acme-v02.api.letsencrypt.org/directory": x509: certificate signed by unknown authority并怀疑(!?)这是由于 Let's Encrypt 的根证书最近到期造成的。

我接受“这个包是一个正在进行中的工作,并且没有 API 稳定性承诺。” 但是,如果它不再起作用(而且我的代码|部署很可能存在问题),那么也许可以标记 repo,例如Here be dragons

该代码产生一个acme_account+key( EC PRIVATE KEY) 但没有证书我被挑战去autocert披露(记录)它的魔力,以便了解我哪里出错了。

该代码本质上是 repo 的Manager示例,其中包含来自此answer的输入。我假设ACME 流程完成时会GetCertificate阻塞。

代码:

package main

import (
    "crypto/tls"
    "flag"
    "fmt"
    "log"
    "net"
    "net/http"

    "golang.org/x/crypto/acme/autocert"

    "google.golang.org/grpc"
    "google.golang.org/grpc/credentials"
    "google.golang.org/grpc/health"
    healthpb "google.golang.org/grpc/health/grpc_health_v1"
)

const (
    email string = "my@email.com"

var (
    host = flag.String("host", "foo.example.org", "Fully-qualified domain name")
    port = flag.Uint("port", 443, "gRPC service port")
    path = flag.String("path", "", "Folder location for certificate")
)

func main() {
    flag.Parse()

    if *host == "" {
        log.Fatal("Flag --host is required")
    }
    log.Printf("Host: %s", *host)
    log.Printf("Port: %d", *port)

    if *path == "" {
        log.Fatal("Flag --path is required")
    }
    log.Printf("Path: %s", *path)

    addr := fmt.Sprintf(":%d", *port)

    lis, err := net.Listen("tcp", addr)
    if err != nil {
        log.Fatalf("failed to listen: %s", err)
    }

    m := &autocert.Manager{
        Cache:      autocert.DirCache(*path),
        Prompt:     autocert.AcceptTOS,
        HostPolicy: autocert.HostWhitelist(*host),
        Email:      email,
    }

    go func() {
        log.Println("Starting HTTP server w/ autocert handler")
        if err := http.ListenAndServe(":http", m.HTTPHandler(nil)); err != nil {
            log.Fatalf("HTTP failure\n%s", err)
        }
    }()

    tlsConfig := &tls.Config{
        ClientAuth: tls.RequireAndVerifyClientCert,
        GetCertificate: func(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
            cert, err := m.GetCertificate(hello)
            if err != nil {
                log.Fatalf("GetCertificate\n%s", err)
            }
            return cert, err
        },
    }

    opts := grpc.Creds(credentials.NewTLS(tlsConfig))
    server := grpc.NewServer(opts)

    healthcheck := health.NewServer()
    healthpb.RegisterHealthServer(server, healthcheck)

    log.Println("Starting gRPC server")
    if err := server.Serve(lis); err != nil {
        log.Fatalf("gRPC failure\n%s", err)
    }
}

我正在部署到(Google Compute Engine)容器虚拟机,等效的 docker 命令是:

docker run \
--name=autocert \
--detach \
--net=host \
--volume=/tmp/certs:/certs \
${IMAGE} \
--host=${HOST} \
--port=${PORT} \
--path=/certs

和容器日志:

2021/11/25 17:30:00 Host: [HOST]
2021/11/25 17:30:00 Port: 443
2021/11/25 17:30:00 Path: /certs
2021/11/25 17:30:00 Starting gRPC server
2021/11/25 17:30:00 Starting HTTP server

主机的/tmp/certs目录接收acme_account+key(我一直在努力寻找由 Google 解释的)但怀疑(!?)是Domain Validation的初始阶段。它包含一个私钥 ( BEGIN EC PRIVATE KEY)。

即使在服务器运行一段时间后,也不会保留更多文件。

我在配置的电子邮件地址没有收到来自 Let's Encrypt 的电子邮件。

不幸的是,虽然易于使用,但autocert产生的日志记录很少,我无法确定是否可以记录(希望)正在发生的 ACME 流。

由于不再创建为GetCertificate,添加匿名函数(我删除了以前的文件以检查它是否已重新创建),因此我无法从中收集任何日志记录,从未调用该函数。这可能是因为我的匿名函数不正确,或者因为我已经超出了对 ACME 端点的请求。删除该功能并恢复为不会导致重新创建,所以我很茫然。acme_account+keym.GetCertificateacme_account+key

autocert Manager类型记录了一个*acme.Client我没有设置的字段。该评论描述了“如果 Client.Key 为零,则会生成一个新的 ECDSA P-256 密钥”,这可能是我正在经历的,但它并没有解释我应该怎么做。我应该将此值设置为的内容acme_account+key吗?:

更新我尝试解码私钥,创建crypto.Signer并传递它,&acme.Client{Key: key}但它没有明显的区别

// Client is used to perform low-level operations, such as account registration
// and requesting new certificates.
//
// If Client is nil, a zero-value acme.Client is used with DefaultACMEDirectory
// as the directory endpoint.
// If the Client.Key is nil, a new ECDSA P-256 key is generated and,
// if Cache is not nil, stored in cache.
//
// Mutating the field after the first call of GetCertificate method will have no effect.
Client *acme.Client

显然,我使用不正确。我没有从 Let's Encrypt 收到证书,因此无法从端点获取证书,也无法调用 gRPC 端点:

openssl s_client -showcerts -connect ${HOST}:${PORT}

grpcurl \
-proto health.proto \
${HOST}:${PORT} \
grpc.health.v1.Health/Check
Failed to dial target host "${HOST}:${PORT}": remote error: tls: internal error

指导将不胜感激。

4

1 回答 1

0

‍♂️</p>

呃:-(

我已经开始使用SCRATCH并且没有复制 CA 证书

一旦容器拥有 CA 证书,一切工作几乎完美无缺。

我继续在尝试使用时遇到问题:

tlsConfig := &tls.Config{
  ClientAuth: tls.RequireAndVerifyClientCert,
  GetCertificate: m.GetCertificate
}

我正在使用m.TLSConfig()

所以,autocert工作就像一种享受(尽管很难调试自己造成的错误)

于 2021-11-26T21:32:20.287 回答