forked from TrueCloudLab/certificates
Add support for custom acme ports
This change adds the flags --acme-http-port, --acme-tls-port, that combined with --insecure can be used to set custom ports for ACME http-01 and tls-alpn-01 challenges. These flags should only be used for testing purposes. Fixes #1015
This commit is contained in:
parent
9d90d0cef3
commit
e27c6c529b
3 changed files with 171 additions and 2 deletions
|
@ -44,6 +44,18 @@ const (
|
|||
DEVICEATTEST01 ChallengeType = "device-attest-01"
|
||||
)
|
||||
|
||||
var (
|
||||
// InsecurePortHTTP01 is the port used to verify http-01 challenges. If not set it
|
||||
// defaults to 80.
|
||||
InsecurePortHTTP01 int
|
||||
|
||||
// InsecurePortTLSALPN01 is the port used to verify tls-alpn-01 challenges. If not
|
||||
// set it defaults to 443.
|
||||
//
|
||||
// This variable can be used for testing purposes.
|
||||
InsecurePortTLSALPN01 int
|
||||
)
|
||||
|
||||
// Challenge represents an ACME response Challenge type.
|
||||
type Challenge struct {
|
||||
ID string `json:"-"`
|
||||
|
@ -93,6 +105,12 @@ func (ch *Challenge) Validate(ctx context.Context, db DB, jwk *jose.JSONWebKey,
|
|||
func http01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWebKey) error {
|
||||
u := &url.URL{Scheme: "http", Host: http01ChallengeHost(ch.Value), Path: fmt.Sprintf("/.well-known/acme-challenge/%s", ch.Token)}
|
||||
|
||||
// Append insecure port if set.
|
||||
// Only used for testing purposes.
|
||||
if InsecurePortHTTP01 != 0 {
|
||||
u.Host += ":" + strconv.Itoa(InsecurePortHTTP01)
|
||||
}
|
||||
|
||||
vc := MustClientFromContext(ctx)
|
||||
resp, err := vc.Get(u.String())
|
||||
if err != nil {
|
||||
|
@ -165,7 +183,14 @@ func tlsalpn01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSON
|
|||
InsecureSkipVerify: true, //nolint:gosec // we expect a self-signed challenge certificate
|
||||
}
|
||||
|
||||
hostPort := net.JoinHostPort(ch.Value, "443")
|
||||
var hostPort string
|
||||
|
||||
// Allow to change TLS port for testing purposes.
|
||||
if port := InsecurePortTLSALPN01; port == 0 {
|
||||
hostPort = net.JoinHostPort(ch.Value, "443")
|
||||
} else {
|
||||
hostPort = net.JoinHostPort(ch.Value, strconv.Itoa(port))
|
||||
}
|
||||
|
||||
vc := MustClientFromContext(ctx)
|
||||
conn, err := vc.TLSDial("tcp", hostPort, config)
|
||||
|
|
|
@ -24,6 +24,7 @@ import (
|
|||
"net/http"
|
||||
"net/http/httptest"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
@ -370,6 +371,47 @@ func TestChallenge_Validate(t *testing.T) {
|
|||
},
|
||||
}
|
||||
},
|
||||
"ok/http-01-insecure": func(t *testing.T) test {
|
||||
t.Cleanup(func() {
|
||||
InsecurePortHTTP01 = 0
|
||||
})
|
||||
|
||||
ch := &Challenge{
|
||||
ID: "chID",
|
||||
Status: StatusPending,
|
||||
Type: "http-01",
|
||||
Token: "token",
|
||||
Value: "zap.internal",
|
||||
}
|
||||
|
||||
InsecurePortHTTP01 = 8080
|
||||
|
||||
return test{
|
||||
ch: ch,
|
||||
vc: &mockClient{
|
||||
get: func(url string) (*http.Response, error) {
|
||||
return nil, errors.New("force")
|
||||
},
|
||||
},
|
||||
db: &MockDB{
|
||||
MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error {
|
||||
assert.Equals(t, updch.ID, ch.ID)
|
||||
assert.Equals(t, updch.Token, ch.Token)
|
||||
assert.Equals(t, updch.Type, ch.Type)
|
||||
assert.Equals(t, updch.Status, ch.Status)
|
||||
assert.Equals(t, updch.Value, ch.Value)
|
||||
|
||||
err := NewError(ErrorConnectionType, "error doing http GET for url http://zap.internal:8080/.well-known/acme-challenge/%s: force", ch.Token)
|
||||
assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error())
|
||||
assert.Equals(t, updch.Error.Type, err.Type)
|
||||
assert.Equals(t, updch.Error.Detail, err.Detail)
|
||||
assert.Equals(t, updch.Error.Status, err.Status)
|
||||
assert.Equals(t, updch.Error.Detail, err.Detail)
|
||||
return nil
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
"fail/dns-01": func(t *testing.T) test {
|
||||
ch := &Challenge{
|
||||
ID: "chID",
|
||||
|
@ -501,6 +543,72 @@ func TestChallenge_Validate(t *testing.T) {
|
|||
srv, tlsDial := newTestTLSALPNServer(cert)
|
||||
srv.Start()
|
||||
|
||||
return test{
|
||||
ch: ch,
|
||||
vc: &mockClient{
|
||||
tlsDial: tlsDial,
|
||||
},
|
||||
db: &MockDB{
|
||||
MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error {
|
||||
assert.Equals(t, updch.ID, ch.ID)
|
||||
assert.Equals(t, updch.Token, ch.Token)
|
||||
assert.Equals(t, updch.Status, ch.Status)
|
||||
assert.Equals(t, updch.Type, ch.Type)
|
||||
assert.Equals(t, updch.Value, ch.Value)
|
||||
assert.Equals(t, updch.Error, nil)
|
||||
return nil
|
||||
},
|
||||
},
|
||||
srv: srv,
|
||||
jwk: jwk,
|
||||
}
|
||||
},
|
||||
"ok/tls-alpn-01-insecure": func(t *testing.T) test {
|
||||
t.Cleanup(func() {
|
||||
InsecurePortTLSALPN01 = 0
|
||||
})
|
||||
|
||||
ch := &Challenge{
|
||||
ID: "chID",
|
||||
Token: "token",
|
||||
Type: "tls-alpn-01",
|
||||
Status: StatusPending,
|
||||
Value: "zap.internal",
|
||||
}
|
||||
|
||||
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
|
||||
assert.FatalError(t, err)
|
||||
|
||||
expKeyAuth, err := KeyAuthorization(ch.Token, jwk)
|
||||
assert.FatalError(t, err)
|
||||
expKeyAuthHash := sha256.Sum256([]byte(expKeyAuth))
|
||||
|
||||
cert, err := newTLSALPNValidationCert(expKeyAuthHash[:], false, true, ch.Value)
|
||||
assert.FatalError(t, err)
|
||||
|
||||
l, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
if l, err = net.Listen("tcp6", "[::1]:0"); err != nil {
|
||||
t.Fatalf("failed to listen on a port: %v", err)
|
||||
}
|
||||
}
|
||||
_, port, err := net.SplitHostPort(l.Addr().String())
|
||||
if err != nil {
|
||||
t.Fatalf("failed to split host port: %v", err)
|
||||
}
|
||||
|
||||
// Use an insecure port
|
||||
InsecurePortTLSALPN01, err = strconv.Atoi(port)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to convert port to int: %v", err)
|
||||
}
|
||||
|
||||
srv, tlsDial := newTestTLSALPNServer(cert, func(srv *httptest.Server) {
|
||||
srv.Listener.Close()
|
||||
srv.Listener = l
|
||||
})
|
||||
srv.Start()
|
||||
|
||||
return test{
|
||||
ch: ch,
|
||||
vc: &mockClient{
|
||||
|
@ -1248,7 +1356,7 @@ func TestDNS01Validate(t *testing.T) {
|
|||
|
||||
type tlsDialer func(network, addr string, config *tls.Config) (conn *tls.Conn, err error)
|
||||
|
||||
func newTestTLSALPNServer(validationCert *tls.Certificate) (*httptest.Server, tlsDialer) {
|
||||
func newTestTLSALPNServer(validationCert *tls.Certificate, opts ...func(*httptest.Server)) (*httptest.Server, tlsDialer) {
|
||||
srv := httptest.NewUnstartedServer(http.NewServeMux())
|
||||
|
||||
srv.Config.TLSNextProto = map[string]func(*http.Server, *tls.Conn, http.Handler){
|
||||
|
@ -1273,6 +1381,11 @@ func newTestTLSALPNServer(validationCert *tls.Certificate) (*httptest.Server, tl
|
|||
},
|
||||
}
|
||||
|
||||
// Apply options
|
||||
for _, fn := range opts {
|
||||
fn(srv)
|
||||
}
|
||||
|
||||
srv.Listener = tls.NewListener(srv.Listener, srv.TLS)
|
||||
//srv.Config.ErrorLog = log.New(ioutil.Discard, "", 0) // hush
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"unicode"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/smallstep/certificates/acme"
|
||||
"github.com/smallstep/certificates/authority/config"
|
||||
"github.com/smallstep/certificates/authority/provisioner"
|
||||
"github.com/smallstep/certificates/ca"
|
||||
|
@ -71,6 +72,19 @@ certificate issuer private key used in the RA mode.`,
|
|||
Usage: "The name of the authority's context.",
|
||||
EnvVar: "STEP_CA_CONTEXT",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "acme-http-port",
|
||||
Usage: `The port used on http-01 challenges. It can be changed for testing purposes.
|
||||
Requires **--insecure** flag.`,
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "acme-tls-port",
|
||||
Usage: `The port used on tls-alpn-01 challenges. It can be changed for testing purposes.
|
||||
Requires **--insecure** flag.`,
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "insecure",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -88,6 +102,23 @@ func appAction(ctx *cli.Context) error {
|
|||
return errs.TooManyArguments(ctx)
|
||||
}
|
||||
|
||||
// Allow custom ACME ports with insecure
|
||||
if acmePort := ctx.Int("acme-http-port"); acmePort != 0 {
|
||||
if ctx.Bool("insecure") {
|
||||
acme.InsecurePortHTTP01 = acmePort
|
||||
} else {
|
||||
return fmt.Errorf("flag '--acme-http-port' requires the '--insecure' flag")
|
||||
}
|
||||
}
|
||||
if acmePort := ctx.Int("acme-tls-port"); acmePort != 0 {
|
||||
if ctx.Bool("insecure") {
|
||||
acme.InsecurePortTLSALPN01 = acmePort
|
||||
} else {
|
||||
return fmt.Errorf("flag '--acme-tls-port' requires the '--insecure' flag")
|
||||
}
|
||||
}
|
||||
|
||||
// Allow custom contexts.
|
||||
if caCtx := ctx.String("context"); caCtx != "" {
|
||||
if err := step.Contexts().SetCurrent(caCtx); err != nil {
|
||||
return err
|
||||
|
|
Loading…
Reference in a new issue