Merge pull request #118 from adriencarbonne/master
Added a --webroot option for HTTP challenge
This commit is contained in:
commit
325db78c91
5 changed files with 124 additions and 1 deletions
|
@ -42,10 +42,11 @@ When using the standard `--path` option, all certificates and account configurat
|
|||
|
||||
#### Sudo
|
||||
The CLI does not require root permissions but needs to bind to port 80 and 443 for certain challenges.
|
||||
To run the CLI without sudo, you have two options:
|
||||
To run the CLI without sudo, you have three 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.
|
||||
|
||||
#### Port Usage
|
||||
By default lego assumes it is able to bind to ports 80 and 443 to solve challenges.
|
||||
|
@ -87,6 +88,7 @@ GLOBAL OPTIONS:
|
|||
--rsa-key-size, -B "2048" Size of the RSA key.
|
||||
--path "${CWD}/.lego" Directory to use for storing the data
|
||||
--exclude, -x [--exclude option --exclude option] Explicitly disallow solvers by name from being used. Solvers: "http-01", "tls-sni-01".
|
||||
--webroot Set the webroot folder to use for HTTP based challenges to write directly in a file in .well-known/acme-challenge
|
||||
--http Set the port and interface to use for HTTP based challenges to listen on. Supported: interface:port or :port
|
||||
--tls Set the port and interface to use for TLS based challenges to listen on. Supported: interface:port or :port
|
||||
--dns Solve a DNS challenge using the specified provider. Disables all other solvers.
|
||||
|
|
4
cli.go
4
cli.go
|
@ -116,6 +116,10 @@ func main() {
|
|||
Name: "exclude, x",
|
||||
Usage: "Explicitly disallow solvers by name from being used. Solvers: \"http-01\", \"tls-sni-01\".",
|
||||
},
|
||||
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",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "http",
|
||||
Usage: "Set the port and interface to use for HTTP based challenges to listen on. Supported: interface:port or :port",
|
||||
|
|
|
@ -16,6 +16,7 @@ import (
|
|||
"github.com/xenolf/lego/providers/dns/dnsimple"
|
||||
"github.com/xenolf/lego/providers/dns/rfc2136"
|
||||
"github.com/xenolf/lego/providers/dns/route53"
|
||||
"github.com/xenolf/lego/providers/http/webroot"
|
||||
)
|
||||
|
||||
func checkFolder(path string) error {
|
||||
|
@ -53,6 +54,18 @@ func setup(c *cli.Context) (*Configuration, *Account, *acme.Client) {
|
|||
client.ExcludeChallenges(conf.ExcludedSolvers())
|
||||
}
|
||||
|
||||
if c.GlobalIsSet("webroot") {
|
||||
provider, err := webroot.NewHTTPProviderWebroot(c.GlobalString("webroot"))
|
||||
if err != nil {
|
||||
logger().Fatal(err)
|
||||
}
|
||||
|
||||
client.SetChallengeProvider(acme.HTTP01, provider)
|
||||
|
||||
// --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.ExcludeChallenges([]acme.Challenge{acme.DNS01, acme.TLSSNI01})
|
||||
}
|
||||
if c.GlobalIsSet("http") {
|
||||
if strings.Index(c.GlobalString("http"), ":") == -1 {
|
||||
logger().Fatalf("The --http switch only accepts interface:port or :port for its argument.")
|
||||
|
|
58
providers/http/webroot/webroot.go
Normal file
58
providers/http/webroot/webroot.go
Normal file
|
@ -0,0 +1,58 @@
|
|||
// Package webroot implements a HTTP provider for solving the HTTP-01 challenge using web server's root path.
|
||||
package webroot
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/xenolf/lego/acme"
|
||||
)
|
||||
|
||||
// HTTPProviderWebroot implements ChallengeProvider for `http-01` challenge
|
||||
type HTTPProviderWebroot struct {
|
||||
path string
|
||||
}
|
||||
|
||||
// NewHTTPProviderWebroot returns a HTTPProviderWebroot instance with a configured webroot path
|
||||
func NewHTTPProviderWebroot(path string) (*HTTPProviderWebroot, error) {
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
return nil, fmt.Errorf("Webroot path does not exist")
|
||||
}
|
||||
|
||||
c := &HTTPProviderWebroot{
|
||||
path: path,
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// Present makes the token available at `HTTP01ChallengePath(token)` by creating a file in the given webroot path
|
||||
func (w *HTTPProviderWebroot) Present(domain, token, keyAuth string) error {
|
||||
var err error
|
||||
|
||||
challengeFilePath := path.Join(w.path, acme.HTTP01ChallengePath(token))
|
||||
err = os.MkdirAll(path.Dir(challengeFilePath), 0777)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not create required directories in webroot for HTTP challenge -> %v", err)
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(challengeFilePath, []byte(keyAuth), 0777)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not write file in webroot for HTTP challenge -> %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CleanUp removes the file created for the challenge
|
||||
func (w *HTTPProviderWebroot) CleanUp(domain, token, keyAuth string) error {
|
||||
var err error
|
||||
err = os.Remove(path.Join(w.path, acme.HTTP01ChallengePath(token)))
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not remove file in webroot after HTTP challenge -> %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
46
providers/http/webroot/webroot_test.go
Normal file
46
providers/http/webroot/webroot_test.go
Normal file
|
@ -0,0 +1,46 @@
|
|||
package webroot
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestHTTPProviderWebRoot(t *testing.T) {
|
||||
webroot := "webroot"
|
||||
domain := "domain"
|
||||
token := "token"
|
||||
keyAuth := "keyAuth"
|
||||
challengeFilePath := webroot + "/.well-known/acme-challenge/" + token
|
||||
|
||||
os.MkdirAll(webroot+"/.well-known/acme-challenge", 0777)
|
||||
defer os.RemoveAll(webroot)
|
||||
|
||||
provider, err := NewHTTPProviderWebroot(webroot)
|
||||
if err != nil {
|
||||
t.Errorf("Webroot provider error: got %v, want nil", err)
|
||||
}
|
||||
|
||||
err = provider.Present(domain, token, keyAuth)
|
||||
if err != nil {
|
||||
t.Errorf("Webroot provider present() error: got %v, want nil", err)
|
||||
}
|
||||
|
||||
if _, err := os.Stat(challengeFilePath); os.IsNotExist(err) {
|
||||
t.Error("Challenge file was not created in webroot")
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadFile(challengeFilePath)
|
||||
if err != nil {
|
||||
t.Errorf("Webroot provider ReadFile() error: got %v, want nil", err)
|
||||
}
|
||||
dataStr := string(data)
|
||||
if dataStr != keyAuth {
|
||||
t.Errorf("Challenge file content: got %q, want %q", dataStr, keyAuth)
|
||||
}
|
||||
|
||||
err = provider.CleanUp(domain, token, keyAuth)
|
||||
if err != nil {
|
||||
t.Errorf("Webroot provider CleanUp() error: got %v, want nil", err)
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue