Skip to content
Snippets Groups Projects
main.go 6.03 KiB
package main

import (
	"fmt"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"github.com/docopt/docopt-go"
	"gopkg.in/ini.v1"
)

var (
	appVersion    string
	buildTime     string
	webDir        string
	jsonConverter string
	bearerToken   string
	WarningLogger *log.Logger
	InfoLogger    *log.Logger
	ErrorLogger   *log.Logger
	verboseBool   bool
)

func init() {
	InfoLogger = log.New(os.Stdout, "INFO: ", log.Ldate|log.Ltime)
	WarningLogger = log.New(os.Stdout, "WARNING: ", log.Ldate|log.Ltime)
	ErrorLogger = log.New(os.Stdout, "ERROR: ", log.Ldate|log.Ltime)
}

// write to file
func writeFile(fileContent string, filePath string) {
	content := []byte(fileContent)
	err := os.WriteFile(filePath, content, 0644)
	if err != nil {
		WarningLogger.Println(err)
	}
}

// serve certificates JSON
func renderJSON(w http.ResponseWriter, req *http.Request) {
	provider := strings.Split(req.URL.Path, "/")[2]
	serveFile := fmt.Sprintf("%v/%v/%v.json", webDir, provider, provider)
	cmd := exec.Command(jsonConverter, "-p", provider)
	err := cmd.Run()
	if err != nil {
		WarningLogger.Println(err)
		w.WriteHeader(http.StatusServiceUnavailable)
	} else {
		if verboseBool {
			InfoLogger.Printf("HTTP Status %v", http.StatusOK)
		}
		w.Header().Set("Content-Type", "application/json")
	}
	http.ServeFile(w, req, serveFile)
}

// serve certificates list
func renderPage(w http.ResponseWriter, req *http.Request) {
	provider := strings.Split(req.URL.Path, "/")
	serveFile := filepath.Join(webDir, req.URL.Path)
	cmd := exec.Command(jsonConverter, "-p", provider[1])
	err := cmd.Run()
	if err != nil {
		WarningLogger.Println(err)
		w.WriteHeader(http.StatusServiceUnavailable)
	} else {
		if verboseBool {
			InfoLogger.Printf("HTTP Status %v", http.StatusOK)
		}
		w.Header().Set("Content-Type", "text/html")
	}
	http.ServeFile(w, req, serveFile)
}

// trigger puppet
func triggerPuppet(w http.ResponseWriter, req *http.Request) {
	//cmd := exec.Command("/usr/bin/pkill", "-f", "/opt/puppetlabs/puppet/bin/puppet", "-s", "SIGUSR1")
	cmd := exec.Command("/usr/bin/touch", "/TEST")
	authToken := strings.Split(req.Header.Get("Authorization"), "Bearer ")[1]
	statusFile := "/tmp/200.json"
	ok := fmt.Sprintf("{\n    \"status\": \"OK\",\n    \"response\": 200\n    \"token\": %v\n}", authToken)
	ko := fmt.Sprintf("{\n    \"status\": \"OK\",\n    \"response\": 401\n    \"token\": %v\n}", authToken)
	writeFile(ok, "/tmp/200.json")
	writeFile(ko, "/tmp/401.json")
	w.Header().Set("Content-Type", "application/json")
	if authToken != bearerToken {
		statusFile = "/tmp/401.json"
		//w.WriteHeader(http.StatusUnauthorized)
		http.Error(w, ko, http.StatusUnauthorized)
		http.ServeFile(w, req, "/tmp/401.json")
		//http.ServeFile(w, req, statusFile)
	} else {
		cmd.Run()
		if verboseBool {
			InfoLogger.Printf("HTTP Status %v", http.StatusOK)
		}
		http.ServeFile(w, req, statusFile)
	}
}

// function redirect
func redirect(w http.ResponseWriter, req *http.Request) {
	redirectURL := filepath.Join(req.URL.Path, "/by_name.html")
	http.Redirect(w, req, redirectURL, http.StatusMovedPermanently)
}

func main() {

	progName := filepath.Base(os.Args[0])

	usage := fmt.Sprintf(`ACME Web:
  - serve ACME HTML pages, trigger Puppet, expose API

Usage:
  %v [--json-converter=JSONCONVERTER] [--listen-address=LISTENADDRESS] [--listen-port=LISTENPORT] [--verbose]
  %v -h | --help
  %v -b | --build
  %v -v | --version

Options:
  -h --help                        Show this screen
  -b --build                       Print version and build information and exit
  -v --version                     Print version information and exit
  --json-converter=JSONCONVERTER   Path to json converter script [default: /usr/bin/cert2json.py]
  --listen-address=LISTENADDRESS   Web server address. Check Go net/http documentation [default: any]
  --listen-port=LISTENPORT         Web server port [default: 8000]
  --verbose                        Log also successful connections
`, progName, progName, progName, progName)

	arguments, _ := docopt.ParseArgs(usage, nil, appVersion)

	if arguments["--build"] == true {
		fmt.Printf("%v version: %v, built on: %v\n", progName, appVersion, buildTime)
		os.Exit(0)
	}
	cfg, err := ini.Load("/root/.acme.ini")
	if err != nil {
		fmt.Printf("Fail to read file: %v", err)
		os.Exit(1)
	}
	//bearerToken = fmt.Sprintf("Bearer %v", cfg.Section("acme").Key("bearer_token").String())
	bearerToken = cfg.Section("acme").Key("bearer_token").String()

	webDir = "/var/www/acme_web"
	jsonConverter = arguments["--json-converter"].(string)
	verboseBool = arguments["--verbose"].(bool)
	listenAddress := arguments["--listen-address"].(string)
	listenPort := arguments["--listen-port"].(string)

	baseURLs := [6]string{"/sectigo_ev", "/sectigo_ov", "/letsencrypt", "/sectigo_ev/", "/sectigo_ov/", "/letsencrypt/"}
	apiURLs := [6]string{"/api/sectigo_ev", "/api/sectigo_ov", "/api/letsencrypt",
		"/api/sectigo_ev/", "/api/sectigo_ov/", "/api/letsencrypt/"}
	otherURLs := [12]string{"/letsencrypt/by_name.html", "/letsencrypt/by_date.html",
		"/letsencrypt/letsencrypt.json", "/letsencrypt/letsencrypt_expired.json",
		"/sectigo_ov/by_name.html", "/sectigo_ov/by_date.html",
		"/sectigo_ov/sectigo_ov.json", "/sectigo_ov/sectigo_ov_expired.json",
		"/sectigo_ev/by_name.html", "/sectigo_ev/by_date.html",
		"/sectigo_ev/sectigo_ev.json", "/sectigo_ev/sectigo_ev_expired.json"}
	puppetURL := "/puppet"

	fs := http.FileServer(http.Dir("/var/www/acme_web/static"))
	http.Handle("/static/", http.StripPrefix("/static/", fs))

	http.HandleFunc(puppetURL, triggerPuppet)

	http.HandleFunc("/", func(res http.ResponseWriter, req *http.Request) {
		http.ServeFile(res, req, filepath.Join(webDir, "index.html"))
	})

	for _, apiElement := range apiURLs {
		http.HandleFunc(apiElement, renderJSON)
	}

	for _, element := range baseURLs {
		http.HandleFunc(element, redirect)
	}

	for _, otherElement := range otherURLs {
		http.HandleFunc(otherElement, renderPage)
	}

	if listenAddress == "any" {
		log.Fatal(http.ListenAndServe(fmt.Sprintf(":%v", listenPort), nil))
	} else {
		log.Fatal(http.ListenAndServe(fmt.Sprintf("%v:%v", listenAddress, listenPort), nil))
	}

}