Skip to content

Commit

Permalink
Added PKCS12 support.
Browse files Browse the repository at this point in the history
  • Loading branch information
jdevelop committed Dec 31, 2019
1 parent d5412ec commit a1da3bb
Show file tree
Hide file tree
Showing 9 changed files with 250 additions and 36 deletions.
4 changes: 0 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,3 @@ and the example command line would be:
```
ezovpn -d /etc/openvpn import -i /etc/openvpn/client.conf
```

## Note

Doesn't work with PKCS12 bundles at the moment.
10 changes: 6 additions & 4 deletions app/ezovpn/cmds/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ var genFlags struct {
Host string
Port int
Certs struct {
CA string
Key string
Cert string
TA string
CA string
Key string
Cert string
TA string
PKCS12 string
}
}

Expand All @@ -40,6 +41,7 @@ var genCmd = &cobra.Command{
CertFile: genFlags.Certs.Cert,
KeyFile: genFlags.Certs.Key,
TAFile: genFlags.Certs.TA,
PKCS12: genFlags.Certs.PKCS12,
}, &ezovpn.VpnSpec{
Server: genFlags.Host,
Port: genFlags.Port,
Expand Down
22 changes: 22 additions & 0 deletions generate_vpn.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package ezovpn

import (
"encoding/base64"
"fmt"
"io"
"io/ioutil"
Expand All @@ -15,6 +16,7 @@ type ovpnData struct {
Cert string
Key string
TlsAuth *string
PKCS12 *string
RemoteHost string
RemotePort int
}
Expand All @@ -28,6 +30,7 @@ nobind
persist-key
persist-tun
comp-lzo
{{ if not .PKCS12 -}}
<ca>
{{.Ca -}}
</ca>
Expand All @@ -37,6 +40,11 @@ comp-lzo
<key>
{{.Key -}}
</key>
{{ else -}}
<pkcs12>
{{.PKCS12 -}}
</pkcs12>
{{ end -}}
{{if .TlsAuth -}}
key-direction 1
<tls-auth>
Expand All @@ -55,6 +63,8 @@ type FileSpec struct {
KeyFile string
// TLS Auth file name ( ta.key )
TAFile string
// PKCS12 File
PKCS12 string
}

// VpnSpec defines the VPN server
Expand Down Expand Up @@ -109,6 +119,18 @@ func GenerateVPNConfig(keysDir string, files *FileSpec, vpn *VpnSpec, out io.Wri
if err := readFileIn(path, &vd.Ca); err != nil {
return err
}
case files.PKCS12:
r, err := fetcher(path)
if err != nil {
return err
}
data, err := ioutil.ReadAll(r)
r.Close()
if err != nil {
return err
}
dataStr := formatBase64(base64.StdEncoding.EncodeToString(data))
vd.PKCS12 = &dataStr
case files.TAFile:
var v string
if err := readFileIn(path, &v); err != nil {
Expand Down
35 changes: 35 additions & 0 deletions generate_vpn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,25 @@ key-direction 1
TLS AUTH
DATA
KEY
</tls-auth>`
vpnRenderedPK12 = `client
dev tun
proto udp
remote vpn.server.here 1144
nobind
persist-key
persist-tun
comp-lzo
<pkcs12>
/deSj5Uh3JfIX2bhP0f+TNTIw1OZJgwK4aXTjOmuIjcz9YVmiRR1ZwWI59VWaGKxci/4er8ZN3YS
OIce8ddQqQ8Ub/CPeBCXrt04DhjTzB3x+4MZ+ZBGHHevySGWe9oHKcavfBZM1+kpg+qz8BbZDbYY
SRs=
</pkcs12>
key-direction 1
<tls-auth>
TLS AUTH
DATA
KEY
</tls-auth>`
)

Expand All @@ -50,3 +69,19 @@ func TestVPNGeneration(t *testing.T) {
require.Equal(t, vpnRendered, buf.String())

}

func TestVPNGenerationPKCS12(t *testing.T) {
var buf strings.Builder
require.Nil(t, GenerateVPNConfig("testdir", &FileSpec{
CAFile: "ca.crt",
CertFile: "ovpn.crt",
KeyFile: "ovpn.key",
TAFile: "tlsauth.key",
PKCS12: "pkcs12.p12",
}, &VpnSpec{
Server: "vpn.server.here",
Port: 1144,
}, &buf, DefaultCertFetcher))
require.Equal(t, vpnRenderedPK12, buf.String())

}
70 changes: 56 additions & 14 deletions import_vpn.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package ezovpn

import (
"bufio"
"encoding/base64"
"io"
"path/filepath"
"regexp"
Expand All @@ -18,31 +19,44 @@ const (
taOpen = "<tls-auth>\n"
taClose = "</tls-auth>\n"
taKey = "key-direction 1\n"
pkcsOpen = "<pkcs12>\n"
pkcsClose = "</pkcs12>\n"
)

const (
fileIdx = 7
)

var (
filesMatcher = regexp.MustCompile(`^\s*((ca)|(cert)|(key)|(tls-auth))\s+?(\S+)`)
crlf = []byte("\n")
filesMatcher = regexp.MustCompile(`^\s*((ca)|(cert)|(key)|(tls-auth)|(pkcs12))\s+?(\S+)`)
)

// readFileIn detects if the path to a certificate, defined in VPN config - is absolute or relative.
//
// Reads the file and wraps it's content into the corresponding tags.
func readFileIn(prefix, path string, w io.Writer, tagOpen, tagClose string, contentFetch CertFetcher) error {
func resolvePath(prefix, path string, cf CertFetcher) (io.ReadCloser, error) {
var r io.ReadCloser
if filepath.IsAbs(path) {
f, err := contentFetch(path)
f, err := cf(path)
if err != nil {
return err
return nil, err
}
r = f
} else {
f, err := contentFetch(filepath.Join(prefix, path))
f, err := cf(filepath.Join(prefix, path))
if err != nil {
return err
return nil, err
}
r = f
}
return r, nil
}

// readFileIn detects if the path to a certificate, defined in VPN config - is absolute or relative.
//
// Reads the file and wraps it's content into the corresponding tags.
func readFileIn(prefix, path string, w io.Writer, tagOpen, tagClose string, contentFetch CertFetcher) error {
r, err := resolvePath(prefix, path, contentFetch)
if err != nil {
return err
}
defer r.Close()
if _, err := w.Write([]byte(tagOpen)); err != nil {
return err
Expand Down Expand Up @@ -101,16 +115,44 @@ loop:
return err
}
if grps := filesMatcher.FindStringSubmatch(l); grps != nil {
filename := grps[fileIdx]
switch grps[1] {
case "ca":
readFileIn(pathPrefix, grps[6], w, caOpen, caClose, contentFetch)
readFileIn(pathPrefix, filename, w, caOpen, caClose, contentFetch)
case "cert":
readFileIn(pathPrefix, grps[6], w, certOpen, certClose, contentFetch)
readFileIn(pathPrefix, filename, w, certOpen, certClose, contentFetch)
case "key":
readFileIn(pathPrefix, grps[6], w, keyOpen, keyClose, contentFetch)
readFileIn(pathPrefix, filename, w, keyOpen, keyClose, contentFetch)
case "tls-auth":
w.Write([]byte(taKey))
readFileIn(pathPrefix, grps[6], w, taOpen, taClose, contentFetch)
readFileIn(pathPrefix, filename, w, taOpen, taClose, contentFetch)
case "pkcs12":
var (
s strings.Builder
)
r, err := resolvePath(pathPrefix, filename, contentFetch)
if err != nil {
return err
}
enc := base64.NewEncoder(base64.StdEncoding, &s)
if _, err := io.Copy(enc, r); err != nil {
return err
}
if err := r.Close(); err != nil {
return err
}
if err := enc.Close(); err != nil {
return err
}
if _, err := w.Write([]byte(pkcsOpen)); err != nil {
return err
}
if _, err := w.Write([]byte(formatBase64(s.String()))); err != nil {
return err
}
if _, err := w.Write([]byte(pkcsClose)); err != nil {
return err
}
default:
w.Write([]byte(l))
w.Write(crlf)
Expand Down
100 changes: 87 additions & 13 deletions import_vpn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package ezovpn
import (
"bytes"
"io"
"os"
"strings"
"testing"

Expand All @@ -24,14 +25,15 @@ func TestRegexResolver(t *testing.T) {
{"cert client.crt ", true, "cert", "client.crt"},
{"tls-auth ta.key 1", true, "tls-auth", "ta.key"},
{"#tls-auth ta.key 1", false, "tls-auth", "ta.key"},
{" pkcs12 /etc/openvpn/client.p12", true, "pkcs12", "/etc/openvpn/client.p12"},
}

for _, test := range testData {
arr := filesMatcher.FindStringSubmatch(test.line)
if test.match {
require.NotNil(t, arr, test)
require.Equal(t, arr[1], test.key, test)
require.Equal(t, arr[6], test.value, test)
require.Equal(t, arr[fileIdx], test.value, test)
} else {
require.Nil(t, arr, test)
}
Expand Down Expand Up @@ -103,31 +105,103 @@ cipher AES-256-CBC
comp-lzo
# Silence repeating messages
;mute 20
`
sourceFilePkcs12 = `# Specify that we are a client and that we
# will be pulling certain config file directives
# from the server.
client
;dev tap
dev tun
;dev-node MyTap
proto udp
remote my-server-1 1194
;remote-random
resolv-retry infinite
# Downgrade privileges after initialization (non-Windows only)
;user nobody
;group nobody
# Try to preserve some state across restarts.
persist-key
persist-tun
pkcs12 pkcs12.p12
tls-auth ta.key 1
cipher AES-256-CBC
comp-lzo
# Silence repeating messages
;mute 20`
targetFilePkcs12 = `# Specify that we are a client and that we
# will be pulling certain config file directives
# from the server.
client
;dev tap
dev tun
;dev-node MyTap
proto udp
remote my-server-1 1194
;remote-random
resolv-retry infinite
# Downgrade privileges after initialization (non-Windows only)
;user nobody
;group nobody
# Try to preserve some state across restarts.
persist-key
persist-tun
<pkcs12>
/deSj5Uh3JfIX2bhP0f+TNTIw1OZJgwK4aXTjOmuIjcz9YVmiRR1ZwWI59VWaGKxci/4er8ZN3YS
OIce8ddQqQ8Ub/CPeBCXrt04DhjTzB3x+4MZ+ZBGHHevySGWe9oHKcavfBZM1+kpg+qz8BbZDbYY
SRs=
</pkcs12>
key-direction 1
<tls-auth>
AUTH PACKET
DATA
</tls-auth>
cipher AES-256-CBC
comp-lzo
# Silence repeating messages
;mute 20
`
)

pkcsFileData, err := os.Open("./testdir/pkcs12.p12")
require.Nil(t, err)

defer pkcsFileData.Close()

var (
ca = stringCloser{strings.NewReader("CA cert here\n1234\n")}
cert = stringCloser{strings.NewReader("0000000\n1111111\n")}
key = stringCloser{strings.NewReader("2222222222222222222\n2222233322222222222\n")}
ta = stringCloser{strings.NewReader("AUTH PACKET\nDATA\n")}
m mck
w bytes.Buffer
ca = stringCloser{strings.NewReader("CA cert here\n1234\n")}
cert = stringCloser{strings.NewReader("0000000\n1111111\n")}
key = stringCloser{strings.NewReader("2222222222222222222\n2222233322222222222\n")}
ta stringCloser
pkcs12 = pkcsFileData
m mck
w bytes.Buffer
)

m.On("Fetch", "/tmp/ca.crt").Once().Return(&ca, nil)
m.On("Fetch", "/tmp/client.crt").Once().Return(&cert, nil)
m.On("Fetch", "/tmp/client.key").Once().Return(&key, nil)
m.On("Fetch", "/tmp/ta.key").Once().Return(&ta, nil)

ImportVPNConfig("/tmp", strings.NewReader(sourceFile), &w, m.Fetch)
m.On("Fetch", "/tmp/ta.key").Twice().Return(&ta, nil).Run(func(args mock.Arguments) {
ta = stringCloser{strings.NewReader("AUTH PACKET\nDATA\n")}
})
m.On("Fetch", "/tmp/pkcs12.p12").Once().Return(pkcs12, nil)

for _, td := range []struct {
expected string
src string
}{
{expected: targetFile, src: sourceFile},
{expected: targetFilePkcs12, src: sourceFilePkcs12},
} {
w.Reset()
ImportVPNConfig("/tmp", strings.NewReader(td.src), &w, m.Fetch)
require.Equal(t, td.expected, w.String())
}
m.AssertExpectations(t)

require.Equal(t, targetFile, w.String())
}

type stringCloser struct {
*strings.Reader
io.Reader
}

func (s *stringCloser) Close() error {
Expand Down
Loading

0 comments on commit a1da3bb

Please sign in to comment.