Skip to content

Commit

Permalink
api: replace unit_name with unit_id
Browse files Browse the repository at this point in the history
Changes:

- unit_id is generated by the firewall and can't be modified
- unit_name is a label editable by the user
  • Loading branch information
gsanchietti committed Mar 11, 2024
1 parent 42e2c8c commit 21d36bf
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 55 deletions.
6 changes: 3 additions & 3 deletions api/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,10 @@ func main() {
units := api.Group("/units")
{
units.GET("", methods.GetUnits)
units.GET("/:unit_name", methods.GetUnit)
units.GET("/:unit_name/token", methods.GetToken)
units.GET("/:unit_id", methods.GetUnit)
units.GET("/:unit_id/token", methods.GetToken)
units.POST("", methods.AddUnit)
units.DELETE("/:unit_name", methods.DeleteUnit)
units.DELETE("/:unit_id", methods.DeleteUnit)
}
}

Expand Down
105 changes: 53 additions & 52 deletions api/methods/unit.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,11 @@ type LoginResponse struct {
}

type AddRequest struct {
UnitName string `json:"unit_name" binding:"required"`
UnitId string `json:"unit_id" binding:"required"`
}

type RegisterRequest struct {
UnitId string `json:"unit_id" binding:"required"`
UnitName string `json:"unit_name" binding:"required"`
Username string `json:"username" binding:"required"`
Password string `json:"password" binding:"required"`
Expand Down Expand Up @@ -112,7 +113,7 @@ func GetUnits(c *gin.Context) {

// compose result
result := gin.H{
"name": e.Name(),
"id": e.Name(),
"ipaddress": parts[1],
"netmask": parts[2],
"registered": true,
Expand All @@ -130,17 +131,17 @@ func GetUnits(c *gin.Context) {
}

// list units in waiting state
for name, _ := range global.WaitingList {
for id, _ := range global.WaitingList {
result := gin.H{
"name": name,
"id": id,
"ipaddress": "",
"netmask": "",
"registered": false,
}

// check if vpn data exists
if vpns[name] != nil {
result["vpn"] = vpns[name]
if vpns[id] != nil {
result["vpn"] = vpns[id]
} else {
result["vpn"] = gin.H{}
}
Expand All @@ -159,8 +160,8 @@ func GetUnits(c *gin.Context) {
}

func GetUnit(c *gin.Context) {
// get unit name
unitName := c.Param("unit_name")
// get unit id
UnitId := c.Param("unit_id")

// execute status command on openvpn socket
var lines []string
Expand All @@ -169,7 +170,7 @@ func GetUnit(c *gin.Context) {
// get only necessary lines
rawLines := strings.Split(outSocket, "\n")
for _, line := range rawLines {
if strings.HasPrefix(line, "CLIENT_LIST\t"+unitName) {
if strings.HasPrefix(line, "CLIENT_LIST\t"+UnitId) {
lines = append(lines, line)
}
}
Expand All @@ -194,7 +195,7 @@ func GetUnit(c *gin.Context) {
}

// read unit file
unitFile, err := os.ReadFile(configuration.Config.OpenVPNCCDDir + "/" + unitName)
unitFile, err := os.ReadFile(configuration.Config.OpenVPNCCDDir + "/" + UnitId)
if err != nil {
c.JSON(http.StatusBadRequest, structs.Map(response.StatusBadRequest{
Code: 400,
Expand All @@ -209,7 +210,7 @@ func GetUnit(c *gin.Context) {

// compose result
result := gin.H{
"name": unitName,
"id": UnitId,
"ipaddress": parts[1],
"netmask": parts[2],
"registered": true,
Expand All @@ -231,16 +232,16 @@ func GetUnit(c *gin.Context) {
}

func GetToken(c *gin.Context) {
// get unit name
unitName := c.Param("unit_name")
// get unit id
UnitId := c.Param("unit_id")

// read credentials
var credentials LoginRequest
body, err := ioutil.ReadFile(configuration.Config.CredentialsDir + "/" + unitName)
body, err := ioutil.ReadFile(configuration.Config.CredentialsDir + "/" + UnitId)
if err != nil {
c.JSON(http.StatusBadRequest, structs.Map(response.StatusBadRequest{
Code: 400,
Message: "cannot open credentials file for: " + unitName,
Message: "cannot open credentials file for: " + UnitId,
Data: err.Error(),
}))
return
Expand All @@ -250,14 +251,14 @@ func GetToken(c *gin.Context) {
json.Unmarshal(body, &credentials)

// compose request URL
postURL := configuration.Config.ProxyProtocol + configuration.Config.ProxyHost + ":" + configuration.Config.ProxyPort + "/" + unitName + configuration.Config.LoginEndpoint
postURL := configuration.Config.ProxyProtocol + configuration.Config.ProxyHost + ":" + configuration.Config.ProxyPort + "/" + UnitId + configuration.Config.LoginEndpoint

// create request action
r, err := http.NewRequest("POST", postURL, bytes.NewBuffer(body))
if err != nil {
c.JSON(http.StatusBadRequest, structs.Map(response.StatusBadRequest{
Code: 400,
Message: "cannot make request for: " + unitName,
Message: "cannot make request for: " + UnitId,
Data: err.Error(),
}))
return
Expand All @@ -272,7 +273,7 @@ func GetToken(c *gin.Context) {
if err != nil {
c.JSON(http.StatusBadRequest, structs.Map(response.StatusBadRequest{
Code: 400,
Message: "request failed for: " + unitName,
Message: "request failed for: " + UnitId,
Data: err.Error(),
}))
return
Expand All @@ -287,7 +288,7 @@ func GetToken(c *gin.Context) {
if err != nil {
c.JSON(http.StatusBadRequest, structs.Map(response.StatusBadRequest{
Code: 400,
Message: "cannot convert response to struct for: " + unitName,
Message: "cannot convert response to struct for: " + UnitId,
Data: err.Error(),
}))
return
Expand All @@ -297,7 +298,7 @@ func GetToken(c *gin.Context) {
if len(loginResponse.Token) == 0 {
c.JSON(http.StatusBadRequest, structs.Map(response.StatusBadRequest{
Code: 400,
Message: "invalid JWT token response for: " + unitName,
Message: "invalid JWT token response for: " + UnitId,
Data: "token is invalid",
}))
return
Expand Down Expand Up @@ -327,10 +328,10 @@ func AddUnit(c *gin.Context) {
}

// check duplicates
if _, err := os.Stat(configuration.Config.OpenVPNCCDDir + "/" + jsonRequest.UnitName); err == nil {
if _, err := os.Stat(configuration.Config.OpenVPNCCDDir + "/" + jsonRequest.UnitId); err == nil {
c.JSON(http.StatusConflict, structs.Map(response.StatusConflict{
Code: 409,
Message: "unit name duplicated",
Message: "duplicated unit id",
Data: "",
}))
return
Expand Down Expand Up @@ -382,68 +383,68 @@ func AddUnit(c *gin.Context) {
}

// generate certificate request
cmdGenerateGenReq := exec.Command(configuration.Config.EasyRSAPath, "gen-req", jsonRequest.UnitName, "nopass")
cmdGenerateGenReq := exec.Command(configuration.Config.EasyRSAPath, "gen-req", jsonRequest.UnitId, "nopass")
cmdGenerateGenReq.Env = append(os.Environ(),
"EASYRSA_BATCH=1",
"EASYRSA_REQ_CN="+jsonRequest.UnitName,
"EASYRSA_REQ_CN="+jsonRequest.UnitId,
"EASYRSA_PKI="+configuration.Config.OpenVPNPKIDir,
)
if err := cmdGenerateGenReq.Run(); err != nil {
c.JSON(http.StatusBadRequest, structs.Map(response.StatusBadRequest{
Code: 400,
Message: "cannot generate request certificate for: " + jsonRequest.UnitName,
Message: "cannot generate request certificate for: " + jsonRequest.UnitId,
Data: err.Error(),
}))
return
}

// generate certificate sign
cmdGenerateSignReq := exec.Command(configuration.Config.EasyRSAPath, "sign-req", "client", jsonRequest.UnitName)
cmdGenerateSignReq := exec.Command(configuration.Config.EasyRSAPath, "sign-req", "client", jsonRequest.UnitId)
cmdGenerateSignReq.Env = append(os.Environ(),
"EASYRSA_BATCH=1",
"EASYRSA_REQ_CN="+jsonRequest.UnitName,
"EASYRSA_REQ_CN="+jsonRequest.UnitId,
"EASYRSA_PKI="+configuration.Config.OpenVPNPKIDir,
)
if err := cmdGenerateSignReq.Run(); err != nil {
c.JSON(http.StatusBadRequest, structs.Map(response.StatusBadRequest{
Code: 400,
Message: "cannot sign request certificate for: " + jsonRequest.UnitName,
Message: "cannot sign request certificate for: " + jsonRequest.UnitId,
Data: err.Error(),
}))
return
}

// write conf
conf := "ifconfig-push " + freeIP + " " + configuration.Config.OpenVPNNetmask + "\n"
errWrite := os.WriteFile(configuration.Config.OpenVPNCCDDir+"/"+jsonRequest.UnitName, []byte(conf), 0644)
errWrite := os.WriteFile(configuration.Config.OpenVPNCCDDir+"/"+jsonRequest.UnitId, []byte(conf), 0644)
if errWrite != nil {
c.JSON(http.StatusBadRequest, structs.Map(response.StatusBadRequest{
Code: 400,
Message: "cannot write conf file for: " + jsonRequest.UnitName,
Message: "cannot write conf file for: " + jsonRequest.UnitId,
Data: errWrite.Error(),
}))
return
}

// get unit from waiting list and save credentials
waiter := global.WaitingList[jsonRequest.UnitName]
waiter := global.WaitingList[jsonRequest.UnitId]

// check if unit was in waiting list
if waiter != nil {
// write new credentials
newCredentials, _ := json.Marshal(waiter)
errSave := os.WriteFile(configuration.Config.CredentialsDir+"/"+jsonRequest.UnitName, newCredentials, 0644)
errSave := os.WriteFile(configuration.Config.CredentialsDir+"/"+jsonRequest.UnitId, newCredentials, 0644)
if errSave != nil {
c.JSON(http.StatusBadRequest, structs.Map(response.StatusBadRequest{
Code: 400,
Message: "cannot write waiter credentials file for: " + jsonRequest.UnitName,
Message: "cannot write waiter credentials file for: " + jsonRequest.UnitId,
Data: errSave.Error(),
}))
return
}

// remove element from waiting list
delete(global.WaitingList, jsonRequest.UnitName)
delete(global.WaitingList, jsonRequest.UnitId)
}

// return 200 OK with data
Expand All @@ -469,7 +470,7 @@ func RegisterUnit(c *gin.Context) {
}

// check openvpn conf exists
if _, err := os.Stat(configuration.Config.OpenVPNPKIDir + "/issued/" + jsonRequest.UnitName + ".crt"); err == nil {
if _, err := os.Stat(configuration.Config.OpenVPNPKIDir + "/issued/" + jsonRequest.UnitId + ".crt"); err == nil {
// read ca
ca, errCa := os.ReadFile(configuration.Config.OpenVPNPKIDir + "/" + "ca.crt")
caS := strings.TrimSpace(string(ca[:]))
Expand All @@ -485,7 +486,7 @@ func RegisterUnit(c *gin.Context) {
}

// read cert
crt, errCrt := os.ReadFile(configuration.Config.OpenVPNPKIDir + "/issued/" + jsonRequest.UnitName + ".crt")
crt, errCrt := os.ReadFile(configuration.Config.OpenVPNPKIDir + "/issued/" + jsonRequest.UnitId + ".crt")
crtS := strings.TrimSpace(string(crt[:]))

// check error
Expand All @@ -499,7 +500,7 @@ func RegisterUnit(c *gin.Context) {
}

// read key
key, errKey := os.ReadFile(configuration.Config.OpenVPNPKIDir + "/private/" + jsonRequest.UnitName + ".key")
key, errKey := os.ReadFile(configuration.Config.OpenVPNPKIDir + "/private/" + jsonRequest.UnitId + ".key")
keyS := strings.TrimSpace(string(key[:]))

// check error
Expand Down Expand Up @@ -529,7 +530,7 @@ func RegisterUnit(c *gin.Context) {

// read credentials from file
var credentials LoginRequest
jsonString, errRead := ioutil.ReadFile(configuration.Config.CredentialsDir + "/" + jsonRequest.UnitName)
jsonString, errRead := ioutil.ReadFile(configuration.Config.CredentialsDir + "/" + jsonRequest.UnitId)

// credentials exists, update only if username matches
if errRead == nil {
Expand All @@ -548,11 +549,11 @@ func RegisterUnit(c *gin.Context) {

// write new credentials
newJsonString, _ := json.Marshal(credentials)
errWrite := os.WriteFile(configuration.Config.CredentialsDir+"/"+jsonRequest.UnitName, newJsonString, 0644)
errWrite := os.WriteFile(configuration.Config.CredentialsDir+"/"+jsonRequest.UnitId, newJsonString, 0644)
if errWrite != nil {
c.JSON(http.StatusBadRequest, structs.Map(response.StatusBadRequest{
Code: 400,
Message: "cannot write credentials file for: " + jsonRequest.UnitName,
Message: "cannot write credentials file for: " + jsonRequest.UnitId,
Data: errWrite.Error(),
}))
return
Expand All @@ -566,7 +567,7 @@ func RegisterUnit(c *gin.Context) {
}))
} else {
// add to waiting list
global.WaitingList[jsonRequest.UnitName] = gin.H{
global.WaitingList[jsonRequest.UnitId] = gin.H{
"username": jsonRequest.Username,
"password": jsonRequest.Password,
}
Expand All @@ -581,22 +582,22 @@ func RegisterUnit(c *gin.Context) {
}

func DeleteUnit(c *gin.Context) {
// get unit name
unitName := c.Param("unit_name")
// get unit id
UnitId := c.Param("unit_id")

// kill vpn connection
_ = socket.Write("kill " + unitName)
_ = socket.Write("kill " + UnitId)

// revoke certificate
cmdRevoke := exec.Command(configuration.Config.EasyRSAPath, "revoke", unitName)
cmdRevoke := exec.Command(configuration.Config.EasyRSAPath, "revoke", UnitId)
cmdRevoke.Env = append(os.Environ(),
"EASYRSA_BATCH=1",
"EASYRSA_PKI="+configuration.Config.OpenVPNPKIDir,
)
if err := cmdRevoke.Run(); err != nil {
c.JSON(http.StatusBadRequest, structs.Map(response.StatusBadRequest{
Code: 400,
Message: "cannot revoke certificate for: " + unitName,
Message: "cannot revoke certificate for: " + UnitId,
Data: err.Error(),
}))
return
Expand All @@ -618,25 +619,25 @@ func DeleteUnit(c *gin.Context) {
}

// delete reservation/auth file
if _, err := os.Stat(configuration.Config.OpenVPNCCDDir + "/" + unitName); err == nil {
errDeleteAuth := os.Remove(configuration.Config.OpenVPNCCDDir + "/" + unitName)
if _, err := os.Stat(configuration.Config.OpenVPNCCDDir + "/" + UnitId); err == nil {
errDeleteAuth := os.Remove(configuration.Config.OpenVPNCCDDir + "/" + UnitId)
if errDeleteAuth != nil {
c.JSON(http.StatusBadRequest, structs.Map(response.StatusBadRequest{
Code: 403,
Message: "error in deletion auth file for: " + unitName,
Message: "error in deletion auth file for: " + UnitId,
Data: errDeleteAuth.Error(),
}))
return
}
}

// delete traefik conf
if _, err := os.Stat(configuration.Config.OpenVPNProxyDir + "/" + unitName + ".yaml"); err == nil {
errDeleteProxy := os.Remove(configuration.Config.OpenVPNProxyDir + "/" + unitName + ".yaml")
if _, err := os.Stat(configuration.Config.OpenVPNProxyDir + "/" + UnitId + ".yaml"); err == nil {
errDeleteProxy := os.Remove(configuration.Config.OpenVPNProxyDir + "/" + UnitId + ".yaml")
if errDeleteProxy != nil {
c.JSON(http.StatusBadRequest, structs.Map(response.StatusBadRequest{
Code: 403,
Message: "error in deletion proxy file for: " + unitName,
Message: "error in deletion proxy file for: " + UnitId,
Data: errDeleteProxy.Error(),
}))
return
Expand Down

0 comments on commit 21d36bf

Please sign in to comment.