Automating OpenVPN Login in Ubuntu Using Golang
OpenVPN enjoys widespread adoption due to its robustness in creating VPN tunnels. Nevertheless, users must navigate a cumbersome step of supplying credentials every time a VPN connection is established. This routine becomes even more laborious when multi-factor authentication (MFA) is involved.
We address the challenge of conveniently remembering the username, config-path, and password, thereby streamlining the authentication process.
Streamlining User Input
To kick things off, let’s demonstrate how to automate the process of supplying the username and password. In the following Go code snippet, we show an approach to do this:
cmd := exec.Command("openvpn3", "session-start", "-config", configPath)
stdin, err := cmd.StdinPipe()
if err != nil {
fmt.Println("Error obtaining stdin:", err)
return
}
go func() {
defer stdin.Close()
io.WriteString(stdin, config.Username+"\n")
io.WriteString(stdin, password+"\n")
}()
err = cmd.Run()
if err != nil {
fmt.Println("Command execution failed:", err)
return
}
This script furnishes the username and password automatically when OpenVPN requests them, thereby simplifying the login process.
Remembering the Configuration
While the preceding solution is an improvement, it does not remember the user configuration (username, password, and config-path). To circumvent this, we store this configuration in a local file system and read it each time the script runs. This involves creating a JSON file to store the user configuration, as seen in the Config struct below:
type Config struct {
Username string `json:"username"`
Password string `json:"password"`
Config string `json:"config"`
}
We can retrieve this configuration at the start of our script, and if the username
, password
, or config
values are absent, we prompt the user to provide them. Here’s a sample snippet for reading and writing to a JSON file:
// Write to the file
config := Config{
Username: "username",
Password: "password",
Config: "config",
}
file, _ := json.MarshalIndent(config, "", " ")
_ = ioutil.WriteFile("config.json", file, 0644)
// Read from the file
file, _ = ioutil.ReadFile("config.json")
_ = json.Unmarshal([]byte(file), &config)
Enhancing Security
The security of user credentials is of paramount importance. Saving passwords as plaintext on a filesystem poses significant security risks. To bolster our solution’s security, we encrypt the password before storage and decrypt it during retrieval.
We employ a key derived from the device’s MAC address, hostname, and a user-provided 4-digit key to encrypt/decrypt the credentials. To read the masked user key securely, we use the gopass
package.
fmt.Print("Enter your 4-digit key: ")
userKey, err := gopass.GetPasswdMasked()
if err != nil {
fmt.Println("Failed to read user key:", err)
return
}
keyMaterial := getMacAddress() + getHostName() + string(userKey)
key := sha256.Sum256([]byte(keyMaterial))
This strategy considerably fortifies our solution’s security. The password is securely stored, and without access to the user’s device and knowledge of the 4-digit key, decrypting the password would be virtually impossible for an attacker.
Conclusion
This blog post traced a journey through Go code, demonstrating how to automate the OpenVPN login process in Ubuntu, remember user
credentials, and secure the stored credentials.
For the comprehensive solution, kindly refer to this GitHub Gist: Automating OpenVPN Login. We invite you to download, share, and contribute to this open-source project. The final solution could be further refined with enhanced error handling, comprehensive testing, and user interface improvements.