Feature: Docker secrets, refers to #306

This commit is contained in:
Quentin McGaw
2020-12-29 20:47:56 +00:00
parent 258e150ebf
commit 5917bb10e4
8 changed files with 187 additions and 73 deletions

View File

@@ -3,8 +3,6 @@ package params
import (
"encoding/pem"
"fmt"
"io/ioutil"
"os"
"strings"
"github.com/qdm12/gluetun/internal/constants"
@@ -25,23 +23,15 @@ func (p *reader) GetCyberghostRegions() (regions []string, err error) {
return p.envParams.GetCSVInPossibilities("REGION", constants.CyberghostRegionChoices())
}
// GetCyberghostClientKey obtains the one line client key to use for openvpn from the
// file at /gluetun/client.key.
// GetCyberghostClientKey obtains the client key to use for openvpn
// from the secret file /run/secrets/openvpn_clientkey or from the file
// /gluetun/client.key.
func (p *reader) GetCyberghostClientKey() (clientKey string, err error) {
const filepath = string(constants.ClientKey)
file, err := p.os.OpenFile(filepath, os.O_RDONLY, 0)
b, err := p.getFromFileOrSecretFile("OPENVPN_CLIENTKEY", string(constants.ClientKey))
if err != nil {
return "", err
}
content, err := ioutil.ReadAll(file)
if err != nil {
_ = file.Close()
return "", err
}
if err := file.Close(); err != nil {
return "", err
}
return extractClientKey(content)
return extractClientKey(b)
}
func extractClientKey(b []byte) (key string, err error) {
@@ -57,23 +47,15 @@ func extractClientKey(b []byte) (key string, err error) {
return s, nil
}
// GetCyberghostClientCertificate obtains the client certificate to use for openvpn from the
// file at /gluetun/client.crt.
// GetCyberghostClientCertificate obtains the client certificate to use for openvpn
// from the secret file /run/secrets/openvpn_clientcrt or from the file
// /gluetun/client.crt.
func (p *reader) GetCyberghostClientCertificate() (clientCertificate string, err error) {
const filepath = string(constants.ClientCertificate)
file, err := p.os.OpenFile(filepath, os.O_RDONLY, 0)
b, err := p.getFromFileOrSecretFile("OPENVPN_CLIENTCRT", string(constants.ClientCertificate))
if err != nil {
return "", err
}
content, err := ioutil.ReadAll(file)
if err != nil {
_ = file.Close()
return "", err
}
if err := file.Close(); err != nil {
return "", err
}
return extractClientCertificate(content)
return extractClientCertificate(b)
}
func extractClientCertificate(b []byte) (certificate string, err error) {

View File

@@ -49,26 +49,28 @@ func (r *reader) GetHTTPProxyPort() (port uint16, err error) {
return r.envParams.GetPort("HTTPPROXY_PORT", retroKeysOption, libparams.Default("8888"))
}
// GetHTTPProxyUser obtains the HTTP proxy server user from the environment variable
// HTTPPROXY_USER, and using TINYPROXY_USER and PROXY_USER as retro-compatibility names.
// GetHTTPProxyUser obtains the HTTP proxy server user.
// It first tries to use the HTTPPROXY_USER environment variable (easier for the end user)
// and then tries to read from the secret file httpproxy_user if nothing was found.
func (r *reader) GetHTTPProxyUser() (user string, err error) {
retroKeysOption := libparams.RetroKeys(
const compulsory = false
return r.getFromEnvOrSecretFile(
"HTTPPROXY_USER",
compulsory,
[]string{"TINYPROXY_USER", "PROXY_USER"},
r.onRetroActive,
)
return r.envParams.GetEnv("HTTPPROXY_USER",
retroKeysOption, libparams.CaseSensitiveValue(), libparams.Unset())
}
// GetHTTPProxyPassword obtains the HTTP proxy server password from the environment variable
// HTTPPROXY_PASSWORD, and using TINYPROXY_PASSWORD and PROXY_PASSWORD as retro-compatibility names.
// GetHTTPProxyPassword obtains the HTTP proxy server password.
// It first tries to use the HTTPPROXY_PASSWORD environment variable (easier for the end user)
// and then tries to read from the secret file httpproxy_password if nothing was found.
func (r *reader) GetHTTPProxyPassword() (password string, err error) {
retroKeysOption := libparams.RetroKeys(
const compulsory = false
return r.getFromEnvOrSecretFile(
"HTTPPROXY_USER",
compulsory,
[]string{"TINYPROXY_PASSWORD", "PROXY_PASSWORD"},
r.onRetroActive,
)
return r.envParams.GetEnv("HTTPPROXY_PASSWORD",
retroKeysOption, libparams.CaseSensitiveValue(), libparams.Unset())
}
// GetHTTPProxyStealth obtains the HTTP proxy server stealth mode

View File

@@ -9,23 +9,19 @@ import (
)
// GetUser obtains the user to use to connect to the VPN servers.
// It first tries to use the OPENVPN_USER environment variable (easier for the end user)
// and then tries to read from the secret file openvpn_user if nothing was found.
func (r *reader) GetUser() (user string, err error) {
return r.envParams.GetEnv("OPENVPN_USER",
libparams.CaseSensitiveValue(),
libparams.Compulsory(),
libparams.RetroKeys([]string{"USER"}, r.onRetroActive),
libparams.Unset())
const compulsory = true
return r.getFromEnvOrSecretFile("OPENVPN_USER", compulsory, []string{"USER"})
}
// GetPassword obtains the password to use to connect to the VPN servers.
// It first tries to use the OPENVPN_PASSWORD environment variable (easier for the end user)
// and then tries to read from the secret file openvpn_password if nothing was found.
func (r *reader) GetPassword() (s string, err error) {
options := []libparams.GetEnvSetter{
libparams.CaseSensitiveValue(),
libparams.Unset(),
libparams.Compulsory(),
libparams.RetroKeys([]string{"PASSWORD"}, r.onRetroActive),
}
return r.envParams.GetEnv("OPENVPN_PASSWORD", options...)
const compulsory = true
return r.getFromEnvOrSecretFile("OPENVPN_PASSWORD", compulsory, []string{"PASSWORD"})
}
// GetNetworkProtocol obtains the network protocol to use to connect to the

108
internal/params/secrets.go Normal file
View File

@@ -0,0 +1,108 @@
package params
import (
"errors"
"fmt"
"io/ioutil"
"strings"
"github.com/qdm12/gluetun/internal/os"
libparams "github.com/qdm12/golibs/params"
)
var (
ErrGetSecretFilepath = errors.New("cannot get secret file path from env")
ErrReadSecretFile = errors.New("cannot read secret file")
ErrSecretFileIsEmpty = errors.New("secret file is empty")
ErrReadNonSecretFile = errors.New("cannot read non secret file")
ErrFilesDoNotExist = errors.New("files do not exist")
)
func (r *reader) getFromEnvOrSecretFile(envKey string, compulsory bool, retroKeys []string) (value string, err error) {
envOptions := []libparams.GetEnvSetter{
libparams.Compulsory(), // to fallback on file reading
libparams.CaseSensitiveValue(),
libparams.Unset(),
libparams.RetroKeys(retroKeys, r.onRetroActive),
}
value, envErr := r.envParams.GetEnv(envKey, envOptions...)
if envErr == nil {
return value, nil
}
defaultSecretFile := "/run/secrets/" + strings.ToLower(envKey)
filepath, err := r.envParams.GetEnv(envKey+"_SECRETFILE",
libparams.CaseSensitiveValue(),
libparams.Default(defaultSecretFile),
)
if err != nil {
return "", fmt.Errorf("%w: %s", ErrGetSecretFilepath, err)
}
file, fileErr := r.os.OpenFile(filepath, os.O_RDONLY, 0)
if os.IsNotExist(fileErr) {
if compulsory {
return "", envErr
}
return "", nil
} else if fileErr != nil {
return "", fmt.Errorf("%w: %s", ErrReadSecretFile, fileErr)
}
b, err := ioutil.ReadAll(file)
if err != nil {
return "", fmt.Errorf("%w: %s", ErrReadSecretFile, err)
}
value = string(b)
if compulsory && len(value) == 0 {
return "", ErrSecretFileIsEmpty
}
return value, nil
}
// Tries to read from the secret file then the non secret file.
func (r *reader) getFromFileOrSecretFile(secretName, filepath string) (
b []byte, err error) {
defaultSecretFile := "/run/secrets/" + strings.ToLower(secretName)
secretFilepath, err := r.envParams.GetEnv(strings.ToUpper(secretName)+"_SECRETFILE",
libparams.CaseSensitiveValue(),
libparams.Default(defaultSecretFile),
)
if err != nil {
return b, fmt.Errorf("%w: %s", ErrGetSecretFilepath, err)
}
b, err = readFromFile(r.os.OpenFile, secretFilepath)
if err != nil && !os.IsNotExist(err) {
return b, fmt.Errorf("%w: %s", ErrReadSecretFile, err)
} else if err == nil {
return b, nil
}
// Secret file does not exist, try the non secret file
b, err = readFromFile(r.os.OpenFile, filepath)
if err != nil && !os.IsNotExist(err) {
return nil, fmt.Errorf("%w: %s", ErrReadSecretFile, err)
} else if err == nil {
return b, nil
}
return nil, fmt.Errorf("%w: %s and %s", ErrFilesDoNotExist, secretFilepath, filepath)
}
func readFromFile(openFile os.OpenFileFunc, filepath string) (b []byte, err error) {
file, err := openFile(filepath, os.O_RDONLY, 0)
if err != nil {
return nil, err
}
b, err = ioutil.ReadAll(file)
if err != nil {
_ = file.Close()
return nil, err
}
if err := file.Close(); err != nil {
return nil, err
}
return b, nil
}

View File

@@ -32,10 +32,12 @@ func (r *reader) GetShadowSocksPort() (port uint16, err error) {
return uint16(portUint64), err
}
// GetShadowSocksPassword obtains the ShadowSocks server password from the environment variable
// SHADOWSOCKS_PASSWORD.
// GetShadowSocksPassword obtains the ShadowSocks server password.
// It first tries to use the SHADOWSOCKS_PASSWORD environment variable (easier for the end user)
// and then tries to read from the secret file shadowsocks_password if nothing was found.
func (r *reader) GetShadowSocksPassword() (password string, err error) {
return r.envParams.GetEnv("SHADOWSOCKS_PASSWORD", libparams.CaseSensitiveValue(), libparams.Unset())
const compulsory = false
return r.getFromEnvOrSecretFile("SHADOWSOCKS_PASSWORD", compulsory, nil)
}
// GetShadowSocksMethod obtains the ShadowSocks method to use from the environment variable