mirror of
https://github.com/go-acme/lego.git
synced 2024-12-23 01:07:23 +02:00
New challenges management. (#741)
This commit is contained in:
parent
9979087572
commit
43401f2475
@ -38,7 +38,6 @@
|
||||
exclude = [
|
||||
"Error return value of (.+) is not checked",
|
||||
"exported (type|method|function) (.+) should have comment or be unexported",
|
||||
"possible misuse of unsafe.Pointer",
|
||||
"cyclomatic complexity (.+) of func `NewDNSChallengeProviderByName` is high (.+)", # providers/dns/dns_providers.go
|
||||
"string `(lego\\.wtf|manhattan)` has (\\d+) occurrences, make it a constant", #providers/dns/gcloud/googlecloud_test.go
|
||||
|
||||
|
85
README.md
85
README.md
@ -12,8 +12,8 @@ Let's Encrypt client and ACME library written in Go
|
||||
|
||||
### Binaries
|
||||
|
||||
To get the binary just download the latest release for your OS/Arch from [the release page](https://github.com/xenolf/lego/releases)
|
||||
and put the binary somewhere convenient. lego does not assume anything about the location you run it from.
|
||||
To get the binary just download the latest release for your OS/Arch from [the release page](https://github.com/xenolf/lego/releases) and put the binary somewhere convenient.
|
||||
lego does not assume anything about the location you run it from.
|
||||
|
||||
### From Docker
|
||||
|
||||
@ -55,7 +55,7 @@ go get -u github.com/xenolf/lego/cmd/lego
|
||||
|
||||
Please keep in mind that CLI switches and APIs are still subject to change.
|
||||
|
||||
When using the standard `--path` option, all certificates and account configurations are saved to a folder *.lego* in the current working directory.
|
||||
When using the standard `--path` option, all certificates and account configurations are saved to a folder `.lego` in the current working directory.
|
||||
|
||||
## Usage
|
||||
|
||||
@ -75,30 +75,31 @@ COMMANDS:
|
||||
help, h Shows a list of commands or help for one command
|
||||
|
||||
GLOBAL OPTIONS:
|
||||
--domains value, -d value Add a domain to the process. Can be specified multiple times.
|
||||
--server value, -s value CA hostname (and optionally :port). The server certificate must be trusted in order to avoid further modifications to the client. (default: "https://acme-v02.api.letsencrypt.org/directory")
|
||||
--accept-tos, -a By setting this flag to true you indicate that you accept the current Let's Encrypt terms of service.
|
||||
--email value, -m value Email used for registration and recovery contact.
|
||||
--csr value, -c value Certificate signing request filename, if an external CSR is to be used
|
||||
--eab Use External Account Binding for account registration. Requires --kid and --hmac.
|
||||
--kid value Key identifier from External CA. Used for External Account Binding.
|
||||
--hmac value MAC key from External CA. Should be in Base64 URL Encoding without padding format. Used for External Account Binding.
|
||||
--key-type value, -k value Key type to use for private keys. Supported: rsa2048, rsa4096, rsa8192, ec256, ec384 (default: "rsa2048")
|
||||
--filename value Filename of the generated certificate
|
||||
--path value Directory to use for storing the data (default: "./.lego")
|
||||
--exclude value, -x value Explicitly disallow solvers by name from being used. Solvers: "http-01", "dns-01", "tls-alpn-01".
|
||||
--http-timeout value Set the HTTP timeout value to a specific value in seconds. The default is 10 seconds. (default: 0)
|
||||
--webroot value Set the webroot folder to use for HTTP based challenges to write directly in a file in .well-known/acme-challenge
|
||||
--memcached-host value Set the memcached host(s) to use for HTTP based challenges. Challenges will be written to all specified hosts.
|
||||
--http value Set the port and interface to use for HTTP based challenges to listen on. Supported: interface:port or :port
|
||||
--tls value Set the port and interface to use for TLS based challenges to listen on. Supported: interface:port or :port
|
||||
--dns value Solve a DNS challenge using the specified provider. Disables all other challenges. Run 'lego dnshelp' for help on usage.
|
||||
--dns-disable-cp By setting this flag to true, disables the need to wait the propagation of the TXT record to all authoritative name servers.
|
||||
--dns-resolvers value Set the resolvers to use for performing recursive DNS queries. Supported: host:port. The default is to use the system resolvers, or Google's DNS resolvers if the system's cannot be determined.
|
||||
--dns-timeout value Set the DNS timeout value to a specific value in seconds. Used only when performing authoritative name servers queries. The default is 10 seconds. (default: 0)
|
||||
--pem Generate a .pem file by concatenating the .key and .crt files together.
|
||||
--help, -h show help
|
||||
--version, -v print the version
|
||||
--domains value, -d value Add a domain to the process. Can be specified multiple times.
|
||||
--server value, -s value CA hostname (and optionally :port). The server certificate must be trusted in order to avoid further modifications to the client. (default: "https://acme-v02.api.letsencrypt.org/directory")
|
||||
--accept-tos, -a By setting this flag to true you indicate that you accept the current Let's Encrypt terms of service.
|
||||
--email value, -m value Email used for registration and recovery contact.
|
||||
--csr value, -c value Certificate signing request filename, if an external CSR is to be used.
|
||||
--eab Use External Account Binding for account registration. Requires --kid and --hmac.
|
||||
--kid value Key identifier from External CA. Used for External Account Binding.
|
||||
--hmac value MAC key from External CA. Should be in Base64 URL Encoding without padding format. Used for External Account Binding.
|
||||
--key-type value, -k value Key type to use for private keys. Supported: rsa2048, rsa4096, rsa8192, ec256, ec384. (default: "rsa2048")
|
||||
--filename value (deprecated) Filename of the generated certificate.
|
||||
--path value Directory to use for storing the data. (default: "./.lego")
|
||||
--http Use the HTTP challenge to solve challenges. Can be mixed with other types of challenges.
|
||||
--http.port value Set the port and interface to use for HTTP based challenges to listen on.Supported: interface:port or :port. (default: ":80")
|
||||
--http.webroot value Set the webroot folder to use for HTTP based challenges to write directly in a file in .well-known/acme-challenge.
|
||||
--http.memcached-host value Set the memcached host(s) to use for HTTP based challenges. Challenges will be written to all specified hosts.
|
||||
--tls Use the TLS challenge to solve challenges. Can be mixed with other types of challenges.
|
||||
--tls.port value Set the port and interface to use for TLS based challenges to listen on. Supported: interface:port or :port. (default: ":443")
|
||||
--dns value Solve a DNS challenge using the specified provider. Can be mixed with other types of challenges. Run 'lego dnshelp' for help on usage.
|
||||
--dns.disable-cp By setting this flag to true, disables the need to wait the propagation of the TXT record to all authoritative name servers.
|
||||
--dns.resolvers value Set the resolvers to use for performing recursive DNS queries. Supported: host:port. The default is to use the system resolvers, or Google's DNS resolvers if the system's cannot be determined.
|
||||
--http-timeout value Set the HTTP timeout value to a specific value in seconds. (default: 0)
|
||||
--dns-timeout value Set the DNS timeout value to a specific value in seconds. Used only when performing authoritative name servers queries. (default: 10)
|
||||
--pem Generate a .pem file by concatenating the .key and .crt files together.
|
||||
--help, -h show help
|
||||
--version, -v print the version
|
||||
```
|
||||
|
||||
### Sudo
|
||||
@ -107,14 +108,14 @@ The CLI does not require root permissions but needs to bind to port 80 and 443 f
|
||||
To run the CLI without sudo, you have four options:
|
||||
|
||||
- Use setcap 'cap_net_bind_service=+ep' /path/to/program
|
||||
- Pass the `--http` or/and the `--tls` option and specify a custom port to bind to. In this case you have to forward port 80/443 to these custom ports (see [Port Usage](#port-usage)).
|
||||
- Pass the `--webroot` option and specify the path to your webroot folder. In this case the challenge will be written in a file in `.well-known/acme-challenge/` inside your webroot.
|
||||
- Pass the `--http.port` or/and the `--tls.port` option and specify a custom port to bind to. In this case you have to forward port 80/443 to these custom ports (see [Port Usage](#port-usage)).
|
||||
- Pass the `--http.webroot` option and specify the path to your webroot folder. In this case the challenge will be written in a file in `.well-known/acme-challenge/` inside your webroot.
|
||||
- Pass the `--dns` option and specify a DNS provider.
|
||||
|
||||
### Port Usage
|
||||
|
||||
By default lego assumes it is able to bind to ports 80 and 443 to solve challenges.
|
||||
If this is not possible in your environment, you can use the `--http` and `--tls` options to instruct
|
||||
If this is not possible in your environment, you can use the `--http.port` and `--tls.port` options to instruct
|
||||
lego to listen on that interface:port for any incoming challenges.
|
||||
|
||||
If you are using this option, make sure you proxy all of the following traffic to these ports.
|
||||
@ -131,13 +132,14 @@ This traffic redirection is only needed as long as lego solves challenges. As so
|
||||
|
||||
### CLI Example
|
||||
|
||||
Assumes the `lego` binary has permission to bind to ports 80 and 443. You can get a pre-built binary from the [releases](https://github.com/xenolf/lego/releases) page.
|
||||
Assumes the `lego` binary has permission to bind to ports 80 and 443.
|
||||
You can get a pre-built binary from the [releases](https://github.com/xenolf/lego/releases) page.
|
||||
If your environment does not allow you to bind to these ports, please read [Port Usage](#port-usage).
|
||||
|
||||
Obtain a certificate:
|
||||
|
||||
```bash
|
||||
lego --email="foo@bar.com" --domains="example.com" run
|
||||
lego --email="foo@bar.com" --domains="example.com" --http run
|
||||
```
|
||||
|
||||
(Find your certificate in the `.lego` folder of current working directory.)
|
||||
@ -145,13 +147,13 @@ lego --email="foo@bar.com" --domains="example.com" run
|
||||
To renew the certificate:
|
||||
|
||||
```bash
|
||||
lego --email="foo@bar.com" --domains="example.com" renew
|
||||
lego --email="foo@bar.com" --domains="example.com" --http renew
|
||||
```
|
||||
|
||||
To renew the certificate only if it expires within 30 days
|
||||
|
||||
```bash
|
||||
lego --email="foo@bar.com" --domains="example.com" renew --days 30
|
||||
lego --email="foo@bar.com" --domains="example.com" --http renew --days 30
|
||||
```
|
||||
|
||||
Obtain a certificate using the DNS challenge and AWS Route 53:
|
||||
@ -160,17 +162,16 @@ Obtain a certificate using the DNS challenge and AWS Route 53:
|
||||
AWS_REGION=us-east-1 AWS_ACCESS_KEY_ID=my_id AWS_SECRET_ACCESS_KEY=my_key lego --email="foo@bar.com" --domains="example.com" --dns="route53" run
|
||||
```
|
||||
|
||||
Note that `--dns=foo` implies `--exclude=http-01`. lego will not attempt other challenges if you've told it to use DNS instead.
|
||||
|
||||
Obtain a certificate given a certificate signing request (CSR) generated by something else:
|
||||
|
||||
```bash
|
||||
lego --email="foo@bar.com" --csr=/path/to/csr.pem run
|
||||
lego --email="foo@bar.com" --http --csr=/path/to/csr.pem run
|
||||
```
|
||||
|
||||
(lego will infer the domains to be validated based on the contents of the CSR, so make sure the CSR's Common Name and optional SubjectAltNames are set correctly.)
|
||||
|
||||
lego defaults to communicating with the production Let's Encrypt ACME server. If you'd like to test something without issuing real certificates, consider using the staging endpoint instead:
|
||||
lego defaults to communicating with the production Let's Encrypt ACME server.
|
||||
If you'd like to test something without issuing real certificates, consider using the staging endpoint instead:
|
||||
|
||||
```bash
|
||||
lego --server=https://acme-staging-v02.api.letsencrypt.org/directory …
|
||||
@ -193,6 +194,8 @@ import (
|
||||
|
||||
"github.com/xenolf/lego/certcrypto"
|
||||
"github.com/xenolf/lego/certificate"
|
||||
"github.com/xenolf/lego/challenge/http01"
|
||||
"github.com/xenolf/lego/challenge/tlsalpn01"
|
||||
"github.com/xenolf/lego/lego"
|
||||
"github.com/xenolf/lego/registration"
|
||||
)
|
||||
@ -243,10 +246,12 @@ func main() {
|
||||
// because we aren't running as root and can't bind a listener to port 80 and 443
|
||||
// (used later when we attempt to pass challenges). Keep in mind that you still
|
||||
// need to proxy challenge traffic to port 5002 and 5001.
|
||||
if err = client.Challenge.SetHTTP01Address(":5002"); err != nil {
|
||||
err = client.Challenge.SetHTTP01Provider(http01.NewProviderServer("", "5002"))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if err = client.Challenge.SetTLSALPN01Address(":5001"); err != nil {
|
||||
err = client.Challenge.SetTLSALPN01Provider(tlsalpn01.NewProviderServer("", "5001"))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
|
@ -140,6 +140,8 @@ func (c *Challenge) Solve(authz acme.Authorization) error {
|
||||
|
||||
// CleanUp cleans the challenge.
|
||||
func (c *Challenge) CleanUp(authz acme.Authorization) error {
|
||||
log.Infof("[%s] acme: Cleaning DNS-01 challenge", challenge.GetTargetedDomain(authz))
|
||||
|
||||
chlng, err := challenge.FindChallenge(challenge.DNS01, authz)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -3,7 +3,6 @@ package resolver
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"sort"
|
||||
"strconv"
|
||||
"time"
|
||||
@ -21,7 +20,7 @@ type byType []acme.Challenge
|
||||
|
||||
func (a byType) Len() int { return len(a) }
|
||||
func (a byType) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
func (a byType) Less(i, j int) bool { return a[i].Type < a[j].Type }
|
||||
func (a byType) Less(i, j int) bool { return a[i].Type > a[j].Type }
|
||||
|
||||
type SolverManager struct {
|
||||
core *api.Core
|
||||
@ -29,55 +28,12 @@ type SolverManager struct {
|
||||
}
|
||||
|
||||
func NewSolversManager(core *api.Core) *SolverManager {
|
||||
solvers := map[challenge.Type]solver{
|
||||
challenge.HTTP01: http01.NewChallenge(core, validate, &http01.ProviderServer{}),
|
||||
challenge.TLSALPN01: tlsalpn01.NewChallenge(core, validate, &tlsalpn01.ProviderServer{}),
|
||||
}
|
||||
|
||||
return &SolverManager{
|
||||
solvers: solvers,
|
||||
solvers: map[challenge.Type]solver{},
|
||||
core: core,
|
||||
}
|
||||
}
|
||||
|
||||
// SetHTTP01Address specifies a custom interface:port to be used for HTTP based challenges.
|
||||
// If this option is not used, the default port 80 and all interfaces will be used.
|
||||
// To only specify a port and no interface use the ":port" notation.
|
||||
//
|
||||
// NOTE: This REPLACES any custom HTTP provider previously set by calling
|
||||
// c.SetProvider with the default HTTP challenge provider.
|
||||
func (c *SolverManager) SetHTTP01Address(iface string) error {
|
||||
host, port, err := net.SplitHostPort(iface)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if chlng, ok := c.solvers[challenge.HTTP01]; ok {
|
||||
chlng.(*http01.Challenge).SetProvider(http01.NewProviderServer(host, port))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetTLSALPN01Address specifies a custom interface:port to be used for TLS based challenges.
|
||||
// If this option is not used, the default port 443 and all interfaces will be used.
|
||||
// To only specify a port and no interface use the ":port" notation.
|
||||
//
|
||||
// NOTE: This REPLACES any custom TLS-ALPN provider previously set by calling
|
||||
// c.SetProvider with the default TLS-ALPN challenge provider.
|
||||
func (c *SolverManager) SetTLSALPN01Address(iface string) error {
|
||||
host, port, err := net.SplitHostPort(iface)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if chlng, ok := c.solvers[challenge.TLSALPN01]; ok {
|
||||
chlng.(*tlsalpn01.Challenge).SetProvider(tlsalpn01.NewProviderServer(host, port))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetHTTP01Provider specifies a custom provider p that can solve the given HTTP-01 challenge.
|
||||
func (c *SolverManager) SetHTTP01Provider(p challenge.Provider) error {
|
||||
c.solvers[challenge.HTTP01] = http01.NewChallenge(c.core, validate, p)
|
||||
@ -96,18 +52,15 @@ func (c *SolverManager) SetDNS01Provider(p challenge.Provider, opts ...dns01.Cha
|
||||
return nil
|
||||
}
|
||||
|
||||
// Exclude explicitly removes challenges from the pool for solving.
|
||||
func (c *SolverManager) Exclude(challenges []challenge.Type) {
|
||||
// Loop through all challenges and delete the requested one if found.
|
||||
for _, chlg := range challenges {
|
||||
delete(c.solvers, chlg)
|
||||
}
|
||||
// Remove Remove a challenge type from the available solvers.
|
||||
func (c *SolverManager) Remove(chlgType challenge.Type) {
|
||||
delete(c.solvers, chlgType)
|
||||
}
|
||||
|
||||
// Checks all challenges from the server in order and returns the first matching solver.
|
||||
func (c *SolverManager) chooseSolver(authz acme.Authorization) solver {
|
||||
// Allow to have a deterministic challenge order
|
||||
sort.Sort(sort.Reverse(byType(authz.Challenges)))
|
||||
sort.Sort(byType(authz.Challenges))
|
||||
|
||||
domain := challenge.GetTargetedDomain(authz)
|
||||
for _, chlg := range authz.Challenges {
|
||||
|
@ -5,54 +5,30 @@ import (
|
||||
"crypto/rsa"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
"unsafe"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/xenolf/lego/acme"
|
||||
"github.com/xenolf/lego/acme/api"
|
||||
"github.com/xenolf/lego/challenge"
|
||||
"github.com/xenolf/lego/challenge/http01"
|
||||
"github.com/xenolf/lego/platform/tester"
|
||||
"gopkg.in/square/go-jose.v2"
|
||||
)
|
||||
|
||||
func TestSolverManager_SetHTTP01Address(t *testing.T) {
|
||||
_, apiURL, tearDown := tester.SetupFakeAPI()
|
||||
defer tearDown()
|
||||
func TestByType(t *testing.T) {
|
||||
challenges := []acme.Challenge{
|
||||
{Type: "dns-01"}, {Type: "tlsalpn-01"}, {Type: "http-01"},
|
||||
}
|
||||
|
||||
keyBits := 32 // small value keeps test fast
|
||||
key, err := rsa.GenerateKey(rand.Reader, keyBits)
|
||||
require.NoError(t, err, "Could not generate test key")
|
||||
sort.Sort(byType(challenges))
|
||||
|
||||
core, err := api.New(http.DefaultClient, "lego-test", apiURL+"/dir", "", key)
|
||||
require.NoError(t, err)
|
||||
expected := []acme.Challenge{
|
||||
{Type: "tlsalpn-01"}, {Type: "http-01"}, {Type: "dns-01"},
|
||||
}
|
||||
|
||||
solversManager := NewSolversManager(core)
|
||||
|
||||
optPort := "1234"
|
||||
optHost := ""
|
||||
|
||||
err = solversManager.SetHTTP01Address(net.JoinHostPort(optHost, optPort))
|
||||
require.NoError(t, err)
|
||||
|
||||
require.IsType(t, &http01.Challenge{}, solversManager.solvers[challenge.HTTP01])
|
||||
httpSolver := solversManager.solvers[challenge.HTTP01].(*http01.Challenge)
|
||||
|
||||
httpProviderServer := (*http01.ProviderServer)(unsafe.Pointer(reflect.ValueOf(httpSolver).Elem().FieldByName("provider").InterfaceData()[1]))
|
||||
assert.Equal(t, net.JoinHostPort(optHost, optPort), httpProviderServer.GetAddress())
|
||||
|
||||
// test setting different host
|
||||
optHost = "127.0.0.1"
|
||||
err = solversManager.SetHTTP01Address(net.JoinHostPort(optHost, optPort))
|
||||
require.NoError(t, err)
|
||||
|
||||
httpProviderServer = (*http01.ProviderServer)(unsafe.Pointer(reflect.ValueOf(httpSolver).Elem().FieldByName("provider").InterfaceData()[1]))
|
||||
assert.Equal(t, net.JoinHostPort(optHost, optPort), httpProviderServer.GetAddress())
|
||||
assert.Equal(t, expected, challenges)
|
||||
}
|
||||
|
||||
func TestValidate(t *testing.T) {
|
||||
|
@ -107,7 +107,7 @@ Here is an example bash command using the CloudFlare DNS provider:
|
||||
fmt.Fprintln(w, "\tglesys:\tGLESYS_POLLING_INTERVAL, GLESYS_PROPAGATION_TIMEOUT, GLESYS_TTL, GLESYS_HTTP_TIMEOUT")
|
||||
fmt.Fprintln(w, "\tgodaddy:\tGODADDY_POLLING_INTERVAL, GODADDY_PROPAGATION_TIMEOUT, GODADDY_TTL, GODADDY_HTTP_TIMEOUT, GODADDY_SEQUENCE_INTERVAL")
|
||||
fmt.Fprintln(w, "\thostingde:\tHOSTINGDE_POLLING_INTERVAL, HOSTINGDE_PROPAGATION_TIMEOUT, HOSTINGDE_TTL, HOSTINGDE_HTTP_TIMEOUT")
|
||||
fmt.Fprintln(w, "\thttpreq:\t,HTTPREQ_POLLING_INTERVAL, HTTPREQ_PROPAGATION_TIMEOUT, HTTPREQ_HTTP_TIMEOUT")
|
||||
fmt.Fprintln(w, "\thttpreq:\tHTTPREQ_POLLING_INTERVAL, HTTPREQ_PROPAGATION_TIMEOUT, HTTPREQ_HTTP_TIMEOUT")
|
||||
fmt.Fprintln(w, "\tiij:\tIIJ_POLLING_INTERVAL, IIJ_PROPAGATION_TIMEOUT, IIJ_TTL")
|
||||
fmt.Fprintln(w, "\tinwx:\tINWX_POLLING_INTERVAL, INWX_PROPAGATION_TIMEOUT, INWX_TTL, INWX_SANDBOX")
|
||||
fmt.Fprintln(w, "\tlightsail:\tLIGHTSAIL_POLLING_INTERVAL, LIGHTSAIL_PROPAGATION_TIMEOUT")
|
||||
|
55
cmd/flags.go
55
cmd/flags.go
@ -13,8 +13,8 @@ func CreateFlags(defaultPath string) []cli.Flag {
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "server, s",
|
||||
Value: lego.LEDirectoryProduction,
|
||||
Usage: "CA hostname (and optionally :port). The server certificate must be trusted in order to avoid further modifications to the client.",
|
||||
Value: lego.LEDirectoryProduction,
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "accept-tos, a",
|
||||
@ -26,7 +26,7 @@ func CreateFlags(defaultPath string) []cli.Flag {
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "csr, c",
|
||||
Usage: "Certificate signing request filename, if an external CSR is to be used",
|
||||
Usage: "Certificate signing request filename, if an external CSR is to be used.",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "eab",
|
||||
@ -43,56 +43,63 @@ func CreateFlags(defaultPath string) []cli.Flag {
|
||||
cli.StringFlag{
|
||||
Name: "key-type, k",
|
||||
Value: "rsa2048",
|
||||
Usage: "Key type to use for private keys. Supported: rsa2048, rsa4096, rsa8192, ec256, ec384",
|
||||
Usage: "Key type to use for private keys. Supported: rsa2048, rsa4096, rsa8192, ec256, ec384.",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "filename",
|
||||
Usage: "Filename of the generated certificate",
|
||||
Usage: "(deprecated) Filename of the generated certificate.",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "path",
|
||||
Usage: "Directory to use for storing the data",
|
||||
Usage: "Directory to use for storing the data.",
|
||||
Value: defaultPath,
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "exclude, x",
|
||||
Usage: "Explicitly disallow solvers by name from being used. Solvers: \"http-01\", \"dns-01\", \"tls-alpn-01\".",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "http-timeout",
|
||||
Usage: "Set the HTTP timeout value to a specific value in seconds. The default is 10 seconds.",
|
||||
cli.BoolFlag{
|
||||
Name: "http",
|
||||
Usage: "Use the HTTP challenge to solve challenges. Can be mixed with other types of challenges.",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "webroot",
|
||||
Usage: "Set the webroot folder to use for HTTP based challenges to write directly in a file in .well-known/acme-challenge",
|
||||
Name: "http.port",
|
||||
Usage: "Set the port and interface to use for HTTP based challenges to listen on.Supported: interface:port or :port.",
|
||||
Value: ":80",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "http.webroot",
|
||||
Usage: "Set the webroot folder to use for HTTP based challenges to write directly in a file in .well-known/acme-challenge.",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "memcached-host",
|
||||
Name: "http.memcached-host",
|
||||
Usage: "Set the memcached host(s) to use for HTTP based challenges. Challenges will be written to all specified hosts.",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "http",
|
||||
Usage: "Set the port and interface to use for HTTP based challenges to listen on. Supported: interface:port or :port",
|
||||
cli.BoolFlag{
|
||||
Name: "tls",
|
||||
Usage: "Use the TLS challenge to solve challenges. Can be mixed with other types of challenges.",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "tls",
|
||||
Usage: "Set the port and interface to use for TLS based challenges to listen on. Supported: interface:port or :port",
|
||||
Name: "tls.port",
|
||||
Usage: "Set the port and interface to use for TLS based challenges to listen on. Supported: interface:port or :port.",
|
||||
Value: ":443",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "dns",
|
||||
Usage: "Solve a DNS challenge using the specified provider. Disables all other challenges. Run 'lego dnshelp' for help on usage.",
|
||||
Usage: "Solve a DNS challenge using the specified provider. Can be mixed with other types of challenges. Run 'lego dnshelp' for help on usage.",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "dns-disable-cp",
|
||||
Name: "dns.disable-cp",
|
||||
Usage: "By setting this flag to true, disables the need to wait the propagation of the TXT record to all authoritative name servers.",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "dns-resolvers",
|
||||
Name: "dns.resolvers",
|
||||
Usage: "Set the resolvers to use for performing recursive DNS queries. Supported: host:port. The default is to use the system resolvers, or Google's DNS resolvers if the system's cannot be determined.",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "http-timeout",
|
||||
Usage: "Set the HTTP timeout value to a specific value in seconds.",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "dns-timeout",
|
||||
Usage: "Set the DNS timeout value to a specific value in seconds. Used only when performing authoritative name servers queries. The default is 10 seconds.",
|
||||
Usage: "Set the DNS timeout value to a specific value in seconds. Used only when performing authoritative name servers queries.",
|
||||
Value: 10,
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "pem",
|
||||
|
@ -3,8 +3,10 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
"github.com/xenolf/lego/cmd"
|
||||
@ -20,7 +22,12 @@ func main() {
|
||||
app.Name = "lego"
|
||||
app.HelpName = "lego"
|
||||
app.Usage = "Let's Encrypt client written in Go"
|
||||
app.EnableBashCompletion = true
|
||||
|
||||
app.Version = version
|
||||
cli.VersionPrinter = func(c *cli.Context) {
|
||||
fmt.Printf("lego version %s %s/%s\n", c.App.Version, runtime.GOOS, runtime.GOARCH)
|
||||
}
|
||||
|
||||
defaultPath := ""
|
||||
cwd, err := os.Getwd()
|
||||
|
@ -1,12 +1,15 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
"github.com/xenolf/lego/challenge"
|
||||
"github.com/xenolf/lego/challenge/dns01"
|
||||
"github.com/xenolf/lego/challenge/http01"
|
||||
"github.com/xenolf/lego/challenge/tlsalpn01"
|
||||
"github.com/xenolf/lego/lego"
|
||||
"github.com/xenolf/lego/log"
|
||||
"github.com/xenolf/lego/providers/dns"
|
||||
@ -15,24 +18,22 @@ import (
|
||||
)
|
||||
|
||||
func setupChallenges(ctx *cli.Context, client *lego.Client) {
|
||||
if len(ctx.GlobalStringSlice("exclude")) > 0 {
|
||||
excludedSolvers(ctx, client)
|
||||
if !ctx.GlobalBool("http") && !ctx.GlobalBool("tls") && !ctx.GlobalIsSet("dns") {
|
||||
log.Fatal("No challenge selected. You must specify at least one challenge: `--http`, `--tls`, `--dns`.")
|
||||
}
|
||||
|
||||
if ctx.GlobalIsSet("webroot") {
|
||||
setupWebroot(client, ctx.GlobalString("webroot"))
|
||||
if ctx.GlobalBool("http") {
|
||||
err := client.Challenge.SetHTTP01Provider(setupHTTPProvider(ctx))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
if ctx.GlobalIsSet("memcached-host") {
|
||||
setupMemcached(client, ctx.GlobalStringSlice("memcached-host"))
|
||||
}
|
||||
|
||||
if ctx.GlobalIsSet("http") {
|
||||
setupHTTP(client, ctx.GlobalString("http"))
|
||||
}
|
||||
|
||||
if ctx.GlobalIsSet("tls") {
|
||||
setupTLS(client, ctx.GlobalString("tls"))
|
||||
if ctx.GlobalBool("tls") {
|
||||
err := client.Challenge.SetTLSALPN01Provider(setupTLSProvider(ctx))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
if ctx.GlobalIsSet("dns") {
|
||||
@ -40,65 +41,59 @@ func setupChallenges(ctx *cli.Context, client *lego.Client) {
|
||||
}
|
||||
}
|
||||
|
||||
func excludedSolvers(ctx *cli.Context, client *lego.Client) {
|
||||
var cc []challenge.Type
|
||||
for _, s := range ctx.GlobalStringSlice("exclude") {
|
||||
cc = append(cc, challenge.Type(s))
|
||||
}
|
||||
client.Challenge.Exclude(cc)
|
||||
}
|
||||
func setupHTTPProvider(ctx *cli.Context) challenge.Provider {
|
||||
switch {
|
||||
case ctx.GlobalIsSet("http.webroot"):
|
||||
ps, err := webroot.NewHTTPProvider(ctx.GlobalString("http.webroot"))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return ps
|
||||
case ctx.GlobalIsSet("http.memcached-host"):
|
||||
ps, err := memcached.NewMemcachedProvider(ctx.GlobalStringSlice("http.memcached-host"))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return ps
|
||||
case ctx.GlobalIsSet("http.port"):
|
||||
iface := ctx.GlobalString("http.port")
|
||||
if !strings.Contains(iface, ":") {
|
||||
log.Fatalf("The --http switch only accepts interface:port or :port for its argument.")
|
||||
}
|
||||
|
||||
func setupWebroot(client *lego.Client, path string) {
|
||||
provider, err := webroot.NewHTTPProvider(path)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
host, port, err := net.SplitHostPort(iface)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
err = client.Challenge.SetHTTP01Provider(provider)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// --webroot=foo indicates that the user specifically want to do a HTTP challenge
|
||||
// infer that the user also wants to exclude all other challenges
|
||||
client.Challenge.Exclude([]challenge.Type{challenge.DNS01, challenge.TLSALPN01})
|
||||
}
|
||||
|
||||
func setupMemcached(client *lego.Client, hosts []string) {
|
||||
provider, err := memcached.NewMemcachedProvider(hosts)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
err = client.Challenge.SetHTTP01Provider(provider)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// --memcached-host=foo:11211 indicates that the user specifically want to do a HTTP challenge
|
||||
// infer that the user also wants to exclude all other challenges
|
||||
client.Challenge.Exclude([]challenge.Type{challenge.DNS01, challenge.TLSALPN01})
|
||||
}
|
||||
|
||||
func setupHTTP(client *lego.Client, iface string) {
|
||||
if !strings.Contains(iface, ":") {
|
||||
log.Fatalf("The --http switch only accepts interface:port or :port for its argument.")
|
||||
}
|
||||
|
||||
err := client.Challenge.SetHTTP01Address(iface)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return http01.NewProviderServer(host, port)
|
||||
case ctx.GlobalBool("http"):
|
||||
return http01.NewProviderServer("", "")
|
||||
default:
|
||||
log.Fatal("Invalid HTTP challenge options.")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func setupTLS(client *lego.Client, iface string) {
|
||||
if !strings.Contains(iface, ":") {
|
||||
log.Fatalf("The --tls switch only accepts interface:port or :port for its argument.")
|
||||
}
|
||||
func setupTLSProvider(ctx *cli.Context) challenge.Provider {
|
||||
switch {
|
||||
case ctx.GlobalIsSet("tls.port"):
|
||||
iface := ctx.GlobalString("tls.port")
|
||||
if !strings.Contains(iface, ":") {
|
||||
log.Fatalf("The --tls switch only accepts interface:port or :port for its argument.")
|
||||
}
|
||||
|
||||
err := client.Challenge.SetTLSALPN01Address(iface)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
host, port, err := net.SplitHostPort(iface)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
return tlsalpn01.NewProviderServer(host, port)
|
||||
case ctx.GlobalBool("tls"):
|
||||
return tlsalpn01.NewProviderServer("", "")
|
||||
default:
|
||||
log.Fatal("Invalid HTTP challenge options.")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@ -108,11 +103,11 @@ func setupDNS(ctx *cli.Context, client *lego.Client) {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
servers := ctx.GlobalStringSlice("dns-resolvers")
|
||||
servers := ctx.GlobalStringSlice("dns.resolvers")
|
||||
err = client.Challenge.SetDNS01Provider(provider,
|
||||
dns01.CondOption(len(servers) > 0,
|
||||
dns01.AddRecursiveNameservers(dns01.ParseNameservers(ctx.GlobalStringSlice("dns-resolvers")))),
|
||||
dns01.CondOption(ctx.GlobalIsSet("dns-disable-cp"),
|
||||
dns01.AddRecursiveNameservers(dns01.ParseNameservers(ctx.GlobalStringSlice("dns.resolvers")))),
|
||||
dns01.CondOption(ctx.GlobalIsSet("dns.disable-cp"),
|
||||
dns01.DisableCompletePropagationRequirement()),
|
||||
dns01.CondOption(ctx.GlobalIsSet("dns-timeout"),
|
||||
dns01.AddDNSTimeout(time.Duration(ctx.GlobalInt("dns-timeout"))*time.Second)),
|
||||
|
@ -13,7 +13,8 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/xenolf/lego/certificate"
|
||||
"github.com/xenolf/lego/challenge"
|
||||
"github.com/xenolf/lego/challenge/http01"
|
||||
"github.com/xenolf/lego/challenge/tlsalpn01"
|
||||
"github.com/xenolf/lego/e2e/loader"
|
||||
"github.com/xenolf/lego/lego"
|
||||
"github.com/xenolf/lego/registration"
|
||||
@ -50,12 +51,10 @@ func TestChallengeHTTP_Run(t *testing.T) {
|
||||
output, err := load.RunLego(
|
||||
"-m", "hubert@hubert.com",
|
||||
"--accept-tos",
|
||||
"-x", "dns-01",
|
||||
"-x", "tls-alpn-01",
|
||||
"-s", "https://localhost:14000/dir",
|
||||
"-d", "acme.wtf",
|
||||
"--http", ":5002",
|
||||
"--tls", ":5001",
|
||||
"--http",
|
||||
"--http.port", ":5002",
|
||||
"run")
|
||||
|
||||
if len(output) > 0 {
|
||||
@ -72,12 +71,10 @@ func TestChallengeTLS_Run_Domains(t *testing.T) {
|
||||
output, err := load.RunLego(
|
||||
"-m", "hubert@hubert.com",
|
||||
"--accept-tos",
|
||||
"-x", "dns-01",
|
||||
"-x", "http-01",
|
||||
"-s", "https://localhost:14000/dir",
|
||||
"-d", "acme.wtf",
|
||||
"--http", ":5002",
|
||||
"--tls", ":5001",
|
||||
"--tls",
|
||||
"--tls.port", ":5001",
|
||||
"run")
|
||||
|
||||
if len(output) > 0 {
|
||||
@ -94,12 +91,10 @@ func TestChallengeTLS_Run_CSR(t *testing.T) {
|
||||
output, err := load.RunLego(
|
||||
"-m", "hubert@hubert.com",
|
||||
"--accept-tos",
|
||||
"-x", "dns-01",
|
||||
"-x", "http-01",
|
||||
"-s", "https://localhost:14000/dir",
|
||||
"-csr", "./fixtures/csr.raw",
|
||||
"--http", ":5002",
|
||||
"--tls", ":5001",
|
||||
"--tls",
|
||||
"--tls.port", ":5001",
|
||||
"run")
|
||||
|
||||
if len(output) > 0 {
|
||||
@ -116,12 +111,10 @@ func TestChallengeTLS_Run_CSR_PEM(t *testing.T) {
|
||||
output, err := load.RunLego(
|
||||
"-m", "hubert@hubert.com",
|
||||
"--accept-tos",
|
||||
"-x", "dns-01",
|
||||
"-x", "http-01",
|
||||
"-s", "https://localhost:14000/dir",
|
||||
"-csr", "./fixtures/csr.cert",
|
||||
"--http", ":5002",
|
||||
"--tls", ":5001",
|
||||
"--tls",
|
||||
"--tls.port", ":5001",
|
||||
"run")
|
||||
|
||||
if len(output) > 0 {
|
||||
@ -138,13 +131,11 @@ func TestChallengeTLS_Run_Revoke(t *testing.T) {
|
||||
output, err := load.RunLego(
|
||||
"-m", "hubert@hubert.com",
|
||||
"--accept-tos",
|
||||
"-x", "dns-01",
|
||||
"-x", "http-01",
|
||||
"-s", "https://localhost:14000/dir",
|
||||
"-d", "lego.wtf",
|
||||
"-d", "acme.lego.wtf",
|
||||
"--http", ":5002",
|
||||
"--tls", ":5001",
|
||||
"--tls",
|
||||
"--tls.port", ":5001",
|
||||
"run")
|
||||
|
||||
if len(output) > 0 {
|
||||
@ -157,12 +148,10 @@ func TestChallengeTLS_Run_Revoke(t *testing.T) {
|
||||
output, err = load.RunLego(
|
||||
"-m", "hubert@hubert.com",
|
||||
"--accept-tos",
|
||||
"-x", "dns-01",
|
||||
"-x", "http-01",
|
||||
"-s", "https://localhost:14000/dir",
|
||||
"-d", "lego.wtf",
|
||||
"--http", ":5002",
|
||||
"--tls", ":5001",
|
||||
"--tls",
|
||||
"--tls.port", ":5001",
|
||||
"revoke")
|
||||
|
||||
if len(output) > 0 {
|
||||
@ -179,12 +168,10 @@ func TestChallengeTLS_Run_Revoke_Non_ASCII(t *testing.T) {
|
||||
output, err := load.RunLego(
|
||||
"-m", "hubert@hubert.com",
|
||||
"--accept-tos",
|
||||
"-x", "dns-01",
|
||||
"-x", "http-01",
|
||||
"-s", "https://localhost:14000/dir",
|
||||
"-d", "légô.wtf",
|
||||
"--http", ":5002",
|
||||
"--tls", ":5001",
|
||||
"--tls",
|
||||
"--tls.port", ":5001",
|
||||
"run")
|
||||
|
||||
if len(output) > 0 {
|
||||
@ -197,12 +184,10 @@ func TestChallengeTLS_Run_Revoke_Non_ASCII(t *testing.T) {
|
||||
output, err = load.RunLego(
|
||||
"-m", "hubert@hubert.com",
|
||||
"--accept-tos",
|
||||
"-x", "dns-01",
|
||||
"-x", "http-01",
|
||||
"-s", "https://localhost:14000/dir",
|
||||
"-d", "légô.wtf",
|
||||
"--http", ":5002",
|
||||
"--tls", ":5001",
|
||||
"--tls",
|
||||
"--tls.port", ":5001",
|
||||
"revoke")
|
||||
|
||||
if len(output) > 0 {
|
||||
@ -228,8 +213,7 @@ func TestChallengeHTTP_Client_Obtain(t *testing.T) {
|
||||
client, err := lego.NewClient(config)
|
||||
require.NoError(t, err)
|
||||
|
||||
client.Challenge.Exclude([]challenge.Type{challenge.DNS01, challenge.TLSALPN01})
|
||||
err = client.Challenge.SetHTTP01Address(":5002")
|
||||
err = client.Challenge.SetHTTP01Provider(http01.NewProviderServer("", "5002"))
|
||||
require.NoError(t, err)
|
||||
|
||||
reg, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
|
||||
@ -267,8 +251,7 @@ func TestChallengeTLS_Client_Obtain(t *testing.T) {
|
||||
client, err := lego.NewClient(config)
|
||||
require.NoError(t, err)
|
||||
|
||||
client.Challenge.Exclude([]challenge.Type{challenge.DNS01, challenge.HTTP01})
|
||||
err = client.Challenge.SetTLSALPN01Address(":5001")
|
||||
err = client.Challenge.SetTLSALPN01Provider(tlsalpn01.NewProviderServer("", "5001"))
|
||||
require.NoError(t, err)
|
||||
|
||||
reg, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
|
||||
@ -307,8 +290,7 @@ func TestChallengeTLS_Client_ObtainForCSR(t *testing.T) {
|
||||
client, err := lego.NewClient(config)
|
||||
require.NoError(t, err)
|
||||
|
||||
client.Challenge.Exclude([]challenge.Type{challenge.DNS01, challenge.HTTP01})
|
||||
err = client.Challenge.SetTLSALPN01Address(":5001")
|
||||
err = client.Challenge.SetTLSALPN01Provider(tlsalpn01.NewProviderServer("", "5001"))
|
||||
require.NoError(t, err)
|
||||
|
||||
reg, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
|
||||
|
@ -11,7 +11,6 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/xenolf/lego/certificate"
|
||||
"github.com/xenolf/lego/challenge"
|
||||
"github.com/xenolf/lego/challenge/dns01"
|
||||
"github.com/xenolf/lego/e2e/loader"
|
||||
"github.com/xenolf/lego/lego"
|
||||
@ -55,16 +54,12 @@ func TestChallengeDNS_Run(t *testing.T) {
|
||||
output, err := load.RunLego(
|
||||
"-m", "hubert@hubert.com",
|
||||
"--accept-tos",
|
||||
"-x", "http-01",
|
||||
"-x", "tls-alpn-01",
|
||||
"--dns-disable-cp",
|
||||
"--dns-resolvers", ":8053",
|
||||
"--dns", "exec",
|
||||
"--dns.resolvers", ":8053",
|
||||
"--dns.disable-cp",
|
||||
"-s", "https://localhost:15000/dir",
|
||||
"-d", "*.légo.acme",
|
||||
"-d", "légo.acme",
|
||||
"--http", ":5004",
|
||||
"--tls", ":5003",
|
||||
"run")
|
||||
|
||||
if len(output) > 0 {
|
||||
@ -100,7 +95,6 @@ func TestChallengeDNS_Client_Obtain(t *testing.T) {
|
||||
err = client.Challenge.SetDNS01Provider(provider,
|
||||
dns01.AddRecursiveNameservers([]string{":8053"}),
|
||||
dns01.DisableCompletePropagationRequirement())
|
||||
client.Challenge.Exclude([]challenge.Type{challenge.HTTP01, challenge.TLSALPN01})
|
||||
require.NoError(t, err)
|
||||
|
||||
reg, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
|
||||
|
Loading…
Reference in New Issue
Block a user