diff --git a/README.md b/README.md index f657114..8c940fc 100644 --- a/README.md +++ b/README.md @@ -88,3 +88,32 @@ In case someone needs to get removed from your OpenVPN there is also a revoke: ``` To have revokes being executed by OpenVPN you need to periodically update the CRL file OpenVPN reads. For my solution see the `living-example` in the `example` folder. + +## Using Tls Auth +OpenVPN highly recommends using TLS Authentication hardening, see https://community.openvpn.net/openvpn/wiki/GettingStartedwithOVPN#TLSAuthentication + +This requires the use of a pre-shared key, if you want to use it you will first need to generate tls auth key and then upload it to vault. + +```bash +openvpn --genkey --secret openvpn.key +vault kv put secret/ovpn key=@openvpn.key +``` + +In the above example we call the secret "ovpn" but you can call it anything you want, so long as its a known value. +The key must be placed into both the client and server configurations and must match, edit both config templates to include a section as shown below + +``` + +{{ .TlsAuth }} + +``` + +Now run vault-openvpn passing in the name of the secret that holds our key, e.g. + +```bash +# for the server config +vault-openvpn --auto-revoke --ovpn-key ovpn --pki-mountpoint luzifer_io server edda.openvpn.luzifer.io + +# and for the client config +vault-openvpn --auto-revoke --ovpn-key ovpn --pki-mountpoint luzifer_io client workwork01.openvpn.luzifer.io +``` diff --git a/main.go b/main.go index 0e5b071..98592d1 100644 --- a/main.go +++ b/main.go @@ -45,6 +45,7 @@ var ( AutoRevoke bool `flag:"auto-revoke" vardefault:"auto-revoke" description:"Automatically revoke older certificates for this FQDN"` CertTTL time.Duration `flag:"ttl" vardefault:"ttl" description:"Set the TTL for this certificate"` + OvpnKey string `flag:"ovpn-key" vardefault:"secret/ovpn" description:"Specify a secret name that holds an OpenVPN shared key"` LogLevel string `flag:"log-level" vardefault:"log-level" description:"Log level to use (debug, info, warning, error)"` Sort string `flag:"sort" vardefault:"sort" description:"How to sort list output (fqdn, issuedate, expiredate)"` @@ -71,6 +72,7 @@ type templateVars struct { CertAuthority string Certificate string PrivateKey string + TlsAuth string } type listCertificatesTableRow struct { @@ -287,6 +289,13 @@ func generateCertificateConfig(tplName, fqdn string) error { tplv.CertAuthority = caCert + if cfg.OvpnKey != "" { + tplv.TlsAuth, err = fetchOvpnKey(fqdn) + if err != nil { + return fmt.Errorf("Could not fetch TlsAuth key: %s", err) + } + } + if err := renderTemplate(tplName, tplv); err != nil { return fmt.Errorf("Could not render configuration: %s", err) } @@ -412,6 +421,20 @@ func getCACert() (string, error) { return cs.Data["certificate"].(string), nil } +func fetchOvpnKey(fqdn string) (string, error) { + path := strings.Join([]string{"secret", "data", strings.Trim(cfg.OvpnKey, "/")}, "/") + secret, err := client.Logical().Read(path) + + if err != nil { + return "", err + } + + if secret == nil { + return "", errors.New("Got no data from backend") + } + return secret.Data["data"].(map[string]interface {})["key"].(string), nil +} + func generateCertificate(fqdn string) (*templateVars, error) { path := strings.Join([]string{strings.Trim(cfg.PKIMountPoint, "/"), "issue", cfg.PKIRole}, "/") secret, err := client.Logical().Write(path, map[string]interface{}{