-
Notifications
You must be signed in to change notification settings - Fork 4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add AllowedSigners support #23
Conversation
src/ssh/allowed_signer.rs
Outdated
for option in parts { | ||
let option = option.to_lowercase(); | ||
let (key, value) = match option.split_once('=') { | ||
Some(v) => v, | ||
None => (option.as_str(), ""), | ||
}; | ||
match key { | ||
"cert-authority" => cert_authority = true, | ||
"namespaces" => { | ||
if namespaces.is_some() { | ||
return Err(Error::InvalidFormat); | ||
} | ||
let namespaces_inner: Vec<&str> = value.trim_matches('"') | ||
.split(',') | ||
.collect(); | ||
namespaces = Some( | ||
namespaces_inner.iter() | ||
.map(|s| s.to_string()) | ||
.collect() | ||
); | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO: Rework the option parsing logic:
- Support spaces in double quotes. Not sure how this works for a namespace. Need to find the specs for namespace.
- Might need to enforce a format for namespace.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The key used for signing is specified using the -f option and may refer to either a private key, or a public key with the private half available via ssh-agent(1). An additional signature namespace, used to prevent signature confusion across different domains of use (e.g. file signing vs email signing) must be provided via the -n flag. Namespaces are arbitrary strings, and may include: “file” for file signing, “email” for email signing. For custom uses, it is recommended to use names following a NAMESPACE@YOUR.DOMAIN pattern to generate unambiguous namespaces.
src/ssh/allowed_signer.rs
Outdated
let principals = parts.pop_front().ok_or(Error::InvalidFormat)?; | ||
let principals: Vec<&str> = principals.split(',').collect(); | ||
let principals = principals.iter().map(|s| s.to_string()).collect(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we enforce a format for principals?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like this is not done in other APIs (cert.rs
) so maybe we don't need this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think principals are pretty open. There might be characters which cannot be in principals (","?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not enforcing format on principals
src/ssh/allowed_signer.rs
Outdated
/// Please refer to [ssh-keygen-1.ALLOWED_SIGNERS] for more details about the format. | ||
/// [ssh-keygen-1.ALLOWED_SIGNERS]: https://man.openbsd.org/ssh-keygen.1#ALLOWED_SIGNERS | ||
#[derive(Debug, PartialEq, Eq)] | ||
pub struct AllowedSigners { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You might just want to use a typedef here (I don't think there would ever be additional fields in this struct.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't implement foreign traits on typedef since typedef is a weak symlink. So I chose to wrap AllowedSigners(Vec<AllowedSigner>)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Few things around parsing
src/ssh/allowed_signer.rs
Outdated
} | ||
|
||
// Remove everything from the first # | ||
if let Some(line) = line.split('#').next() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will break for namespaces which contain pound
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was intended. But I checked and git's allowed_signers allows # in namespaces. I updated the parsing logic.
1487bc2
to
b9b2e2e
Compare
src/ssh/allowed_signer.rs
Outdated
let (mut head, mut rest) = s.split_once(char::is_whitespace) | ||
.ok_or(Error::InvalidAllowedSigner("missing key data".to_string()))?; | ||
|
||
let principals: Vec<&str> = head.split(',').collect(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since you just realloc as String below, you may as well make this Vec and get the allocations done in one step.
src/ssh/allowed_signer.rs
Outdated
|
||
let principals: Vec<&str> = head.split(',').collect(); | ||
if principals.iter().any(|p| p.is_empty()) { | ||
return Err(Error::InvalidAllowedSigner("principal cannot be empty".to_string())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you can have a sub enum to make this more clear and get rid of the static strings from here (they'll move to the std::fmt::Display of the new enum type)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
I just realized the timestamp specs is Another problem is UTC vs Local timezone. Right now, I return the local timezone timestamp as |
src/ssh/allowed_signer.rs
Outdated
|
||
impl AllowedSignerSplitter { | ||
/// Split the string by delimiters but keep the delimiters. | ||
pub(in self) fn new(s: &str) -> Self { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this different from pub(self)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No. I changed it to pub(self)
src/ssh/allowed_signer.rs
Outdated
// If we don't see any double quote in the token, we greedily parse the token until the | ||
// next whitespace. | ||
let mut s = String::new(); | ||
while !self.buffer.is_empty() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You might be able to get rid of the the last().unwrap()
by using a while let
here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've used while let
where I can. I also tried to use if let
to remove the unwrap but that made things look a little uglier since if let _ && cond
is still unstable.
src/ssh/allowed_signer.rs
Outdated
/// The timestamp can be enclosed by quotation marks. | ||
fn parse_timestamp(s: &str) -> Result<i64> { | ||
let mut s = s.trim_matches('"'); | ||
println!("s: {}", s); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rogue println
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a couple comments. Mostly I'm worried about panics in the splitter due to extensive use of [] and unwrap.
I've got rid of all the unwrap I can. The remaining ones are only initialization/conversion calls from constants that should not fail. Anyway, could you approve the GHA workflow to run the tests? |
ea1f496
to
e655af2
Compare
This adds a new API to parse AllowedSigner. The specification can be found here: https://man.openbsd.org/ssh-keygen.1#ALLOWED_SIGNERS
The parser rejects unsupported options instead of building a HashMap to store additional options.