1
0
Fork 0
mirror of https://github.com/Luzifer/vault-openvpn.git synced 2024-12-25 22:31:20 +00:00

Add "list" command

Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
Knut Ahlers 2017-05-04 11:06:33 +02:00
parent dff4ca7853
commit 1a8767d852
Signed by: luzifer
GPG key ID: DC2729FDD34BE99E

110
main.go
View file

@ -12,17 +12,20 @@ import (
"text/template" "text/template"
"time" "time"
"github.com/Luzifer/go_helpers/str"
"github.com/Luzifer/rconfig" "github.com/Luzifer/rconfig"
log "github.com/Sirupsen/logrus" log "github.com/Sirupsen/logrus"
"github.com/hashicorp/vault/api" "github.com/hashicorp/vault/api"
homedir "github.com/mitchellh/go-homedir" homedir "github.com/mitchellh/go-homedir"
"github.com/olekukonko/tablewriter"
) )
const ( const (
actionRevoke = "revoke" actionList = "list"
actionMakeClientConfig = "client" actionMakeClientConfig = "client"
actionMakeServerConfig = "server" actionMakeServerConfig = "server"
actionRevoke = "revoke"
dateFormat = "2006-01-02 15:04:05 -0700"
) )
var ( var (
@ -91,17 +94,16 @@ func init() {
} }
func main() { func main() {
if len(rconfig.Args()) != 3 { if len(rconfig.Args()) < 2 {
fmt.Println("Usage: vault-openvpn [options] <action> <FQDN>") fmt.Println("Usage: vault-openvpn [options] <action> <FQDN>")
fmt.Println(" actions: client / server / revoke") fmt.Println(" actions: client / server / list / revoke")
os.Exit(1) os.Exit(1)
} }
action := rconfig.Args()[1] action := rconfig.Args()[1]
fqdn := rconfig.Args()[2] fqdn := ""
if len(rconfig.Args()) == 3 {
if !str.StringInSlice(action, []string{actionRevoke, actionMakeClientConfig, actionMakeServerConfig}) { fqdn = rconfig.Args()[2]
log.Fatalf("Unknown action: %s", action)
} }
var err error var err error
@ -117,36 +119,100 @@ func main() {
client.SetToken(cfg.VaultToken) client.SetToken(cfg.VaultToken)
if cfg.AutoRevoke || action == actionRevoke { switch action {
case actionRevoke:
if err := revokeOlderCertificate(fqdn); err != nil { if err := revokeOlderCertificate(fqdn); err != nil {
log.Fatalf("Could not revoke certificate: %s", err) log.Fatalf("Could not revoke certificate: %s", err)
} }
case actionMakeClientConfig:
if err := generateCertificateConfig("client.conf", fqdn); err != nil {
log.Fatalf("Unable to generate config file: %s", err)
}
case actionMakeServerConfig:
if err := generateCertificateConfig("server.conf", fqdn); err != nil {
log.Fatalf("Unable to generate config file: %s", err)
}
case actionList:
if err := listCertificates(); err != nil {
log.Fatalf("Unable to list certificates: %s", err)
}
default:
log.Fatalf("Unknown action: %s", action)
}
}
func listCertificates() error {
table := tablewriter.NewWriter(os.Stdout)
table.SetHeader([]string{"FQDN", "Not Before", "Not After", "Serial"})
table.SetBorder(false)
path := strings.Join([]string{strings.Trim(cfg.PKIMountPoint, "/"), "certs"}, "/")
secret, err := client.Logical().List(path)
if err != nil {
return err
} }
if action != actionMakeClientConfig && action != actionMakeServerConfig { if secret.Data == nil {
return return errors.New("Got no data from backend")
} }
tplName := "client.conf" for _, serial := range secret.Data["keys"].([]interface{}) {
if action == actionMakeServerConfig { path := strings.Join([]string{strings.Trim(cfg.PKIMountPoint, "/"), "cert", serial.(string)}, "/")
tplName = "server.conf" cs, err := client.Logical().Read(path)
if err != nil {
return errors.New("Unable to read certificate: " + err.Error())
}
cert, err := parseCertificate(cs.Data["certificate"].(string))
if err != nil {
return err
}
if revokationTime, ok := cs.Data["revocation_time"]; ok {
rt, err := revokationTime.(json.Number).Int64()
if err == nil && rt < time.Now().Unix() && rt > 0 {
// Don't display revoked certs
continue
}
}
table.Append([]string{
cert.Subject.CommonName,
cert.NotBefore.Format(dateFormat),
cert.NotAfter.Format(dateFormat),
serial.(string),
})
}
table.Render()
return nil
}
func generateCertificateConfig(tplName, fqdn string) error {
if cfg.AutoRevoke {
if err := revokeOlderCertificate(fqdn); err != nil {
return fmt.Errorf("Could not revoke certificate: %s", err)
}
} }
caCert, err := getCACert() caCert, err := getCACert()
if err != nil { if err != nil {
log.Fatalf("Could not load CA certificate: %s", err) return fmt.Errorf("Could not load CA certificate: %s", err)
} }
tplv, err := generateCertificate(fqdn) tplv, err := generateCertificate(fqdn)
if err != nil { if err != nil {
log.Fatalf("Could not generate new certificate: %s", err) return fmt.Errorf("Could not generate new certificate: %s", err)
} }
tplv.CertAuthority = caCert tplv.CertAuthority = caCert
if err := renderTemplate(tplName, tplv); err != nil { if err := renderTemplate(tplName, tplv); err != nil {
log.Fatalf("Could not render configuration: %s", err) return fmt.Errorf("Could not render configuration: %s", err)
} }
return nil
} }
func renderTemplate(tplName string, tplv *templateVars) error { func renderTemplate(tplName string, tplv *templateVars) error {
@ -218,9 +284,13 @@ func revokeOlderCertificate(fqdn string) error {
return nil return nil
} }
func commonNameFromCertificate(pemString string) (string, error) { func parseCertificate(pemString string) (*x509.Certificate, error) {
data, _ := pem.Decode([]byte(pemString)) data, _ := pem.Decode([]byte(pemString))
cert, err := x509.ParseCertificate(data.Bytes) return x509.ParseCertificate(data.Bytes)
}
func commonNameFromCertificate(pemString string) (string, error) {
cert, err := parseCertificate(pemString)
if err != nil { if err != nil {
return "", err return "", err
} }
@ -253,7 +323,7 @@ func generateCertificate(fqdn string) (*templateVars, error) {
return nil, errors.New("Got no data from backend") return nil, errors.New("Got no data from backend")
} }
log.WithField(log.Fields{ log.WithFields(log.Fields{
"cn": fqdn, "cn": fqdn,
"serial": secret.Data["serial_number"].(string), "serial": secret.Data["serial_number"].(string),
}).Info("Generated new certificate") }).Info("Generated new certificate")