更新经过多次调试,我发现
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+key
m.GetCertificate
acme_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
指导将不胜感激。