dynamic_config_server/config/
refusal.rs1use std::fmt;
9
10use crate::auth::MIN_TOKEN_LEN;
11
12#[derive(Debug, Clone, PartialEq, Eq)]
18#[non_exhaustive]
19pub enum Refusal {
20 NoSections,
22 NoClients,
24 DuplicateSection {
26 application: String,
28 profile: String,
30 },
31 UnroutableSection {
34 application: String,
36 profile: String,
38 part: &'static str,
40 },
41 DuplicateClient {
43 name: String,
45 },
46 DuplicateToken,
48 WeakToken {
50 client: String,
52 },
53 AnonymousNotAllowed {
55 client: String,
57 },
58 SeveralAnonymousClients,
61 UnservedGrant {
63 client: String,
65 application: String,
67 },
68 ExposedBind {
70 bind: String,
72 },
73 KubernetesAuth {
77 reason: String,
79 },
80 UnparsableBind {
82 bind: String,
84 },
85 TlsUnsupported,
87 InsecureWithTls,
90 TlsPathMissing {
92 key: &'static str,
94 },
95 RevocationUnsupported,
98}
99
100impl fmt::Display for Refusal {
101 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102 match self {
103 Self::NoSections => {
104 f.write_str("no `sections` are configured: this server would serve nothing at all")
105 }
106 Self::NoClients => f.write_str(
107 "no `clients` are configured: nothing could ever be read. Add a client with \
108 a `token` and the `applications` it may read, or an anonymous one with \
109 `allow_anonymous = true`",
110 ),
111 Self::DuplicateSection {
112 application,
113 profile,
114 } => write!(
115 f,
116 "two `sections` claim `{application}`/`{profile}`; one application and \
117 profile is served by exactly one section"
118 ),
119 Self::UnroutableSection {
120 application,
121 profile,
122 part,
123 } => write!(
124 f,
125 "the section `{application}`/`{profile}` has a `{part}` no request can \
126 name: a path segment is up to 64 characters, starts with a letter or a \
127 digit, and carries only letters, digits, `.`, `_` and `-`. The server \
128 would start, report ready and answer `404` for that section forever"
129 ),
130 Self::DuplicateClient { name } => {
131 write!(f, "two `clients` are named `{name}`; names identify a caller in the audit log and must be unique")
132 }
133 Self::DuplicateToken => f.write_str(
134 "two `clients` share a `token`; the first listed would silently win every \
135 request and the audit log would name the wrong caller",
136 ),
137 Self::WeakToken { client } => write!(
138 f,
139 "the `token` for client `{client}` is shorter than {MIN_TOKEN_LEN} characters"
140 ),
141 Self::AnonymousNotAllowed { client } => write!(
142 f,
143 "client `{client}` has no `token`, which makes it the anonymous caller; set \
144 `allow_anonymous = true` to say that is intended, or give it a token"
145 ),
146 Self::KubernetesAuth { reason } => {
147 write!(f, "[kubernetes] auth refused: {reason}")
148 }
149 Self::SeveralAnonymousClients => f.write_str(
150 "more than one client has no `token`; there is one anonymous caller, so it \
151 can have only one set of grants",
152 ),
153 Self::UnservedGrant {
154 client,
155 application,
156 } => write!(
157 f,
158 "client `{client}` is granted `{application}`, which no section serves; a \
159 grant that matches nothing is a typo that reads as a working deployment"
160 ),
161 Self::ExposedBind { bind } => write!(
162 f,
163 "`bind` is `{bind}`, which is not loopback, and this server is terminating no \
164 TLS: that would put configuration — secrets included — on the network in the \
165 clear. Terminate TLS here with a `[server.tls]` section, or put a terminator \
166 in front of it and set `insecure = true` to say so, or bind loopback"
167 ),
168 Self::UnparsableBind { bind } => write!(
169 f,
170 "`bind` is `{bind}`, which is not a literal `address:port`; a hostname is \
171 refused rather than resolved"
172 ),
173 Self::TlsUnsupported => f.write_str(
174 "`[server.tls]` is configured, but this binary was built without the `tls` \
175 feature and contains no TLS at all. Rebuild it with `--features tls`, or \
176 remove `[server.tls]` and put a terminator in front",
177 ),
178 Self::InsecureWithTls => f.write_str(
179 "`insecure = true` is set and `[server.tls]` is configured. `insecure` \
180 acknowledges that this server's own socket is unencrypted, which is no longer \
181 true — remove it, so that removing the TLS section later refuses again \
182 instead of quietly serving in the clear",
183 ),
184 Self::TlsPathMissing { key } => {
185 write!(f, "`tls.{key}` is empty; it has to name a PEM file")
186 }
187 Self::RevocationUnsupported => f.write_str(
188 "`tls.crl` is configured, but this server checks no certificate revocation and \
189 will not pretend to. A CRL whose `nextUpdate` has passed is accepted silently \
190 by default, so the list would stop being true the moment it stopped being \
191 refreshed and nothing would report it; the one setting that refuses a stale \
192 list refuses every client along with it, which turns a publishing hiccup into \
193 an outage for every service at once. Remove the key. Issue short-lived client \
194 certificates, and revoke the `token` — delete the client's line and restart — \
195 which is the credential that actually authorises here",
196 ),
197 }
198 }
199}
200
201impl std::error::Error for Refusal {}