Newer
Older
"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
tmpCertificateDestination string
tmpFullchainDestination string
tmpCaDestination string
tmpKeyDestination string
certTmpDir string
key *x509.Certificate
cert *x509.Certificate
if runtime.GOOS == "windows" {
certTmpDir = "C:\\tmp\\acme-downloader\\"
} else {
certTmpDir = "/tmp/acme-downloader"
}
err := os.RemoveAll(certTmpDir)
func checkCertificates(dnsname string, certificate string, fullchain string, ca string, key string, days int, fail bool) bool {
daysNumber := time.Now().Local().Add(time.Second * time.Duration(Seconds))
certPEM, err := ioutil.ReadFile(certificate)
if err != nil {
if fail == true {
} else {
return false
}
}
certFullchainPEM, err := ioutil.ReadFile(fullchain)
if err != nil {
if fail == true {
} else {
return false
}
}
rootPEM, err := ioutil.ReadFile(ca)
if err != nil {
if fail == true {
} else {
return false
}
}
roots := x509.NewCertPool()
ok := roots.AppendCertsFromPEM([]byte(rootPEM))
if !ok {
if fail == true {
} else {
return false
}
}
block, _ := pem.Decode([]byte(certPEM))
if block == nil {
if fail == true {
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
if fail == true {
} else {
return false
}
}
fullchainBlock, _ := pem.Decode([]byte(certFullchainPEM))
if fullchainBlock == nil {
if fail == true {
} else {
return false
}
}
fullchainCert, fullchainErr := x509.ParseCertificate(fullchainBlock.Bytes)
if fullchainErr != nil {
if fail == true {
} else {
return false
}
}
opts := x509.VerifyOptions{
Roots: roots,
DNSName: dnsname,
CurrentTime: daysNumber,
Intermediates: x509.NewCertPool(),
}
if _, err := cert.Verify(opts); err != nil {
if fail == true {
} else {
return false
}
}
if _, fullchainErr := fullchainCert.Verify(opts); fullchainErr != nil {
if fail == true {
} else {
return false
}
}
return true
}
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
// check if priv key matches the publick key
func checkPrivkey(privkey string, pubcert string, opensslbinary string, fail bool, silent bool) bool {
_, errOpenssl := exec.Command(opensslbinary, "help").Output()
if errOpenssl != nil {
fmt.Printf("[WARN] skipping private key matching check: please install OpenSSL: %v\n", errOpenssl)
} else {
certPubKey, errCertPubKey := exec.Command(opensslbinary, "x509", "-noout", "-pubkey", "-in", pubcert).Output()
if errCertPubKey != nil {
if fail == true {
fmt.Printf("[ERR] running openssl against %s: %s\n", pubcert, errCertPubKey)
appExit(255)
} else {
return false
}
}
certPrivKey, errCertPrivKey := exec.Command(opensslbinary, "pkey", "-pubout", "-in", privkey).Output()
if errCertPrivKey != nil {
if fail == true {
fmt.Printf("[ERR] running openssl against %s: %s\n", privkey, errCertPrivKey)
appExit(255)
} else {
return false
}
}
pubkeyOutput := string(certPubKey[:])
privkeyOutput := string(certPrivKey[:])
if pubkeyOutput != privkeyOutput {
if fail == true {
fmt.Printf("[ERR] the private key %v does not match the the public certificate %v\n", privkey, pubcert)
appExit(255)
}
}
}
return true
}
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
// check if priv key matches the publick key
/*
func checkPrivkey(privkey string, pubkey string, fail bool) bool {
// extract data from public key
pubkeyByte, errpubkey := ioutil.ReadFile(pubkey)
if errpubkey != nil {
fmt.Printf("[ERR] reading private key %v: %v\n", pubkey, errpubkey)
appExit(255)
}
block, _ := pem.Decode(pubkeyByte)
cert, _ = x509.ParseCertificate(block.Bytes)
rsaPublicKey := cert.PublicKey.(*rsa.PublicKey)
rsaPublicKeyString := fmt.Sprintf(rsaPublicKey.N.String())
// extract data from private key
privkeyByte, errprivkey := ioutil.ReadFile(privkey)
if errprivkey != nil {
fmt.Printf("[ERR] reading private key %v: %v\n", privkey, errprivkey)
appExit(255)
}
fmt.Printf("primo passaggio\n")
keyBlock, _ := pem.Decode(privkeyByte)
key, _ = x509.ParseCertificate(keyBlock.Bytes)
rsaPrivateKey := key.PublicKey.(*rsa.PrivateKey)
rsaPrivateKeyString := fmt.Sprintf(rsaPrivateKey.N.String())
fmt.Printf("primo passaggio")
if rsaPublicKeyString != rsaPrivateKeyString {
if fail == true {
fmt.Printf("[ERR] the private key %v does not match the the public key %v\n", privkey, pubkey)
appExit(255)
} else {
return false
}
}
return true
}
*/
// 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)
// get Vault key
func GetVaultKey(vaulturl string, vaulttoken string) string {
vaultClient := &http.Client{}
req, err := http.NewRequest("GET", vaulturl, nil)
req.Header.Add("X-vault-token", vaulttoken)
resp, err := vaultClient.Do(req)
body, err := ioutil.ReadAll(resp.Body)
func WriteToFile(content string, destination string, filemode os.FileMode) {
baseDir := filepath.Dir(destination)
if _, err := os.Stat(baseDir); os.IsNotExist(err) {
os.MkdirAll(baseDir, 0755)
}
file, err := os.OpenFile(destination, os.O_WRONLY|os.O_CREATE, filemode)
if err != nil {
}
fmt.Fprintf(file, "%v\n", content)
file.Close()
}
// move temp file to destination
func moveFile(source string, destination string, groupid int, filemode os.FileMode, dirmode os.FileMode) {
baseDir := filepath.Dir(destination)
if _, err := os.Stat(baseDir); os.IsNotExist(err) {
os.MkdirAll(baseDir, 0755)
}
err := os.Rename(source, destination)
if err != nil {
fmt.Printf("[ERR] Fail to install %v: %v\n", destination, err)
appExit(255)
}
if runtime.GOOS != "windows" {
err = os.Chown(destination, 0, groupid)
if err != nil {
fmt.Printf("[ERR] Changing file owner to %v", groupid)
appExit(255)
}
}
fmt.Printf("[INFO] installed: %v\n", destination)
}
// ReadOSRelease from /etc/os-release
func ReadOSRelease(configfile string) map[string]string {
ConfigParams["ID"] = "unknown"
} else {
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"
} else if OSRelease == "arch" {
CertBase = "/etc/ssl/certs"
KeyBase = "/etc/ssl/private"
GroupName = "root"
if runtime.GOOS == "windows" {
CertBase = "DRIVE:\\PATH\\TO\\CERTIFICATE"
KeyBase = "DRIVE:\\PATH\\TO\\KEY"
GroupName = "root"
} else {
CertBase = "/PATH/TO/CERTIFICATE"
KeyBase = "/PATH/TO/PRIV/KEY"
GroupName = "root"
}
}
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 [--silent] [--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, 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)
arguments, _ := docopt.Parse(usage, nil, true, appVersion, false)
if arguments["--build"] == true {
fmt.Printf("acme-downloader version: %v, built on: %v\n", appVersion, buildTime)
silent := false
if arguments["--silent"] == true {
silent = true
}
tmpCertificateDestination = "C:\\tmp\\acme-downloader\\cert\\amce_cert.pem"
tmpFullchainDestination = "C:\\tmp\\acme-downloader\\cert\\amce_fullchain.pem"
tmpCaDestination = "C:\\tmp\\acme-downloader\\cert\\amce_ca.pem"
tmpKeyDestination = "C:\\tmp\\acme-downloader\\key\\amce_key.pem"
tmpCertificateDestination = "/tmp/acme-downloader/cert/amce_cert.pem"
tmpFullchainDestination = "/tmp/acme-downloader/cert/amce_fullchain.pem"
tmpCaDestination = "/tmp/acme-downloader/cert/amce_ca.pem"
tmpKeyDestination = "/tmp/acme-downloader/key/amce_key.pem"
group, groupErr := user.LookupGroup(GroupName)
if groupErr != nil {
fmt.Printf("[ERR] Fail looking up %v user user info\n", GroupName)
appExit(255)
}
GroupID, _ = strconv.Atoi(group.Gid)
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)
DayString := arguments["--days"].(string)
Days, daysErr := strconv.Atoi(DayString)
if daysErr != nil {
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)
if arguments["--cert-destination"] == fmt.Sprintf(filepath.Join(CertBase, "<cert-name>.crt")) {
certificateDestination = fmt.Sprintf(filepath.Join(CertBase, fmt.Sprintf("%v.crt", CertName)))
} else {
certificateDestination = arguments["--cert-destination"].(string)
}
if arguments["--fullchain-destination"] == fmt.Sprintf(filepath.Join(CertBase, "<cert-name>_fullchain.crt")) {
fullchainDestination = fmt.Sprintf(filepath.Join(CertBase, fmt.Sprintf("%v_fullchain.crt", CertName)))
} else {
fullchainDestination = arguments["--fullchain-destination"].(string)
}
if arguments["--ca-destination"] == fmt.Sprintf(filepath.Join(CertBase, "COMODO_<type>.crt")) {
caDestination = fmt.Sprintf(filepath.Join(CertBase, fmt.Sprintf("COMODO_%v.crt", Type)))
} else {
caDestination = arguments["--ca-destination"].(string)
}
if arguments["--key-destination"] == fmt.Sprintf(filepath.Join(KeyBase, "<cert-name>.key")) {
keyDestination = fmt.Sprintf(filepath.Join(KeyBase, fmt.Sprintf("%v.key", CertName)))
} else {
keyDestination = arguments["--key-destination"].(string)
}
// check if there is a certificate installed and it is valid
existingCert := checkCertificates(CertName, certificateDestination, fullchainDestination, caDestination, keyDestination, Days, false)
// existingKey := checkPrivkey(keyDestination, certificateDestination, false)
existingKey := checkPrivkey(keyDestination, certificateDestination, opensslBinary, false, silent)
if existingCert == true && existingKey == true {
fmt.Printf("[INFO] the certificate is still valid\n")
certificate := GetRedisKey(RedisCertURL, RedisToken)
ca := GetRedisKey(RedisCAURL, RedisToken)
fullChain := GetRedisKey(RedisFullChainURL, RedisToken)
WriteToFile(certificate, tmpCertificateDestination, 0644)
WriteToFile(fullChain, tmpFullchainDestination, 0644)
WriteToFile(ca, tmpCaDestination, 0644)
WriteToFile(privKey, tmpKeyDestination, 0640)
checkCertificates(CertName, tmpCertificateDestination, tmpFullchainDestination, tmpCaDestination, tmpKeyDestination, Days, true)
//checkPrivkey(tmpKeyDestination, tmpCertificateDestination, true)
checkPrivkey(tmpKeyDestination, tmpCertificateDestination, opensslBinary, true, silent)
// move certificates in place
moveFile(tmpCertificateDestination, certificateDestination, GroupID, 0644, 0755)
moveFile(tmpFullchainDestination, fullchainDestination, GroupID, 0644, 0755)
moveFile(tmpCaDestination, caDestination, GroupID, 0644, 0755)
moveFile(tmpKeyDestination, keyDestination, GroupID, 0640, 0750)