我正在尝试为指向简单 api 的域设置 SSL。我正在运行 .net 8,并将 Caddy 服务器设置为反向代理,所有设置均使用 docker compose 进行。我最初使用 certbot/letsencrypt 运行 nginx,但无法正常工作,因此切换到 Caddy,因为我听说它开箱即用。我以为我的问题是 Web 服务器,但我认为问题出在 API 或 kesterel 处理 SSL 上?
这是我的 Caddy 文件:
example.com {
redir /api /api/
handle_path /api/* {
reverse_proxy backend:8000
}
handle {
root * /usr/share/caddy
file_server
try_files {path} /index.html
}
log {
output stdout
}
}
我的码头工人撰写:
name: myapp
services:
backend:
container_name: mycontainer
image: myimage
ports:
- 8000:8080
caddy:
container_name: caddy
image: caddy
restart: always
ports:
- '80:80'
- '443:443'
volumes:
- caddy-config:/config
- caddy-data:/data
- ./Caddyfile:/etc/caddy/Caddyfile
- ./index.html:/usr/share/caddy/index.html
volumes:
caddy-config:
caddy-data:
这是我用 docker compose up 开始一切的时候:
[+] Running 3/3
✔ Network myapp_default Created 0.1s
✔ Container caddy Created 0.0s
✔ Container mycontainer Created 0.1s
Attaching to caddy, mycontainer
caddy | {"level":"info","ts":1725743811.7998998,"msg":"using config from file","file":"/etc/caddy/Caddyfile"}
caddy | {"level":"info","ts":1725743811.8028176,"msg":"adapted config to JSON","adapter":"caddyfile"}
caddy | {"level":"warn","ts":1725743811.803073,"msg":"Caddyfile input is not formatted; run 'caddy fmt --overwrite' to fix inconsistencies","adapter":"caddyfile","file":"/etc/caddy/Caddyfile","line":2}
caddy | {"level":"info","ts":1725743811.8087811,"logger":"admin","msg":"admin endpoint started","address":"localhost:2019","enforce_origin":false,"origins":["//localhost:2019","//[::1]:2019","//127.0.0.1:2019"]}
caddy | {"level":"info","ts":1725743811.8092213,"logger":"http.auto_https","msg":"server is listening only on the HTTPS port but has no TLS connection policies; adding one to enable TLS","server_name":"srv0","https_port":443}
caddy | {"level":"info","ts":1725743811.8094192,"logger":"http.auto_https","msg":"enabling automatic HTTP->HTTPS redirects","server_name":"srv0"}
caddy | {"level":"info","ts":1725743811.8110456,"logger":"http","msg":"enabling HTTP/3 listener","addr":":443"}
caddy | {"level":"info","ts":1725743811.811443,"msg":"failed to sufficiently increase receive buffer size (was: 208 kiB, wanted: 7168 kiB, got: 416 kiB). See https://github.com/quic-go/quic-go/wiki/UDP-Buffer-Sizes for details."}
caddy | {"level":"info","ts":1725743811.8148913,"logger":"http.log","msg":"server running","name":"srv0","protocols":["h1","h2","h3"]}
caddy | {"level":"info","ts":1725743811.815138,"logger":"http.log","msg":"server running","name":"remaining_auto_https_redirects","protocols":["h1","h2","h3"]}
caddy | {"level":"info","ts":1725743811.8152907,"logger":"http","msg":"enabling automatic TLS certificate management","domains":["example.com"]}
caddy | {"level":"info","ts":1725743811.8163688,"msg":"autosaved config (load with --resume flag)","file":"/config/caddy/autosave.json"}
caddy | {"level":"info","ts":1725743811.8165393,"msg":"serving initial configuration"}
caddy | {"level":"info","ts":1725743811.818198,"logger":"tls.cache.maintenance","msg":"started background certificate maintenance","cache":"0xc000942200"}
caddy | {"level":"info","ts":1725743811.8239324,"logger":"tls","msg":"cleaning storage unit","storage":"FileStorage:/data/caddy"}
caddy | {"level":"info","ts":1725743811.8250542,"logger":"tls","msg":"finished cleaning storage units"}
caddy | {"level":"info","ts":1725743812.0530305,"logger":"http.acme_client","msg":"got renewal info","names":["example.com"],"window_start":1730607560,"window_end":1730780360,"selected_time":1730768979,"recheck_after":1725765412.053012,"explanation_url":""}
caddy | {"level":"info","ts":1725743812.0540433,"logger":"tls","msg":"updated ACME renewal information","identifiers":["example.com"],"cert_hash":"5a638e82e1d8085182c5w44372dae49ac0c0425a171d3b9a7147df","ari_unique_id":"kydGmAOpUWiOmNbEQkjbI7I.BG3jz_njzgdss2X27mGXu","cert_expiry":1733284790,"selected_time":1730764641,"next_update":1725765412.053012,"explanation_url":""}
mycontainer| info: Microsoft.Hosting.Lifetime[14]
mycontainer| Now listening on: http://[::]:8080
mycontainer| info: Microsoft.Hosting.Lifetime[0]
mycontainer| Application started. Press Ctrl+C to shut down.
mycontainer| info: Microsoft.Hosting.Lifetime[0]
mycontainer| Hosting environment: Production
mycontainer| info: Microsoft.Hosting.Lifetime[0]
mycontainer| Content root path: /App
当我在非 https url 上使用 curl 访问我的域时,它起作用了:
curl -v -L http://example.com:8000/api/heartbeat
* Trying 2600:3c03::f03c:92ff:fe92:c4a9...
* TCP_NODELAY set
* Expire in 149994 ms for 3 (transfer 0x558166564f50)
* Expire in 200 ms for 4 (transfer 0x558166564f50)
* Connected to example.com (2600:3c03::f03c:92ff:fe92:c4a9) port 8000 (#0)
> GET /api/heartbeat HTTP/1.1
> Host: example.com.com:8000
> User-Agent: curl/7.64.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Type: text/plain; charset=utf-8
< Date: Sat, 07 Sep 2024 21:48:29 GMT
< Server: Kestrel
< Transfer-Encoding: chunked
<
* Connection #0 to host example.com left intact
hello
当我尝试 https 时,它没有:
curl -v -L https://example.com:8000/api/heartbeat
* TCP_NODELAY set
* Expire in 149998 ms for 3 (transfer 0x55ee5dca7f50)
* Expire in 200 ms for 4 (transfer 0x55ee5dca7f50)
* Connected to example.com port 8000 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: none
CApath: /etc/ssl/certs
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* error:1408F10B:SSL routines:ssl3_get_record:wrong version number
* Closing connection 0
curl: (35) error:1408F10B:SSL routines:ssl3_get_record:wrong version number
当我尝试在 https 上不使用端口号时:
curl -v -L https://example.com/api/heartbeat
TCP_NODELAY set
* Expire in 149992 ms for 3 (transfer 0x563d5ed06f50)
* Expire in 200 ms for 4 (transfer 0x563d5ed06f50)
* Connected to example.com (2600:3c03::f03c:92ff:fe92:c4a9) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: none
CApath: /etc/ssl/certs
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_128_GCM_SHA256
* ALPN, server accepted to use h2
* Server certificate:
* subject: CN=example.com
* start date: Sep 5 03:59:51 2024 GMT
* expire date: Dec 4 03:59:50 2024 GMT
* subjectAltName: host "example.com" matched cert's "example.com"
* issuer: C=US; O=Let's Encrypt; CN=E6
* SSL certificate verify ok.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x563d5ed06f50)
> GET /api/heartbeat HTTP/2
> Host: example.com
> User-Agent: curl/7.64.0
> Accept: */*
>
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* Connection state changed (MAX_CONCURRENT_STREAMS == 250)!
< HTTP/2 502
< alt-svc: h3=":443"; ma=2592000
< server: Caddy
< content-length: 0
< date: Sat, 07 Sep 2024 21:51:12 GMT
<
* Connection #0 to host example.com left intact
球童记录了这一点:
caddy | {"level":"error","ts":1725745872.6222372,"logger":"http.log.error.log0","msg":"dial tcp 172.27.0.2:8000: connect: connection refused","request":{"remote_ip":"172.27.0.1","remote_port":"33794","client_ip":"172.27.0.1","proto":"HTTP/2.0","method":"GET","host":"example.com","uri":"/api/heartbeat","headers":{"User-Agent":["curl/7.64.0"],"Accept":["*/*"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","server_name":"example.com"}},"duration":0.00286854,"status":502,"err_id":"psdgnenq6","err_trace":"reverseproxy.statusError (reverseproxy.go:1269)"}
caddy | {"level":"error","ts":1725745872.6229415,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.27.0.1","remote_port":"33794","client_ip":"172.27.0.1","proto":"HTTP/2.0","method":"GET","host":"example.com","uri":"/api/heartbeat","headers":{"User-Agent":["curl/7.64.0"],"Accept":["*/*"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","server_name":"example.com"}},"bytes_read":0,"user_id":"","duration":0.00286854,"size":0,"status":502,"resp_headers":{"Server":["Caddy"],"Alt-Svc":["h3=\":443\"; ma=2592000"]}}
在.NET 中,我尝试了一些方法。我尝试使用 UseHttpsRedirection 而不使用它,并在我的 appsettings 文件中设置 https 端口而不设置它。目前,我从应用程序设置中删除了 https 端口的设置,这就是我的代码的样子:
builder.Services.AddControllers();
builder.Services.AddCors(options => options.AddPolicy("CorsPolicy",
builder =>
{
builder.AllowAnyMethod()
.AllowAnyHeader()
.AllowAnyOrigin();
}));
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
// app.UseForwardedHeaders();
}
else
{
app.UseExceptionHandler("/Error");
// app.UseForwardedHeaders();
// app.UseHsts();
}
app.UseCors("CorsPolicy");
//app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
[Route("api/[controller]")]
[ApiController]
public class HeartBeatController : ControllerBase
{
[HttpGet]
public IActionResult Get()
{
return Ok("hello");
}
}
所以我已经尝试解决这个问题大约一周了,但无法找出问题所在。有什么突出的事情或关于如何进一步调试的建议吗?
看起来您正在尝试使用 http 和 https 访问同一端口 8000 上的同一服务器。这是行不通的,而且也完全绕过了 Caddy。您收到的错误“版本号错误”是由于您尝试使用 HTTPS 访问纯 HTTP 服务器,而纯 HTTP 响应被错误地解释为 HTTPS。
当您使用默认端口 443 的 HTTPS(即您所说的“无端口”)时,可以看出 Caddy 与 HTTPS 配合使用,其中 Caddy 设置正确并且 SSL 握手明显成功。 Caddy 产生的错误 502 是因为 Caddy 无法访问您的服务器。看起来您已将 172.27.0.2:8000 设置为此处的目标,这似乎不是您的服务器运行的 IP + 端口 - 因此“连接被拒绝”。不幸的是,从您的帖子中不清楚您的内部后端服务器真正在哪里运行,但也许它是 127.0.0.1:8000 而不是 172.27.0.2:8000?