1

Golang toddler here, so I'd imagine I'm missing something obvious. After experimenting for a couple of days though, I decided to reach out for some help. :-)

The code I'm posting is working, except for the situation when a user requests a new client certificate/key package be created (this is an OpenVPN server administrative WebUI), when a client of the same name already exists. And even in that case, no new client package is created, but an incorrect alert message is displayed indicating that it has been.

I know I need to rework the controller to display different alert banners, based on whether the name exists or not. However, I'm stuck on getting anything besides "nil" back from the library.

The Golang controller code is as follows:

    func (c *CertificatesController) Post() {
    c.TplName = "certificates.html"
    flash := beego.NewFlash()

    cParams := NewCertParams{}
    if err := c.ParseForm(&cParams); err != nil {
        beego.Error(err)
        flash.Error(err.Error())
        flash.Store(&c.Controller)
    } else {
        if vMap := validateCertParams(cParams); vMap != nil {
            c.Data["validation"] = vMap
        } else {
            if err := lib.CreateCertificate(cParams.Name, cParams.Passphrase); err != nil {
                beego.Error(err)
                flash.Error(err.Error())
                flash.Store(&c.Controller)
            } else {
                fmt.Println(err)
                flash.Success("Certificate for the name \"" + cParams.Name + "\" created")
                flash.Store(&c.Controller)
            }
        }
    }
    c.showCerts()
}

And the library function that's called via lib.CreateCertificate:

    func CreateCertificate(name string, passphrase string) error {
    rsaPath := models.GlobalCfg.OVConfigPath + "easy-rsa"
    rsaIndex := models.GlobalCfg.OVConfigPath + "easy-rsa/pki/index.txt"
    pass := false
    if passphrase != "" {
        pass = true
    }
    certs, err := ReadCerts(rsaIndex)
    if err != nil {
        //      beego.Debug(string(output))
        beego.Error(err)
        //      return err
    }
    Dump(certs)
    exists := false
    for _, v := range certs {
        if v.Details.Name == name {
            exists = true
        }
    }
    if !exists && !pass {
        cmd := exec.Command("/bin/bash", "-c",
            fmt.Sprintf(
                "%s/easyrsa --batch build-client-full %s nopass",
                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
    }
    if !exists && pass {
        cmd := exec.Command("/bin/bash", "-c",
            fmt.Sprintf(
                "%s/easyrsa --passout=pass:%s build-client-full %s",
                rsaPath, passphrase, name))
        cmd.Dir = models.GlobalCfg.OVConfigPath
        output, err := cmd.CombinedOutput()
        if err != nil {
            beego.Debug(string(output))
            beego.Error(err)
            return err
        }
        return nil
    }
    if exists {
        return err
    }
    return err
}

I've gone so far as changing every return in the library to err, and inserting a fmt.Println(err) in the controller's second "else" statement, but nil is all I ever get back.

bnhf
  • 41
  • 4
  • Try to focus your question a bit. Which library are you having trouble with? Which functions are not working as you expect? – Hymns For Disco Dec 14 '22 at 02:22
  • So, when the controller calls a function in an internal library via `if err := lib.CreateCertificate(cParams.Name, cParams.Passphrase); err != nil` should I be able to get an error returned (that I can act upon in the controller) to display one of two messages `flash.Success("Certificate for the name \"" + cParams.Name + "\" created")` or `flash.Error("Certificate for the name \"" + cParams.Name + "\" already exists")` based on the value of the variable `exists` in the library function lib.CreateCertificate? – bnhf Dec 14 '22 at 02:58
  • If you don't understand what's going on inside a function, the most reliable way to figure it out is to run with a debugger and step through it slowly. – Hymns For Disco Dec 14 '22 at 04:24

1 Answers1

0

So, I was able to figure out how to deal with this. A bit more googling and I found a post that was at least adjacent to what I was trying to accomplish. In the end, I only needed to add/change 3 lines in my certificates library. I needed to import the "errors" library, add a custom error in the form newError := errors.New("Error! There is already a valid or invalid certificate for that name") and change only the last return to return newError. I definitely learned a thing-or-two about how Go handles errors!

Here's the updated code for the certificates library:

    func CreateCertificate(name string, passphrase string) error {
    rsaPath := models.GlobalCfg.OVConfigPath + "easy-rsa"
    rsaIndex := models.GlobalCfg.OVConfigPath + "easy-rsa/pki/index.txt"
    pass := false
    newError := errors.New("Error! There is already a valid or invalid certificate for that name")
    if passphrase != "" {
        pass = true
    }
    certs, err := ReadCerts(rsaIndex)
    if err != nil {
        //      beego.Debug(string(output))
        beego.Error(err)
        //      return err
    }
    Dump(certs)
    exists := false
    for _, v := range certs {
        if v.Details.Name == name {
            exists = true
        }
    }
    if !exists && !pass {
        cmd := exec.Command("/bin/bash", "-c",
            fmt.Sprintf(
                "%s/easyrsa --batch build-client-full %s nopass",
                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
    }
    if !exists && pass {
        cmd := exec.Command("/bin/bash", "-c",
            fmt.Sprintf(
                "%s/easyrsa --passout=pass:%s build-client-full %s",
                rsaPath, passphrase, 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 newError
}

Now, if I try to add an OpenVPN client with a name that already exists:

enter image description here

bnhf
  • 41
  • 4