2023-10-25 01:59:53 +08:00
|
|
|
package integration
|
|
|
|
|
|
|
|
import (
|
2024-02-24 07:26:00 +08:00
|
|
|
"context"
|
|
|
|
"crypto/ecdsa"
|
|
|
|
"crypto/elliptic"
|
|
|
|
"crypto/rand"
|
|
|
|
"strings"
|
2023-10-25 01:59:53 +08:00
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/caddyserver/caddy/v2/caddytest"
|
2024-04-14 09:31:43 +08:00
|
|
|
"github.com/mholt/acmez/v2"
|
|
|
|
"github.com/mholt/acmez/v2/acme"
|
2024-02-24 07:26:00 +08:00
|
|
|
"go.uber.org/zap"
|
2023-10-25 01:59:53 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
func TestACMEServerDirectory(t *testing.T) {
|
|
|
|
tester := caddytest.NewTester(t)
|
|
|
|
tester.InitServer(`
|
|
|
|
{
|
|
|
|
skip_install_trust
|
|
|
|
local_certs
|
|
|
|
admin localhost:2999
|
|
|
|
http_port 9080
|
|
|
|
https_port 9443
|
|
|
|
pki {
|
|
|
|
ca local {
|
|
|
|
name "Caddy Local Authority"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
acme.localhost:9443 {
|
|
|
|
acme_server
|
|
|
|
}
|
|
|
|
`, "caddyfile")
|
|
|
|
tester.AssertGetResponse(
|
|
|
|
"https://acme.localhost:9443/acme/local/directory",
|
|
|
|
200,
|
|
|
|
`{"newNonce":"https://acme.localhost:9443/acme/local/new-nonce","newAccount":"https://acme.localhost:9443/acme/local/new-account","newOrder":"https://acme.localhost:9443/acme/local/new-order","revokeCert":"https://acme.localhost:9443/acme/local/revoke-cert","keyChange":"https://acme.localhost:9443/acme/local/key-change"}
|
|
|
|
`)
|
|
|
|
}
|
2024-02-24 07:26:00 +08:00
|
|
|
|
|
|
|
func TestACMEServerAllowPolicy(t *testing.T) {
|
|
|
|
tester := caddytest.NewTester(t)
|
|
|
|
tester.InitServer(`
|
|
|
|
{
|
|
|
|
skip_install_trust
|
|
|
|
local_certs
|
|
|
|
admin localhost:2999
|
|
|
|
http_port 9080
|
|
|
|
https_port 9443
|
|
|
|
pki {
|
|
|
|
ca local {
|
|
|
|
name "Caddy Local Authority"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
acme.localhost {
|
|
|
|
acme_server {
|
|
|
|
challenges http-01
|
|
|
|
allow {
|
|
|
|
domains localhost
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`, "caddyfile")
|
|
|
|
|
|
|
|
ctx := context.Background()
|
|
|
|
logger, err := zap.NewDevelopment()
|
|
|
|
if err != nil {
|
|
|
|
t.Error(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
client := acmez.Client{
|
|
|
|
Client: &acme.Client{
|
|
|
|
Directory: "https://acme.localhost:9443/acme/local/directory",
|
|
|
|
HTTPClient: tester.Client,
|
|
|
|
Logger: logger,
|
|
|
|
},
|
|
|
|
ChallengeSolvers: map[string]acmez.Solver{
|
|
|
|
acme.ChallengeTypeHTTP01: &naiveHTTPSolver{logger: logger},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
accountPrivateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("generating account key: %v", err)
|
|
|
|
}
|
|
|
|
account := acme.Account{
|
|
|
|
Contact: []string{"mailto:you@example.com"},
|
|
|
|
TermsOfServiceAgreed: true,
|
|
|
|
PrivateKey: accountPrivateKey,
|
|
|
|
}
|
|
|
|
account, err = client.NewAccount(ctx, account)
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("new account: %v", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Every certificate needs a key.
|
|
|
|
certPrivateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("generating certificate key: %v", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
{
|
2024-04-14 09:31:43 +08:00
|
|
|
certs, err := client.ObtainCertificateForSANs(ctx, account, certPrivateKey, []string{"localhost"})
|
2024-02-24 07:26:00 +08:00
|
|
|
if err != nil {
|
|
|
|
t.Errorf("obtaining certificate for allowed domain: %v", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// ACME servers should usually give you the entire certificate chain
|
|
|
|
// in PEM format, and sometimes even alternate chains! It's up to you
|
|
|
|
// which one(s) to store and use, but whatever you do, be sure to
|
|
|
|
// store the certificate and key somewhere safe and secure, i.e. don't
|
|
|
|
// lose them!
|
|
|
|
for _, cert := range certs {
|
|
|
|
t.Logf("Certificate %q:\n%s\n\n", cert.URL, cert.ChainPEM)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
{
|
2024-04-14 09:31:43 +08:00
|
|
|
_, err := client.ObtainCertificateForSANs(ctx, account, certPrivateKey, []string{"not-matching.localhost"})
|
2024-02-24 07:26:00 +08:00
|
|
|
if err == nil {
|
|
|
|
t.Errorf("obtaining certificate for 'not-matching.localhost' domain")
|
|
|
|
} else if err != nil && !strings.Contains(err.Error(), "urn:ietf:params:acme:error:rejectedIdentifier") {
|
|
|
|
t.Logf("unexpected error: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestACMEServerDenyPolicy(t *testing.T) {
|
|
|
|
tester := caddytest.NewTester(t)
|
|
|
|
tester.InitServer(`
|
|
|
|
{
|
|
|
|
skip_install_trust
|
|
|
|
local_certs
|
|
|
|
admin localhost:2999
|
|
|
|
http_port 9080
|
|
|
|
https_port 9443
|
|
|
|
pki {
|
|
|
|
ca local {
|
|
|
|
name "Caddy Local Authority"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
acme.localhost {
|
|
|
|
acme_server {
|
|
|
|
deny {
|
|
|
|
domains deny.localhost
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`, "caddyfile")
|
|
|
|
|
|
|
|
ctx := context.Background()
|
|
|
|
logger, err := zap.NewDevelopment()
|
|
|
|
if err != nil {
|
|
|
|
t.Error(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
client := acmez.Client{
|
|
|
|
Client: &acme.Client{
|
|
|
|
Directory: "https://acme.localhost:9443/acme/local/directory",
|
|
|
|
HTTPClient: tester.Client,
|
|
|
|
Logger: logger,
|
|
|
|
},
|
|
|
|
ChallengeSolvers: map[string]acmez.Solver{
|
|
|
|
acme.ChallengeTypeHTTP01: &naiveHTTPSolver{logger: logger},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
accountPrivateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("generating account key: %v", err)
|
|
|
|
}
|
|
|
|
account := acme.Account{
|
|
|
|
Contact: []string{"mailto:you@example.com"},
|
|
|
|
TermsOfServiceAgreed: true,
|
|
|
|
PrivateKey: accountPrivateKey,
|
|
|
|
}
|
|
|
|
account, err = client.NewAccount(ctx, account)
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("new account: %v", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Every certificate needs a key.
|
|
|
|
certPrivateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("generating certificate key: %v", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
{
|
2024-04-14 09:31:43 +08:00
|
|
|
_, err := client.ObtainCertificateForSANs(ctx, account, certPrivateKey, []string{"deny.localhost"})
|
2024-02-24 07:26:00 +08:00
|
|
|
if err == nil {
|
|
|
|
t.Errorf("obtaining certificate for 'deny.localhost' domain")
|
|
|
|
} else if err != nil && !strings.Contains(err.Error(), "urn:ietf:params:acme:error:rejectedIdentifier") {
|
|
|
|
t.Logf("unexpected error: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|