package main import ( "fmt" "io/ioutil" "log" "net/http" "os" "path/filepath" "strings" "github.com/docopt/docopt-go" "github.com/go-ini/ini" "github.com/tidwall/gjson" ) var ( appVersion string buildTime string CertBase string KeyBase string GroupName string RedisBaseURL string VaultBaseURL string certificateDestination string fullchainDestination string keyDestination string caDestination string Type string ) // get redis key func GetRedisKey(redisurl string, redistoken string) string { client := &http.Client{} req, err := http.NewRequest("GET", redisurl, nil) req.SetBasicAuth("redis", redistoken) resp, err := client.Do(req) body, err := ioutil.ReadAll(resp.Body) defer resp.Body.Close() if err != nil { log.Fatalf("[ERROR] Fail to read %v: %v", redisurl, err) } return fmt.Sprintf(string(body)) } // create directory structure and write certificate to file func WriteToFile(content string, destination string, groupname string, filemode os.FileMode, dirmode os.FileMode) { baseDir := filepath.Dir(destination) if _, err := os.Stat(baseDir); os.IsNotExist(err) { os.MkdirAll(baseDir, 0755) } os.Chmod(baseDir, dirmode) file, err := os.OpenFile(destination, os.O_WRONLY|os.O_CREATE, filemode) if err != nil { log.Fatalf("[ERROR] %v cannot be created", destination) } fmt.Fprintf(file, "%v\n", content) file.Close() } // ReadOSRelease from /etc/os-release func ReadOSRelease(configfile string) map[string]string { cfg, err := ini.Load(configfile) if err != nil { log.Fatal("[ERROR] Fail to read file: ", err) } ConfigParams := make(map[string]string) ConfigParams["ID"] = cfg.Section("").Key("ID").String() return ConfigParams } func main() { OSInfo := ReadOSRelease("/etc/os-release") OSRelease := OSInfo["ID"] if OSRelease == "centos" || OSRelease == "rhel" { CertBase = "/etc/pki/tls/certs" KeyBase = "/etc/pki/tls/private" GroupName = "root" } else if OSRelease == "ubuntu" || OSRelease == "debian" { CertBase = "/etc/ssl/certs" KeyBase = "/etc/ssl/private" GroupName = "ssl-cert" } usage := fmt.Sprintf(`ACME Downloader: - fetches and stores a given Certificate, Full Chain, CA and Private Key Usage: acme-downloader --redis-token=REDISTOKEN --vault-token=VAULTTOKEN --cert-name=CERTNAME --team-name=TEAMNAME [--days=DAYS] [--type=TYPE] [--cert-destination=CERTDESTINATION] [--fullchain-destination=FULLCHAINDESTINATION] [--key-destination=KEYDESTINATION] [--ca-destination=CADESTINATION] acme-downloader -v | --version acme-downloader -b | --build acme-downloader -h | --help Options: -h --help Show this screen -v --version Print version exit -b --build Print version and build information and exit --redis-token=REDISTOKEN Redis access token --vault-token=VAULTTOKEN Vault access token --cert-name=CERTNAME Certificate name --team-name=TEAMNAME Team name: swd, dream_team, it, ne, ti... --days=DAYS Days before expiration [default: 30] --type=TYPE Type, EV or OV [default: EV] --cert-destination=CERTDESTINATION Cert Destination [default: %v/<cert-name>.crt] --fullchain-destination=FULLCHAINDESTINATION Full Chain Destination[default: %v/<cert-name>_fullchain.crt] --key-destination=KEYDESTINATION Key Destination [default: %v/<cert-name>.key] --ca-destination=CADESTINATION CA Destination [default: %v/COMODO_<type>.crt] `, CertBase, CertBase, KeyBase, CertBase) // Annoyingly docopt tries to use 'version' the way he wants and I am using build arguments, _ := docopt.Parse(usage, nil, true, appVersion, false) if arguments["--build"] == true { fmt.Printf("acme-downloader version: %v, built on: %v\n", appVersion, buildTime) os.Exit(0) } VaultToken := arguments["--vault-token"].(string) CertName := arguments["--cert-name"].(string) CertNameUndercored := strings.Replace(CertName, ".", "_", -1) TeamName := arguments["--team-name"].(string) RedisToken := arguments["--redis-token"].(string) Type = arguments["--type"].(string) RedisBaseURL = "https://redis.geant.org/GET" VaultBaseURL = "https://vault.geant.org/v1" VaultURL := fmt.Sprintf("%v/%v/%v/vault_%v_key", VaultBaseURL, TeamName, CertName, CertNameUndercored) RedisCertURL := fmt.Sprintf("%v/%v:%v:redis_%v_pem.txt", RedisBaseURL, TeamName, CertName, CertNameUndercored) RedisCAURL := fmt.Sprintf("%v/%v:%v:redis_%v_chain_pem.txt", RedisBaseURL, TeamName, CertName, CertNameUndercored) RedisFullChainURL := fmt.Sprintf("%v/%v:%v:redis_%v_fullchain_pem.txt", RedisBaseURL, TeamName, CertName, CertNameUndercored) certificate := GetRedisKey(RedisCertURL, RedisToken) ca := GetRedisKey(RedisCAURL, RedisToken) fullChain := GetRedisKey(RedisFullChainURL, RedisToken) if arguments["--cert-destination"] == fmt.Sprintf("%v/<cert-name>.crt", CertBase) { certificateDestination = fmt.Sprintf("%v/%v.crt", CertBase, CertName) } else { certificateDestination = arguments["--cert-destination"].(string) } if arguments["--fullchain-destination"] == fmt.Sprintf("%v/<cert-name>_fullchain.crt", CertBase) { fullchainDestination = fmt.Sprintf("%v/%v_fullchain.crt", CertBase, CertName) } else { fullchainDestination = arguments["--fullchain-destination"].(string) } if arguments["--key-destination"] == fmt.Sprintf("%v/<cert-name>.key", KeyBase) { keyDestination = fmt.Sprintf("%v/%v.key", KeyBase, CertName) } else { keyDestination = arguments["--key-destination"].(string) } if arguments["--ca-destination"] == fmt.Sprintf("%v/COMODO_<type>.crt", CertBase) { caDestination = fmt.Sprintf("%v/COMODO_%v.crt", CertBase, Type) } else { caDestination = arguments["--ca-destination"].(string) } // get Vault key vaultClient := &http.Client{} vaultReq, err := http.NewRequest("GET", VaultURL, nil) vaultReq.Header.Add("X-vault-token", VaultToken) vaultResp, err := vaultClient.Do(vaultReq) vaultBody, err := ioutil.ReadAll(vaultResp.Body) defer vaultResp.Body.Close() if err != nil { log.Fatalf("Fail to read %v: %v", VaultURL, err) } privKey := gjson.Get(string(vaultBody), "data.value").String() WriteToFile(certificate, certificateDestination, GroupName, 0644, 0755) WriteToFile(fullChain, fullchainDestination, GroupName, 0644, 0755) WriteToFile(ca, caDestination, GroupName, 0644, 0755) WriteToFile(privKey, keyDestination, GroupName, 0640, 0750) fmt.Printf("installed: %v\n", certificateDestination) fmt.Printf("installed: %v\n", caDestination) fmt.Printf("installed: %v\n", fullchainDestination) fmt.Printf("installed: %v\n", keyDestination) }