The ssh username is provided in a very weird way
Closed this issue · 7 comments
@albertofaria I have limited time that I can dedicate to this so I will just dump my thoughts here and see where things go from there. Hope you don't mind :)
The subject kinda says it all, really. The fact that the first argument passed to podman exec
is interpreted as the ssh username is extremely weird and, in my opinion, should be changed.
I guess you've done it that way instead of introducing another custom command line option like --cloud-init
because you only get to hook those up for podman run
?
Anyway, I think a better way is needed. My idea is that crun-qemu could conditionally interpret the first few arguments as its own options if they are present, without requiring them to be there.
Extremely hacky POC (apologies for my Rust being terrible, haven't touched the language in months/years):
diff --git a/src/commands/exec.rs b/src/commands/exec.rs
index 7b19cfc..b9abf85 100644
--- a/src/commands/exec.rs
+++ b/src/commands/exec.rs
@@ -17,10 +17,16 @@ pub fn exec(global_args: &liboci_cli::GlobalOpts, args: &liboci_cli::Exec) -> Re
serde_json::from_reader(File::open(process_config_path).map(BufReader::new)?)?;
let command = process.args().as_ref().expect("command specified");
-
- let ssh_user = command
- .first()
- .expect("first command argument is user to ssh as into the vm");
+ let mut ssh_user = "abologna"; // TODO grab this from $USERNAME
+ let mut skip = 0;
+
+ let first_arg = command.first();
+ if first_arg.is_some() && first_arg.unwrap() == "-l" {
+ ssh_user = command
+ .get(1)
+ .expect("first command argument is user to ssh as into the vm");
+ skip = 2;
+ }
let mut new_command = vec![];
@@ -32,12 +38,12 @@ pub fn exec(global_args: &liboci_cli::GlobalOpts, args: &liboci_cli::Exec) -> Re
"-o".to_string(),
"StrictHostKeyChecking=no".to_string(),
"-l".to_string(),
- ssh_user.clone(),
+ ssh_user.to_string(),
"localhost".to_string(),
]);
}
- new_command.extend(command.iter().skip(1).cloned());
+ new_command.extend(command.iter().skip(skip).cloned());
if ssh_user == "-" && new_command.is_empty() {
new_command.push("/bin/bash".to_string());
With this patch applied and
#cloud-config
users:
- name: abologna
groups: sudo
shell: /bin/bash
sudo: 'ALL=(ALL) NOPASSWD:ALL'
ssh-authorized-keys:
- ssh-rsa AAAAB3Nza...
in examples/cloud-init/config/user-data
, I can just do
$ podman exec --latest whoami
abologna
which is much closer to the interactions I normally expect from podman. If I use the default cloud-init data, I can still do
$ podman exec --latest -- -l fedora
fedora
which is only slightly clunkier. The trade-off is IMO very much worth it.
Additionally, I think it would be very useful to adopt this model to allow for further expansion. For example:
$ podman exec --latest -- --ssh-options="-o whatever" actual-command
Let me know what you think!
Hi @andreabolognani, thanks for the suggestion, it is indeed a lot nicer than the current behavior. I'll write up a patch soon.
I'm just not sure I'd use $USERNAME as the default. That's the expected behavior for ssh, but not for containers. Simply complaining if the -l
option isn't specified may be better.
Since the idea is to keep the user experience as close as possible to native containers, I think doing something sensible when podman exec cmd
is used is important.
Given that podman exec
usually gives you a root shell inside the container, perhaps that's what should happen by default. Maybe crun-vm could automatically augment the user-provided cloud-init information to include an additional authorized key for root, the one which we already have inside the container. That way, assuming that cloud-init is supported by the guest OS and sshd is not configured to reject key-based authentication for root, you'd have a behavior that's basically identical to the one for standard containers.
The -l
option would then only need to be used if the default mechanism doesn't work. And we could have a podman run
option that disables all this magic for people who desire that.
The modified patch below seems to do the trick.
Of course there needs to be an opt-out for this mechanism, but something along these lines IMO would provide the smoothest experience when jumping between regular containers and crun-vm ones.
diff --git a/src/commands/create/first_boot.rs b/src/commands/create/first_boot.rs
index fb6b23a..69481ed 100644
--- a/src/commands/create/first_boot.rs
+++ b/src/commands/create/first_boot.rs
@@ -132,6 +132,8 @@ impl FirstBootConfig<'_> {
ssh_authorized_keys.push(self.container_public_key.into());
+ user_data_mapping.insert("disable_root".into(), false.into());
+
// create block device symlinks and udev rules
let block_device_symlinks = self.get_block_device_symlinks();
diff --git a/src/commands/exec.rs b/src/commands/exec.rs
index 7b19cfc..f4dcd81 100644
--- a/src/commands/exec.rs
+++ b/src/commands/exec.rs
@@ -17,10 +17,16 @@ pub fn exec(global_args: &liboci_cli::GlobalOpts, args: &liboci_cli::Exec) -> Re
serde_json::from_reader(File::open(process_config_path).map(BufReader::new)?)?;
let command = process.args().as_ref().expect("command specified");
-
- let ssh_user = command
- .first()
- .expect("first command argument is user to ssh as into the vm");
+ let mut ssh_user = "root";
+ let mut skip = 0;
+
+ let first_arg = command.first();
+ if first_arg.is_some() && first_arg.unwrap() == "-l" {
+ ssh_user = command
+ .get(1)
+ .expect("first command argument is user to ssh as into the vm");
+ skip = 2;
+ }
let mut new_command = vec![];
@@ -32,12 +38,12 @@ pub fn exec(global_args: &liboci_cli::GlobalOpts, args: &liboci_cli::Exec) -> Re
"-o".to_string(),
"StrictHostKeyChecking=no".to_string(),
"-l".to_string(),
- ssh_user.clone(),
+ ssh_user.to_string(),
"localhost".to_string(),
]);
}
- new_command.extend(command.iter().skip(1).cloned());
+ new_command.extend(command.iter().skip(skip).cloned());
if ssh_user == "-" && new_command.is_empty() {
new_command.push("/bin/bash".to_string());
Perfect, that sounds like the way to go.
@albertofaria I still had looking into this on my todo list, but realistically speaking I don't know if I would have managed to get to it anytime soon... Thanks a lot for taking care of it! The results look great :)
Sorry about that, let me know if you ever feel like taking another issue :)
No need to apologize! In case I wasn't clear, I wanted to see this fixed but was unable to find the time to do it myself. You coming in and addressing it is pretty much the best case scenario as far as I'm concerned ;) Thanks again!