Skip to content

Commit

Permalink
feat: yaml config file support (#340)
Browse files Browse the repository at this point in the history
* initial yaml support

* add test that reads yaml config
  • Loading branch information
soerenschneider authored Oct 14, 2023
1 parent 67ed17c commit ab7577b
Show file tree
Hide file tree
Showing 9 changed files with 109 additions and 70 deletions.
7 changes: 3 additions & 4 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ func main() {
if err != nil {
log.Fatal().Msgf("Could not load config: %v", err)
}
conf.Print()
err = conf.Validate()
if err != nil {
log.Fatal().Msgf("Invalid configuration provided: %v", err)
Expand Down Expand Up @@ -71,7 +70,7 @@ func getUserHomeDirectory() string {
return dir
}

func buildVaultAuth(conf config.AcmeVaultServerConfig) (vault.Auth, error) {
func buildVaultAuth(conf config.VaultConfig) (vault.Auth, error) {
switch conf.AuthMethod {
case "token":
return vault.NewTokenAuth(conf.Token)
Expand All @@ -97,10 +96,10 @@ func dieOnError(err error, msg string) {
}

func NewAcmeVaultServer(conf config.AcmeVaultServerConfig) {
vaultAuth, err := buildVaultAuth(conf)
vaultAuth, err := buildVaultAuth(conf.Vault)
dieOnError(err, "could not build token auth")

storage, err := vault.NewVaultBackend(conf.VaultConfig, vaultAuth)
storage, err := vault.NewVaultBackend(conf.Vault, vaultAuth)
dieOnError(err, "could not generate desired backend")

err = storage.Authenticate()
Expand Down
10 changes: 0 additions & 10 deletions contrib/client.json

This file was deleted.

25 changes: 25 additions & 0 deletions contrib/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"domains": [
{
"domain": "domain1.tld",
"sans": [
"domain3.tld",
"domain4.tld"
]
},
{
"domain": "domain2.tld"
}
],
"email": "[email protected]",
"vault": {
"authMethod": "approle",
"addr": "https://vault:8200",
"secretId": "secretId",
"roleId": "roleId",
"pathPrefix": "preprod",
"kv2MountPath": "secret",
"awsMountPath": "custom-aws-mountpath",
"awsRole": "my-custom-role"
}
}
16 changes: 16 additions & 0 deletions contrib/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
domains:
- domain: domain1.tld
sans:
- domain3.tld
- domain4.tld
- domain: domain2.tld
email: [email protected]
vault:
authMethod: approle
addr: https://vault:8200
secretId: secretId
roleId: roleId
pathPrefix: preprod
kv2MountPath: secret
awsMountPath: custom-aws-mountpath
awsRole: my-custom-role
23 changes: 0 additions & 23 deletions contrib/server.json

This file was deleted.

2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ require (
github.com/stretchr/testify v1.8.4
go.uber.org/multierr v1.11.0
golang.org/x/net v0.15.0
gopkg.in/yaml.v3 v3.0.1
)

require (
Expand Down Expand Up @@ -73,5 +74,4 @@ require (
golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.10.0 // indirect
google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
26 changes: 13 additions & 13 deletions internal/config/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,24 @@ import (
var validate = validator.New()

type VaultConfig struct {
Addr string `json:"vaultAddr" validate:"required,http_url"`
AuthMethod string `json:"vaultAuthMethod" validate:"required,oneof=token approle k8s implicit"`
Token string `json:"vaultToken" validate:"required_if=AuthMethod 'token'"`
Addr string `yaml:"addr" validate:"required,http_url"`
AuthMethod string `yaml:"authMethod" validate:"required,oneof=token approle k8s implicit"`
Token string `yaml:"token" validate:"required_if=AuthMethod 'token'"`

RoleId string `json:"vaultRoleId" validate:"required_if=AuthMethod 'approle'"`
SecretId string `json:"vaultSecretId" validate:"excluded_unless=SecretIdFile '',required_if=SecretIdFile '' AuthMethod 'approle'"`
SecretIdFile string `json:"vaultSecretIdFile" validate:"excluded_unless=SecretId '',required_if=SecretId '' AuthMethod 'approle'"`
RoleId string `yaml:"roleId" validate:"required_if=AuthMethod 'approle'"`
SecretId string `yaml:"secretId" validate:"excluded_unless=SecretIdFile '',required_if=SecretIdFile '' AuthMethod 'approle'"`
SecretIdFile string `yaml:"secretIdFile" validate:"excluded_unless=SecretId '',required_if=SecretId '' AuthMethod 'approle'"`

K8sRoleId string `json:"vaultK8sRoleId" validate:"required_if=AuthMethod 'k8s'"`
K8sMountPath string `json:"vaultK8sMountPath"`
K8sRoleId string `yaml:"k8sRoleId" validate:"required_if=AuthMethod 'k8s'"`
K8sMountPath string `yaml:"k8sMountPath"`

PathPrefix string `json:"vaultPathPrefix" validate:"required,startsnotwith=/,startsnotwith=/secret,endsnotwith=/,ne=acmevault"`
DomainPathFormat string `json:"domainPathFormat" validate:"omitempty,containsrune=%"`
PathPrefix string `yaml:"pathPrefix" validate:"required,startsnotwith=/,startsnotwith=/secret,endsnotwith=/,ne=acmevault"`
DomainPathFormat string `yaml:"domainPathFormat" validate:"omitempty,containsrune=%"`

Kv2MountPath string `json:"vaultKv2MountPath" validate:"required,endsnotwith=/,startsnotwith=/"`
Kv2MountPath string `yaml:"kv2MountPath" validate:"required,endsnotwith=/,startsnotwith=/"`

AwsMountPath string `json:"vaultAwsMountPath" validate:"required,endsnotwith=/,startsnotwith=/"`
AwsRole string `json:"vaultAwsRole" validate:"required"`
AwsMountPath string `yaml:"awsMountPath" validate:"required,endsnotwith=/,startsnotwith=/"`
AwsRole string `yaml:"awsRole" validate:"required"`
}

func (conf *VaultConfig) Print() {
Expand Down
31 changes: 16 additions & 15 deletions internal/config/server.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package config

import (
"encoding/json"
"fmt"
"io/ioutil"
"os"

"gopkg.in/yaml.v3"
)

const letsEncryptUrl = "https://acme-v02.api.letsencrypt.org/directory"
Expand All @@ -15,19 +16,19 @@ var (
)

type AcmeVaultServerConfig struct {
VaultConfig
AcmeEmail string `json:"email" validate:"required,email"`
AcmeUrl string `json:"acmeUrl" validate:"required,oneof=https://acme-v02.api.letsencrypt.org/directory https://acme-staging-v02.api.letsencrypt.org/directory"`
AcmeDnsProvider string `json:"acmeDnsProvider"`
AcmeCustomDnsServers []string `json:"acmeCustomDnsServers,omitempty" validate:"dive,ip"`
IntervalSeconds int `json:"intervalSeconds" validate:"min=3600,max=86400"`
Domains []AcmeServerDomains `json:"domains" validate:"required,dive"`
MetricsAddr string `json:"metricsAddr" validate:"tcp_addr"`
Vault VaultConfig `yaml:"vault" validate:"required"`
AcmeEmail string `yaml:"email" validate:"required,email"`
AcmeUrl string `yaml:"acmeUrl" validate:"required,oneof=https://acme-v02.api.letsencrypt.org/directory https://acme-staging-v02.api.letsencrypt.org/directory"`
AcmeDnsProvider string `yaml:"acmeDnsProvider"`
AcmeCustomDnsServers []string `yaml:"acmeCustomDnsServers,omitempty" validate:"dive,ip"`
IntervalSeconds int `yaml:"intervalSeconds" validate:"min=3600,max=86400"`
Domains []AcmeServerDomains `yaml:"domains" validate:"required,dive"`
MetricsAddr string `yaml:"metricsAddr" validate:"tcp_addr"`
}

type AcmeServerDomains struct {
Domain string `json:"domain" validate:"required,fqdn"`
Sans []string `json:"sans,omitempty" validate:"dive,fqdn"`
Domain string `yaml:"domain" validate:"required,fqdn"`
Sans []string `yaml:"sans,omitempty" validate:"dive,fqdn"`
}

func (a AcmeServerDomains) String() string {
Expand All @@ -47,17 +48,17 @@ func getDefaultServerConfig() AcmeVaultServerConfig {
AcmeUrl: letsEncryptUrl,
IntervalSeconds: defaultIntervalSeconds,
MetricsAddr: defaultMetricsAddr,
VaultConfig: DefaultVaultConfig(),
Vault: DefaultVaultConfig(),
}
}

func AcmeVaultServerConfigFromFile(path string) (AcmeVaultServerConfig, error) {
conf := getDefaultServerConfig()
content, err := ioutil.ReadFile(path)
content, err := os.ReadFile(path)
if err != nil {
return conf, fmt.Errorf("can not read config from file %s: %v", path, err)
}

err = json.Unmarshal(content, &conf)
err = yaml.Unmarshal(content, &conf)
return conf, err
}
39 changes: 35 additions & 4 deletions internal/config/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,41 @@ func TestAcmeVaultServerConfigFromFile(t *testing.T) {
wantErr bool
}{
{
name: "example server config",
path: "../../contrib/server.json",
name: "example json config",
path: "../../contrib/config.json",
want: AcmeVaultServerConfig{
VaultConfig: VaultConfig{
Vault: VaultConfig{
Addr: "https://vault:8200",
SecretId: "secretId",
RoleId: "roleId",
PathPrefix: "preprod",
AuthMethod: "approle",
Kv2MountPath: "secret",
AwsMountPath: "custom-aws-mountpath",
AwsRole: "my-custom-role",
},
AcmeEmail: "[email protected]",
AcmeUrl: letsEncryptUrl,
AcmeDnsProvider: "",
IntervalSeconds: 43200,
Domains: []AcmeServerDomains{
{
Domain: "domain1.tld",
Sans: []string{"domain3.tld", "domain4.tld"},
},
{
Domain: "domain2.tld",
},
},
MetricsAddr: "127.0.0.1:9112",
},
wantErr: false,
},
{
name: "example yaml config",
path: "../../contrib/config.yaml",
want: AcmeVaultServerConfig{
Vault: VaultConfig{
Addr: "https://vault:8200",
SecretId: "secretId",
RoleId: "roleId",
Expand Down Expand Up @@ -292,7 +323,7 @@ func TestAcmeVaultServerConfig_Validate(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
conf := AcmeVaultServerConfig{
VaultConfig: tt.fields.VaultConfig,
Vault: tt.fields.VaultConfig,
AcmeEmail: tt.fields.AcmeEmail,
AcmeUrl: tt.fields.AcmeUrl,
AcmeDnsProvider: tt.fields.AcmeDnsProvider,
Expand Down

0 comments on commit ab7577b

Please sign in to comment.