184 lines
6.1 KiB
Markdown
184 lines
6.1 KiB
Markdown
# Configuration Validation
|
|
|
|
We use a fork of https://github.com/go-playground/validator which can be found
|
|
at https://github.com/letsencrypt/validator.
|
|
|
|
## Usage
|
|
|
|
By default Boulder validates config files for all components with a registered
|
|
validator. Validating a config file for a given component is as simple as
|
|
running the component directly:
|
|
|
|
```shell
|
|
$ ./bin/boulder-observer -config test/config-next/observer.yml
|
|
Error validating config file "test/config-next/observer.yml": Key: 'ObsConf.MonConfs[1].Kind' Error:Field validation for 'Kind' failed on the 'oneof' tag
|
|
```
|
|
|
|
or by running the `boulder` binary and passing the component name as a
|
|
subcommand:
|
|
|
|
```shell
|
|
$ ./bin/boulder boulder-observer -config test/config-next/observer.yml
|
|
Error validating config file "test/config-next/observer.yml": Key: 'ObsConf.MonConfs[1].Kind' Error:Field validation for 'Kind' failed on the 'oneof' tag
|
|
```
|
|
|
|
## Struct Tag Tips
|
|
|
|
You can find the full list of struct tags supported by the validator [here]
|
|
(https://pkg.go.dev/github.com/go-playground/validator/v10#section-documentation).
|
|
The following are some tips for struct tags that are commonly used in our
|
|
configuration files.
|
|
|
|
### `required`
|
|
|
|
The required tag means that the field is not allowed to take its zero value, or
|
|
equivalently, is not allowed to be omitted. Note that this does not validate
|
|
that slices or maps have contents, it simply guarantees that they are not nil.
|
|
For fields of those types, you should use min=1 or similar to ensure they are
|
|
not empty.
|
|
|
|
There are also "conditional" required tags, such as `required_with`,
|
|
`required_with_all`, `required_without`, `required_without_all`, and
|
|
`required_unless`. These behave exactly like the basic required tag, but only if
|
|
their conditional (usually the presence or absence of one or more other named
|
|
fields) is met.
|
|
|
|
### `omitempty`
|
|
|
|
The omitempty tag allows a field to be empty, or equivalently, to take its zero
|
|
value. If the field is omitted, none of the other validation tags on the field
|
|
will be enforced. This can be useful for tags like validate="omitempty,url", for
|
|
a field which is optional, but must be a URL if it is present.
|
|
|
|
The omitempty tag can be "overruled" by the various conditional required tags.
|
|
For example, a field with tag `validate="omitempty,url,required_with=Foo"` is
|
|
allowed to be empty when field Foo is not present, but if field Foo is present,
|
|
then this field must be present and must be a URL.
|
|
|
|
### `-`
|
|
|
|
Normally, config validation descends into all struct-type fields, recursively
|
|
validating their fields all the way down. Sometimes this can pose a problem,
|
|
when a nested struct declares one of its fields as required, but a parent struct
|
|
wants to treat the whole nested struct as optional. The "-" tag tells the
|
|
validation not to recurse, marking the tagged field as optional, and therefore
|
|
making all of its sub-fields optional as well. We use this tag for many config
|
|
duration and password file struct valued fields which are optional in some
|
|
configs but required in others.
|
|
|
|
### `structonly`
|
|
|
|
The structonly tag allows a struct valued field to be empty, or equivalently, to
|
|
take its zero value, if it's not "overruled" by various conditional tags. If the
|
|
field is omitted the recursive validation of the structs fields will be skipped.
|
|
This can be useful for tags like `validate:"required_without=Foo,structonly"`
|
|
for a struct valued field which is only required, and thus should only be
|
|
validated, if field `Foo` is not present.
|
|
|
|
### `min=1`, `gte=1`
|
|
|
|
These validate that the value of integer valued field is greater than zero and
|
|
that the length of the slice or map is greater than zero.
|
|
|
|
For instance, the following would be valid config for a slice valued field
|
|
tagged with `required`.
|
|
```json
|
|
{
|
|
"foo": [],
|
|
}
|
|
```
|
|
|
|
But, only the following would be valid config for a slice valued field tagged
|
|
with `min=1`.
|
|
```json
|
|
{
|
|
"foo": ["bar"],
|
|
}
|
|
```
|
|
|
|
### `len`
|
|
|
|
Same as `eq` (equal to) but can also be used to validate the length of the
|
|
strings.
|
|
|
|
### `hostname_port`
|
|
|
|
The
|
|
[docs](https://pkg.go.dev/github.com/go-playground/validator/v10#hdr-HostPort)
|
|
for this tag are scant with detail, but it validates that the value is a valid
|
|
RFC 1123 hostname and port. It is used to validate many of the
|
|
`ListenAddress` and `DebugAddr` fields of our components.
|
|
|
|
#### Future Work
|
|
|
|
This tag is compatible with IPv4 addresses, but not IPv6 addresses. We should
|
|
consider fixing this in our fork of the validator.
|
|
|
|
### `dive`
|
|
|
|
This tag is used to validate the values of a slice or map. For instance, the
|
|
following would be valid config for a slice valued field (`[]string`) tagged
|
|
with `min=1,dive,oneof=bar baz`.
|
|
|
|
```json
|
|
{
|
|
"foo": ["bar", "baz"],
|
|
}
|
|
```
|
|
|
|
Note that the `dive` tag introduces an order-dependence in writing tags: tags
|
|
that come before `dive` apply to the current field, while tags that come after
|
|
`dive` apply to the current field's child values. In the example above: `min=1`
|
|
applies to the length of the slice (`[]string`), while `oneof=bar baz` applies
|
|
to the value of each string in the slice.
|
|
|
|
We can also use `dive` to validate the values of a map. For instance, the
|
|
following would be valid config for a map valued field (`map[string]string`)
|
|
tagged with `min=1,dive,oneof=one two`.
|
|
|
|
```json
|
|
{
|
|
"foo": {
|
|
"bar": "one",
|
|
"baz": "two"
|
|
},
|
|
}
|
|
```
|
|
|
|
`dive` can also be invoked multiple times to validate the values of nested
|
|
slices or maps. For instance, the following would be valid config for a slice of
|
|
slice valued field (`[][]string`) tagged with `min=1,dive,min=2,dive,oneof=bar
|
|
baz`.
|
|
|
|
```json
|
|
{
|
|
"foo": [
|
|
["bar", "baz"],
|
|
["baz", "bar"],
|
|
],
|
|
}
|
|
```
|
|
|
|
- `min=1` will be applied to the outer slice (`[]`).
|
|
- `min=2` will be applied to inner slice (`[]string`).
|
|
- `oneof=bar baz` will be applied to each string in the inner slice.
|
|
|
|
### `keys` and `endkeys`
|
|
|
|
These tags are used to validate the keys of a map. For instance, the following
|
|
would be valid config for a map valued field (`map[string]string`) tagged with
|
|
`min=1,dive,keys,eq=1|eq=2,endkeys,required`.
|
|
|
|
```json
|
|
{
|
|
"foo": {
|
|
"1": "bar",
|
|
"2": "baz",
|
|
},
|
|
}
|
|
```
|
|
|
|
- `min=1` will be applied to the map itself
|
|
- `eq=1|eq=2` will be applied to the map keys
|
|
- `required` will be applied to map values
|