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))
}
}