2019-03-11 16:56:48 +00:00
package http01
2016-01-15 04:06:25 +00:00
import (
"fmt"
"net"
"net/http"
"strings"
2018-05-30 17:53:04 +00:00
2019-07-30 19:19:32 +00:00
"github.com/go-acme/lego/v3/log"
2016-01-15 04:06:25 +00:00
)
2018-12-06 21:50:17 +00:00
// ProviderServer implements ChallengeProvider for `http-01` challenge
// It may be instantiated without using the NewProviderServer function if
2016-02-14 15:56:14 +00:00
// you want only to use the default values.
2018-12-06 21:50:17 +00:00
type ProviderServer struct {
2016-01-15 04:06:25 +00:00
iface string
port string
done chan bool
listener net . Listener
}
2018-12-06 21:50:17 +00:00
// NewProviderServer creates a new ProviderServer on the selected interface and port.
2016-02-14 15:56:14 +00:00
// Setting iface and / or port to an empty string will make the server fall back to
// the "any" interface and port 80 respectively.
2018-12-06 21:50:17 +00:00
func NewProviderServer ( iface , port string ) * ProviderServer {
return & ProviderServer { iface : iface , port : port }
2016-02-14 15:56:14 +00:00
}
2018-12-06 21:50:17 +00:00
// Present starts a web server and makes the token available at `ChallengePath(token)` for web requests.
func ( s * ProviderServer ) Present ( domain , token , keyAuth string ) error {
2016-01-15 04:06:25 +00:00
if s . port == "" {
s . port = "80"
}
var err error
2018-12-06 21:50:17 +00:00
s . listener , err = net . Listen ( "tcp" , s . GetAddress ( ) )
2016-01-15 04:06:25 +00:00
if err != nil {
2018-09-24 19:07:20 +00:00
return fmt . Errorf ( "could not start HTTP server for challenge -> %v" , err )
2016-01-15 04:06:25 +00:00
}
s . done = make ( chan bool )
go s . serve ( domain , token , keyAuth )
return nil
}
2018-12-06 21:50:17 +00:00
func ( s * ProviderServer ) GetAddress ( ) string {
return net . JoinHostPort ( s . iface , s . port )
}
// CleanUp closes the HTTP server and removes the token from `ChallengePath(token)`
func ( s * ProviderServer ) CleanUp ( domain , token , keyAuth string ) error {
2016-01-15 04:06:25 +00:00
if s . listener == nil {
return nil
}
s . listener . Close ( )
<- s . done
return nil
}
2018-12-06 21:50:17 +00:00
func ( s * ProviderServer ) serve ( domain , token , keyAuth string ) {
path := ChallengePath ( token )
2016-01-15 04:06:25 +00:00
// The handler validates the HOST header and request type.
// For validation it then writes the token the server returned with the challenge
mux := http . NewServeMux ( )
mux . HandleFunc ( path , func ( w http . ResponseWriter , r * http . Request ) {
2018-06-21 17:06:16 +00:00
if strings . HasPrefix ( r . Host , domain ) && r . Method == http . MethodGet {
2016-01-15 04:06:25 +00:00
w . Header ( ) . Add ( "Content-Type" , "text/plain" )
2018-09-24 19:07:20 +00:00
_ , err := w . Write ( [ ] byte ( keyAuth ) )
if err != nil {
http . Error ( w , err . Error ( ) , http . StatusInternalServerError )
return
}
2018-06-21 17:06:16 +00:00
log . Infof ( "[%s] Served key authentication" , domain )
2016-01-15 04:06:25 +00:00
} else {
2018-06-21 17:06:16 +00:00
log . Warnf ( "Received request for domain %s with method %s but the domain did not match any challenge. Please ensure your are passing the HOST header properly." , r . Host , r . Method )
2018-09-24 19:07:20 +00:00
_ , err := w . Write ( [ ] byte ( "TEST" ) )
if err != nil {
http . Error ( w , err . Error ( ) , http . StatusInternalServerError )
return
}
2016-01-15 04:06:25 +00:00
}
} )
2018-09-24 19:07:20 +00:00
httpServer := & http . Server { Handler : mux }
2018-12-06 21:50:17 +00:00
// Once httpServer is shut down
// we don't want any lingering connections, so disable KeepAlives.
2016-02-07 13:25:31 +00:00
httpServer . SetKeepAlivesEnabled ( false )
2018-09-24 19:07:20 +00:00
err := httpServer . Serve ( s . listener )
2018-12-06 21:50:17 +00:00
if err != nil && ! strings . Contains ( err . Error ( ) , "use of closed network connection" ) {
2018-09-24 19:07:20 +00:00
log . Println ( err )
}
2016-01-15 04:06:25 +00:00
s . done <- true
}