Skip to content

Commit f09a790

Browse files
committed
Add kerberos realm name type
This used to use Hostname, but secret-op's Hostname had looser validation constraints
1 parent 9f460da commit f09a790

File tree

2 files changed

+58
-0
lines changed

2 files changed

+58
-0
lines changed

crates/stackable-operator/src/commons/networking.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,36 @@ impl Deref for Hostname {
3535
&self.0
3636
}
3737
}
38+
39+
/// A validated kerberos realm name type, for use in CRDs.
40+
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
41+
#[serde(try_from = "String", into = "String")]
42+
pub struct KerberosRealmName(
43+
#[validate(regex(path = "validation::KERBEROS_REALM_NAME_REGEX"))] String,
44+
);
45+
46+
impl TryFrom<String> for KerberosRealmName {
47+
type Error = ValidationErrors;
48+
49+
fn try_from(value: String) -> Result<Self, Self::Error> {
50+
validation::is_kerberos_realm_name(&value)?;
51+
Ok(KerberosRealmName(value))
52+
}
53+
}
54+
impl From<KerberosRealmName> for String {
55+
fn from(value: KerberosRealmName) -> Self {
56+
value.0
57+
}
58+
}
59+
impl Display for KerberosRealmName {
60+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61+
f.write_str(&self.0)
62+
}
63+
}
64+
impl Deref for KerberosRealmName {
65+
type Target = str;
66+
67+
fn deref(&self) -> &Self::Target {
68+
&self.0
69+
}
70+
}

crates/stackable-operator/src/validation.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,17 @@ const RFC_1035_LABEL_ERROR_MSG: &str = "a DNS-1035 label must consist of lower c
3232
// This is a label's max length in DNS (RFC 1035)
3333
const RFC_1035_LABEL_MAX_LENGTH: usize = 63;
3434

35+
// Technically Kerberos allows more realm names
36+
// (https://web.mit.edu/kerberos/krb5-1.21/doc/admin/realm_config.html#realm-name),
37+
// however, these are embedded in a lot of configuration files and other strings,
38+
// and will not always be quoted properly.
39+
//
40+
// Hence, restrict them to a reasonable subset. The convention is to use upper-case
41+
// DNS hostnames, so allow all characters used there.
42+
const KERBEROS_REALM_NAME_FMT: &str = "[-.a-zA-Z0-9]+";
43+
const KERBEROS_REALM_NAME_ERROR_MSG: &str =
44+
"Kerberos realm name must only contain alphanumeric characters, '-', and '.'";
45+
3546
// Lazily initialized regular expressions
3647
pub(crate) static RFC_1123_SUBDOMAIN_REGEX: LazyLock<Regex> = LazyLock::new(|| {
3748
Regex::new(&format!("^{RFC_1123_SUBDOMAIN_FMT}$"))
@@ -46,6 +57,11 @@ static RFC_1035_LABEL_REGEX: LazyLock<Regex> = LazyLock::new(|| {
4657
Regex::new(&format!("^{RFC_1035_LABEL_FMT}$")).expect("failed to compile RFC 1035 label regex")
4758
});
4859

60+
pub(crate) static KERBEROS_REALM_NAME_REGEX: LazyLock<Regex> = LazyLock::new(|| {
61+
Regex::new(&format!("^{KERBEROS_REALM_NAME_FMT}$"))
62+
.expect("failed to compile Kerberos realm name regex")
63+
});
64+
4965
#[derive(Debug)]
5066
pub struct ValidationErrors(Vec<ValidationError>);
5167

@@ -195,6 +211,15 @@ pub fn is_rfc_1035_label(value: &str) -> Result<(), ValidationErrors> {
195211
])
196212
}
197213

214+
pub fn is_kerberos_realm_name(value: &str) -> Result<(), ValidationErrors> {
215+
validate_all([validate_str_regex(
216+
value,
217+
&KERBEROS_REALM_NAME_REGEX,
218+
KERBEROS_REALM_NAME_ERROR_MSG,
219+
&["EXAMPLE.COM"],
220+
)])
221+
}
222+
198223
// mask_trailing_dash replaces the final character of a string with a subdomain safe
199224
// value if is a dash.
200225
fn mask_trailing_dash(mut name: String) -> String {

0 commit comments

Comments
 (0)