Skip to content

Commit

Permalink
Serve ACME HTTP challenges (fix #1)
Browse files Browse the repository at this point in the history
This commit introduces a new flag, acme (string) that serves plain text
files at .well-known/acme-challenge. This is intended to help TLS
deployments that depend on ACME's HTTP challenges. Most Let's Encrypt
users likely depend on this being available, even if they are committed
to being TLS only.

You're encouraged to use DNS challenges if possible so that serving up
files isn't required and you can disable all disk access by
tls-redirector.
  • Loading branch information
ancarda committed Jul 20, 2018
1 parent b38b495 commit 3072db1
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 2 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ replaced with "https".
### Features

* Can run as an unprivileged user by use of systemd activation sockets.
* Requires no disk access, making sandboxing trivial.
* Possible to run without any disk access, making sandboxing trivial.
* IP address traffic (usually by crawlers) is dropped.
* Can serve your .well-known/acme-challenge directory.

### Possible Caveats

Expand Down
37 changes: 36 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,21 @@
package main

import (
"flag"
"io/ioutil"
"log"
"net"
"net/http"
"os"
"strings"

"github.com/coreos/go-systemd/activation"
)

const acmeChallengeUrlPrefix = "/.well-known/acme-challenge/"

var acmeChallengeDir string

func isIPAddress(host string) bool {
if net.ParseIP(host) != nil {
return true
Expand All @@ -25,7 +33,7 @@ func isIPAddress(host string) bool {
}

func handle(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Server", "tls-redirector/1.0")
w.Header().Set("Server", "tls-redirector/1.1")

// If we haven't been given a host, just abort.
if r.Host == "" {
Expand All @@ -43,6 +51,24 @@ func handle(w http.ResponseWriter, r *http.Request) {
return
}

// If we are serving the ACME HTTP challenges, handle that here.
if acmeChallengeDir != "" {
if strings.HasPrefix(r.URL.Path, acmeChallengeUrlPrefix) {
id := strings.TrimPrefix(r.URL.Path, acmeChallengeUrlPrefix)
if _, err := os.Stat(acmeChallengeDir + "/" + id); err == nil {
w.Header().Set("Content-Type", "text/plain")
b, err := ioutil.ReadFile(acmeChallengeDir + "/" + id)
if err != nil {
http.Error(w, "File Not Found", http.StatusNotFound)
return
}

w.Write(b)
return
}
}
}

// Overwrite the scheme to https:// and redirect.
// Change host as well as in r.URL, it's empty.
r.URL.Host = r.Host
Expand All @@ -51,6 +77,15 @@ func handle(w http.ResponseWriter, r *http.Request) {
}

func main() {
flag.StringVar(&acmeChallengeDir, "acme", "", "Location to serve ACME HTTP challenges from")
flag.Parse()

if acmeChallengeDir != "" {
if _, err := os.Stat(acmeChallengeDir); os.IsNotExist(err) {
log.Fatal("fatal: ACME HTTP challenge directory not found: " + acmeChallengeDir)
}
}

http.HandleFunc("/", handle)

listeners, err := activation.Listeners()
Expand Down

0 comments on commit 3072db1

Please sign in to comment.