Skip to content

Commit

Permalink
Merge pull request #9 from bnhf/develop
Browse files Browse the repository at this point in the history
Certificate revocation and removal
  • Loading branch information
bnhf authored Dec 7, 2022
2 parents e2fd8ef + 72f9658 commit e42023b
Show file tree
Hide file tree
Showing 11 changed files with 256 additions and 30 deletions.
5 changes: 4 additions & 1 deletion build/Multi-arch.dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
FROM bnhf/go-beego-bee-git
WORKDIR /go/src/github.com/bnhf

RUN git clone https://github.com/bnhf/pivpn-tap-web-ui
# Uncomment for a multi-arch buildx of the main branch
# RUN git clone https://github.com/bnhf/pivpn-tap-web-ui
# Uncomment for a multi-arch buildx of the develop branch
RUN git clone -b develop --single-branch https://github.com/bnhf/pivpn-tap-web-ui
WORKDIR /go/src/github.com/bnhf/pivpn-tap-web-ui
RUN go mod tidy && \
bee pack -exr='^vendor|^data.db|^build|^README.md|^docs'
Expand Down
7 changes: 6 additions & 1 deletion build/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ cp -f ../$PKGFILE ./
# docker build -t bnhf/pivpn-tap-web-ui:manifest-armv7 --build-arg ARCH=armv7/ .

# Multi-arch the buildx way -- just use the command below, don't run the script
docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -f build/Multi-arch.dockerfile -t bnhf/pivpn-tap-web-ui . --push
# docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -f build/Multi-arch.dockerfile -t bnhf/pivpn-tap-web-ui . --push

# Multi-arch development build
# docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -f build/Multi-arch.dockerfile -t bnhf/tap-development . --push

# Single-arch (amd64) development build
docker buildx build --platform linux/amd64 -f build/Multi-arch.dockerfile -t bnhf/tap-development . --push --no-cache
rm -f $PKGFILE
2 changes: 1 addition & 1 deletion conf/openvpn-server-config.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ auth {{ .Auth }}

persist-key
persist-tun
crl-verify /etc/openvpn/crl.pem
crl-verify /etc/openvpn/easy-rsa/pki/crl.pem

# status /etc/openvpn/openvpn-status.log 20
# status-version 3
Expand Down
28 changes: 28 additions & 0 deletions controllers/certificates.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,34 @@ func (c *CertificatesController) Download() {
}
}

// @router /certificates/revoke/:key/:serial [get]
func (c *CertificatesController) Revoke() {
c.TplName = "certificates.html"
name := c.GetString(":key")
serial := c.GetString(":serial")
err := lib.RevokeCertificate(name, serial)
if err != nil {
beego.Error(err)
//flash.Error(err.Error())
//flash.Store(&c.Controller)
}
c.showCerts()
}

// @router /certificates/remove/:key/:serial [get]
func (c *CertificatesController) Remove() {
c.TplName = "certificates.html"
name := c.GetString(":key")
serial := c.GetString(":serial")
err := lib.RemoveCertificate(name, serial)
if err != nil {
beego.Error(err)
//flash.Error(err.Error())
//flash.Store(&c.Controller)
}
c.showCerts()
}

func addFileToZip(zw *zip.Writer, path string) error {
header := &zip.FileHeader{
Name: filepath.Base(path),
Expand Down
149 changes: 138 additions & 11 deletions lib/certificates.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package lib

import (
"bufio"
"fmt"
"io/ioutil"
"os"
"os/exec"
"strings"
"time"
Expand Down Expand Up @@ -33,7 +35,7 @@ type Details struct {
}

func ReadCerts(path string) ([]*Cert, error) {
certs := make([]*Cert, 0, 0)
certs := make([]*Cert, 0)
text, err := ioutil.ReadFile(path)
if err != nil {
return certs, err
Expand Down Expand Up @@ -95,18 +97,143 @@ func trim(s string) string {

func CreateCertificate(name string) error {
rsaPath := "/etc/openvpn/easy-rsa"
// varsPath := models.GlobalCfg.OVConfigPath + "easy-rsa/vars"
cmd := exec.Command("/bin/bash", "-c",
fmt.Sprintf(
// "source %s &&"+
"export KEY_NAME=%s &&"+
"%s/easyrsa --batch build-client-full %s nopass", name, rsaPath, name))
cmd.Dir = models.GlobalCfg.OVConfigPath
output, err := cmd.CombinedOutput()
// // varsPath := models.GlobalCfg.OVConfigPath + "easy-rsa/vars"
// cmd := exec.Command("/bin/bash", "-c",
// fmt.Sprintf(
// // "source %s &&"+
// "export KEY_NAME=%s &&"+
// "%s/easyrsa --batch build-client-full %s nopass", name, rsaPath, name))
// cmd.Dir = models.GlobalCfg.OVConfigPath
// output, err := cmd.CombinedOutput()
path := models.GlobalCfg.OVConfigPath + "easy-rsa/pki/index.txt"
certs, err := ReadCerts(path)
if err != nil {
beego.Debug(string(output))
// beego.Debug(string(output))
beego.Error(err)
return err
// return err
}
Dump(certs)
exists := false
for _, v := range certs {
if v.Details.Name == name {
exists = true
}
}
if !exists {
cmd := exec.Command("/bin/bash", "-c",
fmt.Sprintf(
// "source %s &&"+
"export KEY_NAME=%s &&"+
"%s/easyrsa --batch build-client-full %s nopass", name, rsaPath, name))
cmd.Dir = models.GlobalCfg.OVConfigPath
output, err := cmd.CombinedOutput()
if err != nil {
beego.Debug(string(output))
beego.Error(err)
return err
}
return nil
}
return nil
}

func RevokeCertificate(name string, serial string) error {
path := models.GlobalCfg.OVConfigPath + "easy-rsa/pki/index.txt"
certs, err := ReadCerts(path)
if err != nil {
beego.Error(err)
}
Dump(certs)
for _, v := range certs {
if v.Details.Name == name {
rsaPath := "/etc/openvpn/easy-rsa/"
// varsPath := models.GlobalCfg.OVConfigPath + "keys/vars"

cmd := exec.Command("/bin/bash", "-c",
fmt.Sprintf(
// "source %s &&"+
"%s/easyrsa --batch revoke %s &&"+
"%s/easyrsa gen-crl &&"+
"cp %s/pki/crl.pem %s/..",
rsaPath, name, rsaPath, rsaPath, rsaPath))
cmd.Dir = models.GlobalCfg.OVConfigPath
output, err2 := cmd.CombinedOutput()
if err2 != nil {
beego.Debug(string(output))
beego.Error(err2)
return err2
}
return nil
}
}
return nil //do nothing for now
}

func RemoveCertificate(name string, serial string) error {
path := models.GlobalCfg.OVConfigPath + "easy-rsa/pki/index.txt"
certs, err := ReadCerts(path)
if err != nil {
beego.Error(err)
}
Dump(certs)
for _, v := range certs {
if v.Details.Name == name {
keyDb := models.GlobalCfg.OVConfigPath + "easy-rsa/pki/index.txt"
/*file, err := os.Open(keyDb)
if err != nil {
beego.Error(err)
return err
}*/
_ = os.Remove(models.GlobalCfg.OVConfigPath + "easy-rsa/pki/certs_by_serial/" + serial + ".pem")
_ = os.Remove(models.GlobalCfg.OVConfigPath + "easy-rsa/pki/issued/" + name + ".crt")
_ = os.Remove(models.GlobalCfg.OVConfigPath + "easy-rsa/pki/private/" + name + ".key")
_ = os.Remove(models.GlobalCfg.OVConfigPath + "easy-rsa/pki/" + name + ".ovpn")
_ = os.Remove(models.GlobalCfg.OVConfigPath + "easy-rsa/pki/" + name + ".conf")
lines, err := readLines(keyDb)
if err != nil {
beego.Error(err)
return err
}
newkeyDb := ""
for _, line := range lines {
if !checkSubstrings(line, name, "\t"+serial) {
newkeyDb += line + "\n"
}
}
err = ioutil.WriteFile(keyDb, []byte(newkeyDb), 0644)
if err != nil {
beego.Error(err)
return err
}
return nil
}
}
return nil //do nothing for now
}

func readLines(path string) ([]string, error) {
file, err := os.Open(path)
if err != nil {
return nil, err
}
defer file.Close()
var lines []string
scanner := bufio.NewScanner(file)
for scanner.Scan() {
lines = append(lines, scanner.Text())
}
return lines, scanner.Err()
}

func checkSubstrings(str string, subs ...string) bool {
matches := 0
isCompleteMatch := true
for _, sub := range subs {
if strings.Contains(str, sub) {
matches += 1
} else {
isCompleteMatch = false
}
}
return isCompleteMatch
}
4 changes: 2 additions & 2 deletions models/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ func createDefaultSettings() {
Profile: "default",
MIAddress: "openvpn:2080",
MINetwork: "tcp",
ServerAddress: "127.0.0.1",
ServerAddress: "myopenvpnserver.duckdns.org",
OVConfigPath: "/etc/openvpn/",
}
o := orm.NewOrm()
Expand Down Expand Up @@ -106,7 +106,7 @@ func createDefaultOVConfig() {
Dh: "none",
Keepalive: "10 120",
IfconfigPoolPersist: "ipp.txt",
Management: "0.0.0.0 2080",
Management: "172.17.0.1 2080",
CCEncryption: "easy-rsa/pki/ta.key",
Server: "server-bridge 192.168.1.100 255.255.255.0 192.168.1.2 192.168.1.8",
Ca: "easy-rsa/pki/ca.crt",
Expand Down
18 changes: 18 additions & 0 deletions routers/commentsRouter_controllers.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,24 @@ func init() {
Filters: nil,
Params: nil})

beego.GlobalControllerRouter["github.com/bnhf/pivpn-tap-web-ui/controllers:CertificatesController"] = append(beego.GlobalControllerRouter["github.com/bnhf/pivpn-tap-web-ui/controllers:CertificatesController"],
beego.ControllerComments{
Method: "Remove",
Router: "/certificates/remove/:key/:serial",
AllowHTTPMethods: []string{"get"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})

beego.GlobalControllerRouter["github.com/bnhf/pivpn-tap-web-ui/controllers:CertificatesController"] = append(beego.GlobalControllerRouter["github.com/bnhf/pivpn-tap-web-ui/controllers:CertificatesController"],
beego.ControllerComments{
Method: "Revoke",
Router: "/certificates/revoke/:key/:serial",
AllowHTTPMethods: []string{"get"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})

beego.GlobalControllerRouter["github.com/bnhf/pivpn-tap-web-ui/controllers:CertificatesController"] = append(beego.GlobalControllerRouter["github.com/bnhf/pivpn-tap-web-ui/controllers:CertificatesController"],
beego.ControllerComments{
Method: "DownloadSingleConfig",
Expand Down
54 changes: 44 additions & 10 deletions views/certificates.html
Original file line number Diff line number Diff line change
Expand Up @@ -27,37 +27,71 @@ <h3 class="box-title">Clients certificates</h3>
<th>Revocation</th>
<th>Serial</th>
<th>Details</th>
<th></th>
<th>Manage</th>
<th>Certificates</th>
</tr>
</thead>
<tbody>

{{range .certificates}}
{{ if ne .Details.CN "server"}}
<tr>
{{if eq .Revocation ""}}
<td>
<a class="btn btn-success btn-sm" href="{{urlfor "CertificatesController.Download" ":key" .Details.Name}}">
{{ .Details.Name }}.zip
</a>
<a class="btn btn-success btn-sm" href="{{urlfor "CertificatesController.DownloadSingleConfig" ":key" .Details.Name}}">
{{ .Details.Name }}.ovpn
</a>
</td>
{{else}}
<td>
<a href="{{urlfor "CertificatesController.Download" ":key" .Details.Name}}">
<a class="btn btn-danger btn-sm" href="{{urlfor "CertificatesController.Download" ":key" .Details.Name}}">
{{ .Details.Name }}.zip
</a>
<a href="{{urlfor "CertificatesController.DownloadSingleConfig" ":key" .Details.Name}}">
<a class="btn btn-danger btn-sm" href="{{urlfor "CertificatesController.DownloadSingleConfig" ":key" .Details.Name}}">
{{ .Details.Name }}.ovpn
</a>
</td>
{{end}}
<td>{{ .EntryType }}</td>
<td>{{ dateformat .ExpirationT "2006-01-02 15:04"}}</td>
{{if eq .Revocation ""}}
<td><span class="label label-success">{{ dateformat .ExpirationT "2006-01-02 15:04"}}</span></td>
{{else}}
<td><span class="label label-danger">{{ dateformat .ExpirationT "2006-01-02 15:04"}}</span></td>
{{end}}
{{if eq .Revocation ""}}
<td></td>
{{else}}
<td>{{ dateformat .RevocationT "2006-01-02 15:04"}}</td>
<td><span class="label label-danger">{{ dateformat .RevocationT "2006-01-02 15:04"}}</span></td>
{{end}}
<td>{{ .Serial }}</td>
<td>
<span class="label label-warning">Country: {{ .Details.Country }}</span>
<span class="label label-warning">CN: {{ .Details.Name }}</span>
<span class="label label-warning">Email: {{ .Details.Email }}</span>
<span class="label label-info">Country: {{ .Details.Country }}</span>
<span class="label label-info">CN: {{ .Details.Name }}</span>
<span class="label label-info">Email: {{ .Details.Email }}</span>
</td>
{{if eq .Revocation ""}}
<td>
<a class="btn btn-warning btn-sm" href="{{urlfor "CertificatesController.Revoke" ":key" .Details.Name ":serial" .Serial}}">
Revoke
</a>
</td>
{{else}}
<td></td>
{{end}}
{{if eq .Revocation ""}}
<td></td>
{{else}}
<td>
<a class="btn btn-danger btn-sm" href="{{urlfor "CertificatesController.Remove" ":key" .Details.Name ":serial" .Serial}}">
Remove
</a>
</td>
{{end}}
</tr>
{{ end }}
{{ end }}
{{end}}

</tbody>
Expand Down Expand Up @@ -104,4 +138,4 @@ <h3 class="box-title">Create a new certificate</h3>
</div>
</form>
</div>
{{end}}
{{end}}
1 change: 1 addition & 0 deletions views/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

{{define "head"}}
<title>OpenVPNAdmin</title>
<meta http-equiv="refresh" content="20">
{{end}}

{{define "body"}}
Expand Down
Loading

0 comments on commit e42023b

Please sign in to comment.