github.com/letsencrypt/boulder@v0.20251208.0/test/chall-test-srv/main.go (about)

     1  package main
     2  
     3  import (
     4  	"context"
     5  	"flag"
     6  	"fmt"
     7  	"log"
     8  	"net/http"
     9  	"os"
    10  	"strings"
    11  	"time"
    12  
    13  	"github.com/letsencrypt/challtestsrv"
    14  
    15  	"github.com/letsencrypt/boulder/cmd"
    16  )
    17  
    18  // managementServer is a small HTTP server that can control a challenge server,
    19  // adding and deleting challenge responses as required
    20  type managementServer struct {
    21  	// A managementServer is a http.Server
    22  	*http.Server
    23  	log *log.Logger
    24  	// The challenge server that is under control by the management server
    25  	challSrv *challtestsrv.ChallSrv
    26  }
    27  
    28  func (srv *managementServer) Run() {
    29  	srv.log.Printf("Starting management server on %s", srv.Server.Addr)
    30  	// Start the HTTP server in its own dedicated Go routine
    31  	go func() {
    32  		err := srv.ListenAndServe()
    33  		if err != nil && !strings.Contains(err.Error(), "Server closed") {
    34  			srv.log.Print(err)
    35  		}
    36  	}()
    37  }
    38  
    39  func (srv *managementServer) Shutdown() {
    40  	if err := srv.Server.Shutdown(context.Background()); err != nil {
    41  		srv.log.Printf("Err shutting down management server")
    42  	}
    43  }
    44  
    45  func filterEmpty(input []string) []string {
    46  	var output []string
    47  	for _, val := range input {
    48  		trimmed := strings.TrimSpace(val)
    49  		if trimmed != "" {
    50  			output = append(output, trimmed)
    51  		}
    52  	}
    53  	return output
    54  }
    55  
    56  func main() {
    57  	httpOneBind := flag.String("http01", ":5002",
    58  		"Comma separated bind addresses/ports for HTTP-01 challenges. Set empty to disable.")
    59  	httpsOneBind := flag.String("https01", ":5003",
    60  		"Comma separated bind addresses/ports for HTTPS HTTP-01 challenges. Set empty to disable.")
    61  	dohBind := flag.String("doh", ":8443",
    62  		"Comma separated bind addresses/ports for DoH queries. Set empty to disable.")
    63  	dohCert := flag.String("doh-cert", "", "Path to certificate file for DoH server.")
    64  	dohCertKey := flag.String("doh-cert-key", "", "Path to certificate key file for DoH server.")
    65  	dnsOneBind := flag.String("dns01", ":8053",
    66  		"Comma separated bind addresses/ports for DNS-01 challenges and fake DNS data. Set empty to disable.")
    67  	tlsAlpnOneBind := flag.String("tlsalpn01", ":5001",
    68  		"Comma separated bind addresses/ports for TLS-ALPN-01 and HTTPS HTTP-01 challenges. Set empty to disable.")
    69  	managementBind := flag.String("management", ":8055",
    70  		"Bind address/port for management HTTP interface")
    71  	defaultIPv4 := flag.String("defaultIPv4", "127.0.0.1",
    72  		"Default IPv4 address for mock DNS responses to A queries")
    73  	defaultIPv6 := flag.String("defaultIPv6", "::1",
    74  		"Default IPv6 address for mock DNS responses to AAAA queries")
    75  
    76  	flag.Parse()
    77  
    78  	if len(flag.Args()) > 0 {
    79  		fmt.Printf("invalid command line arguments: %s\n", strings.Join(flag.Args(), " "))
    80  		flag.Usage()
    81  		os.Exit(1)
    82  	}
    83  
    84  	httpOneAddresses := filterEmpty(strings.Split(*httpOneBind, ","))
    85  	httpsOneAddresses := filterEmpty(strings.Split(*httpsOneBind, ","))
    86  	dohAddresses := filterEmpty(strings.Split(*dohBind, ","))
    87  	dnsOneAddresses := filterEmpty(strings.Split(*dnsOneBind, ","))
    88  	tlsAlpnOneAddresses := filterEmpty(strings.Split(*tlsAlpnOneBind, ","))
    89  
    90  	logger := log.New(os.Stdout, "chall-test-srv - ", log.Ldate|log.Ltime)
    91  
    92  	// Create a new challenge server with the provided config
    93  	srv, err := challtestsrv.New(challtestsrv.Config{
    94  		HTTPOneAddrs:    httpOneAddresses,
    95  		HTTPSOneAddrs:   httpsOneAddresses,
    96  		DOHAddrs:        dohAddresses,
    97  		DOHCert:         *dohCert,
    98  		DOHCertKey:      *dohCertKey,
    99  		DNSOneAddrs:     dnsOneAddresses,
   100  		TLSALPNOneAddrs: tlsAlpnOneAddresses,
   101  		Log:             logger,
   102  	})
   103  	cmd.FailOnError(err, "Unable to construct challenge server")
   104  
   105  	// Create a new management server with the provided config
   106  	oobSrv := managementServer{
   107  		Server: &http.Server{
   108  			Addr:        *managementBind,
   109  			ReadTimeout: 30 * time.Second,
   110  		},
   111  		challSrv: srv,
   112  		log:      logger,
   113  	}
   114  	// Register handlers on the management server for adding challenge responses
   115  	// for the configured challenges.
   116  	if *httpOneBind != "" || *httpsOneBind != "" {
   117  		http.HandleFunc("/add-http01", oobSrv.addHTTP01)
   118  		http.HandleFunc("/del-http01", oobSrv.delHTTP01)
   119  		http.HandleFunc("/add-redirect", oobSrv.addHTTPRedirect)
   120  		http.HandleFunc("/del-redirect", oobSrv.delHTTPRedirect)
   121  	}
   122  	if *dnsOneBind != "" {
   123  		http.HandleFunc("/set-default-ipv4", oobSrv.setDefaultDNSIPv4)
   124  		http.HandleFunc("/set-default-ipv6", oobSrv.setDefaultDNSIPv6)
   125  		// TODO(@cpu): It might make sense to revisit this API in the future to have
   126  		// one endpoint that accepts the mock type required (A, AAAA, CNAME, etc)
   127  		// instead of having separate endpoints per type.
   128  		http.HandleFunc("/set-txt", oobSrv.addDNS01)
   129  		http.HandleFunc("/clear-txt", oobSrv.delDNS01)
   130  		http.HandleFunc("/add-a", oobSrv.addDNSARecord)
   131  		http.HandleFunc("/clear-a", oobSrv.delDNSARecord)
   132  		http.HandleFunc("/add-aaaa", oobSrv.addDNSAAAARecord)
   133  		http.HandleFunc("/clear-aaaa", oobSrv.delDNSAAAARecord)
   134  		http.HandleFunc("/add-caa", oobSrv.addDNSCAARecord)
   135  		http.HandleFunc("/clear-caa", oobSrv.delDNSCAARecord)
   136  		http.HandleFunc("/set-cname", oobSrv.addDNSCNAMERecord)
   137  		http.HandleFunc("/clear-cname", oobSrv.delDNSCNAMERecord)
   138  		http.HandleFunc("/set-servfail", oobSrv.addDNSServFailRecord)
   139  		http.HandleFunc("/clear-servfail", oobSrv.delDNSServFailRecord)
   140  
   141  		srv.SetDefaultDNSIPv4(*defaultIPv4)
   142  		srv.SetDefaultDNSIPv6(*defaultIPv6)
   143  		if *defaultIPv4 != "" {
   144  			logger.Printf("Answering A queries with %s by default",
   145  				*defaultIPv4)
   146  		}
   147  		if *defaultIPv6 != "" {
   148  			logger.Printf("Answering AAAA queries with %s by default",
   149  				*defaultIPv6)
   150  		}
   151  	}
   152  	if *tlsAlpnOneBind != "" {
   153  		http.HandleFunc("/add-tlsalpn01", oobSrv.addTLSALPN01)
   154  		http.HandleFunc("/del-tlsalpn01", oobSrv.delTLSALPN01)
   155  	}
   156  
   157  	http.HandleFunc("/clear-request-history", oobSrv.clearHistory)
   158  	http.HandleFunc("/http-request-history", oobSrv.getHTTPHistory)
   159  	http.HandleFunc("/dns-request-history", oobSrv.getDNSHistory)
   160  	http.HandleFunc("/tlsalpn01-request-history", oobSrv.getTLSALPNHistory)
   161  
   162  	// Start all of the sub-servers in their own Go routines so that the main Go
   163  	// routine can spin forever looking for signals to catch.
   164  	go srv.Run()
   165  	go oobSrv.Run()
   166  
   167  	cmd.CatchSignals(func() {
   168  		srv.Shutdown()
   169  		oobSrv.Shutdown()
   170  	})
   171  }