Skip to content

Commit

Permalink
feat: validate using validator package
Browse files Browse the repository at this point in the history
  • Loading branch information
soerenschneider committed Jul 10, 2023
1 parent f0c519b commit a3cada0
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 153 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ require (
github.com/aws/aws-sdk-go v1.44.298
github.com/blushft/go-diagrams v0.0.0-20201006005127-c78c821223d9
github.com/go-acme/lego/v4 v4.12.3
github.com/go-playground/validator/v10 v10.14.1 // indirect
github.com/hashicorp/go-retryablehttp v0.7.4
github.com/hashicorp/vault/api v1.9.2
github.com/prometheus/client_golang v1.16.0
Expand Down
11 changes: 11 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
github.com/getkin/kin-openapi v0.87.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
Expand Down Expand Up @@ -563,13 +565,20 @@ github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KE
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
github.com/go-playground/validator/v10 v10.9.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
github.com/go-playground/validator/v10 v10.11.0/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
github.com/go-playground/validator/v10 v10.14.1 h1:9c50NUPC30zyuKprjL3vNZ0m5oG+jU0zvx4AqHGnv4k=
github.com/go-playground/validator/v10 v10.14.1/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
github.com/go-resty/resty/v2 v2.1.1-0.20191201195748-d7b97669fe48/go.mod h1:dZGr0i9PLlaaTD4H/hoZIDjQ+r6xq8mgbRzHZf7f2J8=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
Expand Down Expand Up @@ -817,6 +826,8 @@ github.com/labstack/gommon v0.2.7/go.mod h1:/tj9csK2iPSBvn+3NLM9e52usepMtrd5ilFY
github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
github.com/lestrrat-go/backoff/v2 v2.0.8/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y=
github.com/lestrrat-go/blackmagic v1.0.0/go.mod h1:TNgH//0vYSs8VXDCfkZLgIrVTTXQELZffUV0tz3MtdQ=
github.com/lestrrat-go/codegen v1.0.2/go.mod h1:JhJw6OQAuPEfVKUCLItpaVLumDGWQznd1VaXrBk9TdM=
Expand Down
70 changes: 11 additions & 59 deletions internal/config/common.go
Original file line number Diff line number Diff line change
@@ -1,31 +1,23 @@
package config

import (
"errors"
"fmt"
"net/url"
"os"
"strings"

"github.com/rs/zerolog/log"
"github.com/go-playground/validator/v10"
)

type VaultConfig struct {
VaultToken string `json:"vaultToken"`
VaultAddr string `json:"vaultAddr"`
RoleId string `json:"vaultRoleId"`
SecretId string `json:"vaultSecretId"`
SecretIdFile string `json:"vaultSecretIdFile"`
VaultWrappedToken string `json:"vaultWrappedToken"`
VaultWrappedTokenFile string `json:"vaultWrappedTokenFile"`
TokenIncreaseSeconds int `json:"tokenIncreaseSeconds"`
TokenIncreaseInterval int `json:"tokenIncreaseInterval"`
PathPrefix string `json:"vaultPathPrefix"`
DomainPathFormat string `json:"domainPathFormat"`
}
var validate = validator.New()

func (conf *VaultConfig) IsTokenIncreaseEnabled() bool {
return conf.TokenIncreaseInterval > 0 || conf.TokenIncreaseSeconds > 0
type VaultConfig struct {
VaultToken string `json:"vaultToken" validate:"required_if=RoleId ''"`
VaultAddr string `json:"vaultAddr" validate:"required,http_url"`
RoleId string `json:"vaultRoleId" validate:"required_if=VaultToken ''"`
SecretId string `json:"vaultSecretId" validate:"excluded_unless=SecretIdFile '',required_if=SecretIdFile '' VaultToken ''"`
SecretIdFile string `json:"vaultSecretIdFile" validate:"excluded_unless=SecretId '',required_if=SecretId '' VaultToken ''"`
PathPrefix string `json:"vaultPathPrefix" validate:"required,startsnotwith=/,startsnotwith=/secret"`
DomainPathFormat string `json:"domainPathFormat" validate:"omitempty,containsrune=%"`
}

func (conf *VaultConfig) Print() {
Expand Down Expand Up @@ -75,47 +67,7 @@ func DefaultVaultConfig() VaultConfig {
}

func (conf *VaultConfig) Validate() error {
if len(conf.VaultAddr) == 0 {
return errors.New("no Vault address defined")
}
addr, err := url.ParseRequestURI(conf.VaultAddr)
if err != nil || addr.Scheme == "" || addr.Host == "" || addr.Port() == "" {
return errors.New("can not parse supplied vault addr as url")
}

if len(conf.PathPrefix) == 0 {
return errors.New("empty path prefix provided")
}
for _, prefix := range []string{"/", "secret/"} {
if strings.HasPrefix(conf.PathPrefix, prefix) {
return fmt.Errorf("vault path prefix must not start with %s", prefix)
}
}

validRoleIdCredentials := (len(conf.SecretId) > 0 || len(conf.SecretIdFile) > 0) && len(conf.RoleId) > 0
if !validRoleIdCredentials && len(conf.VaultToken) == 0 {
return errors.New("neither valid 'App Role' credentials nor plain Vault token provided")
}

if conf.HasWrappedToken() && !conf.LoadSecretIdFromFile() {
return errors.New("vaultWrappedToken specified but no vaultSecretIdFile specified to write acquired secret to")
}

if len(conf.SecretId) > 0 && conf.LoadSecretIdFromFile() {
return errors.New("both secretId and secretIdFile specified, unsure what to do")
}

if conf.LoadSecretIdFromFile() && !isFileWritable(conf.SecretIdFile) {
return errors.New("specified secretIdFile is not writable, quitting")
}

if len(conf.DomainPathFormat) > 0 {
if !strings.ContainsRune(conf.DomainPathFormat, '%') {
return fmt.Errorf("the domainPathFormat '%s' does not seem to be a valid format string", conf.DomainPathFormat)
}
}

return nil
return validate.Struct(conf)
}

func isFileWritable(fileName string) bool {
Expand Down
120 changes: 35 additions & 85 deletions internal/config/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,13 @@ import "testing"

func TestVaultConfig_Validate(t *testing.T) {
type fields struct {
VaultToken string
VaultAddr string
SecretId string
RoleId string
TokenIncreaseSeconds int
TokenIncreaseInterval int
PathPrefix string
SecretIdFile string
VaultWrappingToken string
VaultToken string
VaultAddr string
SecretId string
RoleId string
PathPrefix string
SecretIdFile string
VaultWrappingToken string
}
tests := []struct {
name string
Expand All @@ -22,86 +20,61 @@ func TestVaultConfig_Validate(t *testing.T) {
{
name: "valid config - token",
fields: fields{
VaultToken: "s.asd83hrfhasfjsda",
VaultAddr: "https://my-vault-instance:443",
TokenIncreaseSeconds: 0,
TokenIncreaseInterval: 0,
PathPrefix: "production",
VaultToken: "s.asd83hrfhasfjsda",
VaultAddr: "https://my-vault-instance:443",
PathPrefix: "production",
},
},
{
name: "valid config - approle",
fields: fields{
VaultAddr: "https://my-vault-instance:443",
SecretId: "super-secret",
RoleId: "my-role",
TokenIncreaseSeconds: 0,
TokenIncreaseInterval: 0,
PathPrefix: "dev-v002",
VaultAddr: "https://my-vault-instance:443",
SecretId: "super-secret",
RoleId: "my-role",
PathPrefix: "dev-v002",
},
},
{
name: "valid config - approle, secret_id file",
fields: fields{
VaultAddr: "https://my-vault-instance:443",
SecretIdFile: "super-secret",
RoleId: "my-role",
TokenIncreaseSeconds: 0,
TokenIncreaseInterval: 0,
PathPrefix: "dev-v002",
VaultAddr: "https://my-vault-instance:443",
SecretIdFile: "super-secret",
RoleId: "my-role",
PathPrefix: "dev-v002",
},
},
{
name: "invalid config - missing protocol",
fields: fields{
VaultToken: "s.asd83hrfhasfjsda",
VaultAddr: "my-vault-instance:443",
TokenIncreaseSeconds: 0,
TokenIncreaseInterval: 0,
PathPrefix: "production",
},
wantErr: true,
},
{
name: "invalid config - missing port",
fields: fields{
VaultToken: "s.asd83hrfhasfjsda",
VaultAddr: "http://my-vault-instance",
TokenIncreaseSeconds: 0,
TokenIncreaseInterval: 0,
PathPrefix: "production",
VaultToken: "s.asd83hrfhasfjsda",
VaultAddr: "my-vault-instance:443",
PathPrefix: "production",
},
wantErr: true,
},
{
name: "invalid config - invalid path prefix",
fields: fields{
VaultToken: "s.asd83hrfhasfjsda",
VaultAddr: "http://my-vault-instance:443",
TokenIncreaseSeconds: 0,
TokenIncreaseInterval: 0,
PathPrefix: "/production",
VaultToken: "s.asd83hrfhasfjsda",
VaultAddr: "http://my-vault-instance:443",
PathPrefix: "/production",
},
wantErr: true,
},
{
name: "invalid config - no auth methods",
fields: fields{
VaultAddr: "http://my-vault-instance:443",
TokenIncreaseSeconds: 0,
TokenIncreaseInterval: 0,
PathPrefix: "production",
VaultAddr: "http://my-vault-instance:443",
PathPrefix: "production",
},
wantErr: true,
},
{
name: "invalid config - empty path prefix",
fields: fields{
VaultAddr: "http://my-vault-instance:443",
VaultToken: "s.VALIDVALIDVALID",
TokenIncreaseSeconds: 0,
TokenIncreaseInterval: 0,
PathPrefix: "",
VaultAddr: "http://my-vault-instance:443",
VaultToken: "s.VALIDVALIDVALID",
PathPrefix: "",
},
wantErr: true,
},
Expand All @@ -117,39 +90,16 @@ func TestVaultConfig_Validate(t *testing.T) {
},
wantErr: true,
},
{
name: "invalid config - secretIdFile not writable",
fields: fields{
VaultAddr: "http://my-vault-instance:443",
VaultToken: "s.VALIDVALIDVALID",
PathPrefix: "production",
SecretIdFile: "/bin/sh",
},
wantErr: true,
},
{
name: "invalid config - wrappingToken specified but no file to write to",
fields: fields{
VaultAddr: "http://my-vault-instance:443",
VaultToken: "s.VALIDVALIDVALID",
PathPrefix: "production",
VaultWrappingToken: "s.XXX",
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
conf := &VaultConfig{
VaultToken: tt.fields.VaultToken,
VaultAddr: tt.fields.VaultAddr,
SecretId: tt.fields.SecretId,
RoleId: tt.fields.RoleId,
TokenIncreaseSeconds: tt.fields.TokenIncreaseSeconds,
TokenIncreaseInterval: tt.fields.TokenIncreaseInterval,
PathPrefix: tt.fields.PathPrefix,
SecretIdFile: tt.fields.SecretIdFile,
VaultWrappedToken: tt.fields.VaultWrappingToken,
VaultToken: tt.fields.VaultToken,
VaultAddr: tt.fields.VaultAddr,
SecretId: tt.fields.SecretId,
RoleId: tt.fields.RoleId,
PathPrefix: tt.fields.PathPrefix,
SecretIdFile: tt.fields.SecretIdFile,
}
if err := conf.Validate(); (err != nil) != tt.wantErr {
t.Errorf("Validate() error = %v, wantErr %v", err, tt.wantErr)
Expand Down
4 changes: 2 additions & 2 deletions internal/config/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,10 @@ func (a AcmeServerDomains) String() string {
}

type AcmeConfig struct {
Email string `json:"email"`
Email string `json:"email" validate:"email"`
AcmeUrl string `json:"acmeUrl"`
AcmeDnsProvider string `json:"acmeDnsProvider"`
AcmeCustomDnsServers []string `json:"acmeCustomDnsServers,omitempty"`
AcmeCustomDnsServers []string `json:"acmeCustomDnsServers,omitempty" validate:"dive,ip"`
}

func (conf AcmeConfig) Validate() error {
Expand Down
11 changes: 4 additions & 7 deletions internal/config/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,10 @@ func TestAcmeVaultServerConfigFromFile(t *testing.T) {
path: "../../contrib/server.json",
want: AcmeVaultServerConfig{
VaultConfig: VaultConfig{
VaultToken: "",
VaultAddr: "https://vault:8200",
SecretId: "secretId",
RoleId: "roleId",
TokenIncreaseSeconds: 0,
TokenIncreaseInterval: 0,
PathPrefix: "preprod",
VaultAddr: "https://vault:8200",
SecretId: "secretId",
RoleId: "roleId",
PathPrefix: "preprod",
},
AcmeConfig: AcmeConfig{
Email: "my@email.tld",
Expand Down

0 comments on commit a3cada0

Please sign in to comment.