Author:
Hash:
Timestamp:
+153 -32 +/-7 browse
Kevin Schoon [me@kevinschoon.com]
77f7ce72236d9a9a479350816556a5009326a0fe
Fri, 01 Aug 2025 19:07:01 +0000 (3 months ago)
| 1 | diff --git a/.gitignore b/.gitignore |
| 2 | index b6fa87a..036ad28 100644 |
| 3 | --- a/.gitignore |
| 4 | +++ b/.gitignore |
| 5 | @@ -3,6 +3,7 @@ ayllu-xmpp/db |
| 6 | ayllu-build/db |
| 7 | target |
| 8 | config.toml |
| 9 | + config.ssh-test.toml |
| 10 | node_modules |
| 11 | static/*.css |
| 12 | package-lock.json |
| 13 | diff --git a/ayllu-keys/src/main.rs b/ayllu-keys/src/main.rs |
| 14 | index 02e38b9..4602c97 100644 |
| 15 | --- a/ayllu-keys/src/main.rs |
| 16 | +++ b/ayllu-keys/src/main.rs |
| 17 | @@ -112,11 +112,8 @@ fn main() -> ExitCode { |
| 18 | |
| 19 | if let Some(identity) = identify(&config, &args.ca_key_type, &args.certificate) { |
| 20 | identity.authorized_keys.iter().for_each(|authorized_key| { |
| 21 | - println!( |
| 22 | - "restrict,pty,command=\"{ayllu_shell_path} --config {GLOBAL_AYLLU_CONFIG} --username={}\" {}", |
| 23 | - identity.username, authorized_key.0 |
| 24 | - ); |
| 25 | - }); |
| 26 | + println!("restrict,pty {}", authorized_key.0); |
| 27 | + }); |
| 28 | } |
| 29 | |
| 30 | ExitCode::SUCCESS |
| 31 | diff --git a/ayllu-shell/src/config.rs b/ayllu-shell/src/config.rs |
| 32 | index 0737340..1fccdd1 100644 |
| 33 | --- a/ayllu-shell/src/config.rs |
| 34 | +++ b/ayllu-shell/src/config.rs |
| 35 | @@ -30,7 +30,9 @@ impl Shell { |
| 36 | |
| 37 | #[derive(Serialize, Deserialize, Clone)] |
| 38 | pub struct Config { |
| 39 | + #[serde(default = "Config::default_log_level")] |
| 40 | pub log_level: String, |
| 41 | + pub base_dir: Option<PathBuf>, |
| 42 | #[serde(default = "Vec::new")] |
| 43 | pub identities: Vec<Identity>, |
| 44 | #[serde(default = "Shell::default")] |
| 45 | @@ -39,4 +41,21 @@ pub struct Config { |
| 46 | pub collections: Vec<Collection>, |
| 47 | } |
| 48 | |
| 49 | - impl Configurable for Config {} |
| 50 | + impl Config { |
| 51 | + fn default_log_level() -> String { |
| 52 | + String::from("INFO") |
| 53 | + } |
| 54 | + } |
| 55 | + |
| 56 | + impl Configurable for Config { |
| 57 | + fn initialize(&mut self) -> Result<(), ayllu_config::Error> { |
| 58 | + if let Some(base_dir) = &self.base_dir { |
| 59 | + self.collections.iter_mut().for_each(|collection| { |
| 60 | + if collection.path.is_relative() { |
| 61 | + collection.path = base_dir.join(&collection.path); |
| 62 | + }; |
| 63 | + }); |
| 64 | + }; |
| 65 | + Ok(()) |
| 66 | + } |
| 67 | + } |
| 68 | diff --git a/ayllu-shell/src/main.rs b/ayllu-shell/src/main.rs |
| 69 | index 1ffff2f..993a8c9 100644 |
| 70 | --- a/ayllu-shell/src/main.rs |
| 71 | +++ b/ayllu-shell/src/main.rs |
| 72 | @@ -1,11 +1,21 @@ |
| 73 | - use std::path::PathBuf; |
| 74 | + use std::path::{Path, PathBuf}; |
| 75 | |
| 76 | - use clap::Parser; |
| 77 | + use ayllu_git::Wrapper; |
| 78 | + use clap::{Parser, Subcommand}; |
| 79 | |
| 80 | mod config; |
| 81 | mod error; |
| 82 | mod ui; |
| 83 | |
| 84 | + #[derive(Subcommand, Debug)] |
| 85 | + enum Commands { |
| 86 | + /// Invokes git-receive-pack with some particularities |
| 87 | + GitReceivePack { |
| 88 | + /// Path to the git repository |
| 89 | + path: PathBuf, |
| 90 | + }, |
| 91 | + } |
| 92 | + |
| 93 | #[derive(Parser, Debug)] |
| 94 | #[clap( |
| 95 | version, |
| 96 | @@ -20,18 +30,20 @@ expected to be paired with ayllu-keys and an OpenSSH server. |
| 97 | "# |
| 98 | )] |
| 99 | struct Arguments { |
| 100 | - /// logging level [ERROR,WARN,INFO,DEBUG,TRACE] |
| 101 | - #[clap(short, long)] |
| 102 | - level: Option<String>, |
| 103 | - #[clap(short, long)] |
| 104 | + /// sh compatibility |
| 105 | + #[clap(short, action)] |
| 106 | + c: Option<String>, |
| 107 | + /// Path to a configuration file |
| 108 | + #[clap(long)] |
| 109 | config: Option<PathBuf>, |
| 110 | /// Specify the user identity to assume |
| 111 | #[clap(short, long)] |
| 112 | username: Option<String>, |
| 113 | + #[command(subcommand)] |
| 114 | + command: Option<Commands>, |
| 115 | } |
| 116 | |
| 117 | - fn main() -> Result<(), Box<dyn std::error::Error>> { |
| 118 | - let args = Arguments::parse(); |
| 119 | + fn execute(args: Arguments) -> Result<(), Box<dyn std::error::Error>> { |
| 120 | let config: config::Config = ayllu_config::Reader::load(args.config.as_deref())?; |
| 121 | let username = args |
| 122 | .username |
| 123 | @@ -40,12 +52,70 @@ fn main() -> Result<(), Box<dyn std::error::Error>> { |
| 124 | .identities |
| 125 | .iter() |
| 126 | .find(|identity| identity.username == username); |
| 127 | - print!("{}", config.shell.motd); |
| 128 | - println!("\nYou are authenticated as: {}\n", username); |
| 129 | - let menu = ui::Prompt { |
| 130 | - config: &config, |
| 131 | - identity, |
| 132 | - }; |
| 133 | - menu.execute(None, None)?; |
| 134 | + match args.command { |
| 135 | + Some(Commands::GitReceivePack { ref path }) => { |
| 136 | + eprintln!("The path is: {path:?}"); |
| 137 | + let resolved = if path.is_relative() { |
| 138 | + if let Some(base_dir) = config.base_dir { |
| 139 | + &base_dir.join(path) |
| 140 | + } else { |
| 141 | + path |
| 142 | + } |
| 143 | + } else { |
| 144 | + path |
| 145 | + }; |
| 146 | + eprintln!("Resolved: {resolved:?}"); |
| 147 | + eprintln!("Collections: {:?}", config.collections); |
| 148 | + match config |
| 149 | + .collections |
| 150 | + .iter() |
| 151 | + .find(|collection| collection.path == resolved.parent().unwrap()) |
| 152 | + { |
| 153 | + Some(_) => { |
| 154 | + let repository = |
| 155 | + if ayllu_git::git_dir(resolved).is_ok_and(|is_git_dir| is_git_dir) { |
| 156 | + eprintln!("opening existing repository"); |
| 157 | + Wrapper::new(resolved)? |
| 158 | + } else { |
| 159 | + eprintln!("creating new repository"); |
| 160 | + Wrapper::create(resolved, true)? |
| 161 | + }; |
| 162 | + repository.receive_pack()?; |
| 163 | + } |
| 164 | + None => { |
| 165 | + eprintln!("never resolved the collection"); |
| 166 | + return Err(format!("Cannot find repository: {resolved:?}").into()); |
| 167 | + } |
| 168 | + } |
| 169 | + } |
| 170 | + None => { |
| 171 | + print!("{}", config.shell.motd); |
| 172 | + println!("\nYou are authenticated as: {}\n", username); |
| 173 | + let menu = ui::Prompt { |
| 174 | + config: &config, |
| 175 | + identity, |
| 176 | + }; |
| 177 | + menu.execute(None, None)?; |
| 178 | + } |
| 179 | + } |
| 180 | + |
| 181 | Ok(()) |
| 182 | } |
| 183 | + |
| 184 | + // TODO: Add RBAC style permissions per identity |
| 185 | + fn main() -> Result<(), Box<dyn std::error::Error>> { |
| 186 | + eprintln!("Args: {:?}", std::env::args()); |
| 187 | + let args = Arguments::parse(); |
| 188 | + if let Some(cmd) = args.c { |
| 189 | + let unescaped = cmd.replace("\'", "").replace("\"", ""); |
| 190 | + let parts = unescaped.split(" ").into_iter().fold( |
| 191 | + vec!["ayllu-shell".to_string()], |
| 192 | + |mut accm, part| { |
| 193 | + accm.push(part.to_string()); |
| 194 | + accm |
| 195 | + }, |
| 196 | + ); |
| 197 | + return execute(Arguments::parse_from(parts)); |
| 198 | + }; |
| 199 | + execute(args) |
| 200 | + } |
| 201 | diff --git a/containers/ayllu/Containerfile b/containers/ayllu/Containerfile |
| 202 | index 00c74b8..ccb8167 100644 |
| 203 | --- a/containers/ayllu/Containerfile |
| 204 | +++ b/containers/ayllu/Containerfile |
| 205 | @@ -132,7 +132,7 @@ RUN \ |
| 206 | # RUN adduser -D -s /bin/sh -h /home/rudolfs rudolfs |
| 207 | |
| 208 | # default to the non-root ayllu user |
| 209 | - USER ayllu |
| 210 | + # USER ayllu |
| 211 | WORKDIR /var/lib/ayllu |
| 212 | |
| 213 | CMD ["/usr/bin/ayllu", "serve"] |
| 214 | diff --git a/crates/git/src/wrapper.rs b/crates/git/src/wrapper.rs |
| 215 | index f4b9431..f432463 100644 |
| 216 | --- a/crates/git/src/wrapper.rs |
| 217 | +++ b/crates/git/src/wrapper.rs |
| 218 | @@ -986,6 +986,18 @@ impl Wrapper { |
| 219 | Ok(child) |
| 220 | } |
| 221 | |
| 222 | + /// Handle git-receive-pack on the repository |
| 223 | + pub fn receive_pack(&self) -> Result<(), Error> { |
| 224 | + let repository_path = Path::new(&self.path); |
| 225 | + let mut command = std::process::Command::new("git"); |
| 226 | + command.args(["receive-pack", &repository_path.to_str().unwrap()]); |
| 227 | + command.stdin(Stdio::inherit()); |
| 228 | + command.stdout(Stdio::inherit()); |
| 229 | + let mut child = command.spawn()?; |
| 230 | + child.wait()?; |
| 231 | + Ok(()) |
| 232 | + } |
| 233 | + |
| 234 | /// verify the signature of the given commit |
| 235 | /// TODO: Can this be done with libgit2? |
| 236 | pub fn verify(&self, id: &str) -> Result<Option<bool>, Error> { |
| 237 | diff --git a/scripts/ayllu_shell_test.sh b/scripts/ayllu_shell_test.sh |
| 238 | index c015cc0..4dbeefc 100755 |
| 239 | --- a/scripts/ayllu_shell_test.sh |
| 240 | +++ b/scripts/ayllu_shell_test.sh |
| 241 | @@ -8,31 +8,48 @@ set -e |
| 242 | DEBUGGING="-d" |
| 243 | |
| 244 | AYLLU_SRC="$PWD" |
| 245 | + AYLLU_SHELL_BINARY="$AYLLU_SRC/target/x86_64-unknown-linux-musl/debug/ayllu-shell" |
| 246 | + AYLLU_KEYS_BINARY="$AYLLU_SRC/target/x86_64-unknown-linux-musl/debug/ayllu-keys" |
| 247 | LOCAL_SSH_PORT="2222" |
| 248 | - KEYS_COMMAND="/src/target/x86_64-unknown-linux-musl/debug/ayllu-keys --ayllu-shell=/src/target/x86_64-unknown-linux-musl/debug/ayllu-shell %u %h %t %k" |
| 249 | + KEYS_COMMAND="/usr/bin/ayllu-keys --ayllu-shell=/usr/bin/ayllu-shell %u %h %t %k" |
| 250 | + IMAGE_NAME="registry.ayllu-forge.org/ayllu/ayllu:multiuser-main" |
| 251 | + IMAGE_NAME="registry-auth.ayllu-forge.org/ayllu/ayllu:multiuser-main" |
| 252 | + |
| 253 | + TEST_CONFIG_FILE=config.ssh-test.toml |
| 254 | |
| 255 | cargo build --target x86_64-unknown-linux-musl --package ayllu-keys |
| 256 | cargo build --target x86_64-unknown-linux-musl --package ayllu-shell |
| 257 | |
| 258 | PUBLIC_KEY="$(find ~/.ssh -name '*.pub' -exec cat {} \; | head -n 1)" |
| 259 | |
| 260 | + make_cfg() { |
| 261 | + cat >$TEST_CONFIG_FILE <<EOF |
| 262 | + base_dir = "/usr/share/ayllu/repos" |
| 263 | + log_level = "DEBUG" |
| 264 | + [[collections]] |
| 265 | + name = "demo" |
| 266 | + description = "demo collection" |
| 267 | + path = "demo" |
| 268 | + [[identities]] |
| 269 | + username = "demo" |
| 270 | + email = "demo@example.org" |
| 271 | + authorized_keys = ["$PUBLIC_KEY"] |
| 272 | + EOF |
| 273 | + } |
| 274 | + |
| 275 | init_env() { |
| 276 | cat<<EOF |
| 277 | passwd -d root |
| 278 | adduser -h /home/demo -D demo |
| 279 | passwd -d demo |
| 280 | - adduser -h /home/ayllu -D ayllu |
| 281 | passwd -d ayllu |
| 282 | ssh-keygen -A |
| 283 | mkdir -p /home/demo/.ssh |
| 284 | echo $PUBLIC_KEY > /home/demo/.ssh/authorized_keys |
| 285 | chmod 644 /home/demo/.ssh/authorized_keys |
| 286 | - cat /etc/ayllu/config.example.toml > /etc/ayllu/config.toml |
| 287 | - echo "[[identities]]" >> /etc/ayllu/config.toml |
| 288 | - echo username = \"demo\" >> /etc/ayllu/config.toml |
| 289 | - echo email = \"demo@example.org\" >> /etc/ayllu/config.toml |
| 290 | - echo authorized_keys = [\"$PUBLIC_KEY\"] >> /etc/ayllu/config.toml |
| 291 | - /usr/sbin/sshd $DEBUGGING -D -o PermitTTY=yes -o PermitUserEnvironment=AYLLU_USERNAME -o PasswordAuthentication=no -o AuthorizedKeysCommand="$KEYS_COMMAND" -o AuthorizedKeysCommandUser=root |
| 292 | + mkdir -p /usr/share/ayllu/repos/demo |
| 293 | + chown -R ayllu:ayllu /usr/share/ayllu |
| 294 | + /usr/sbin/sshd $DEBUGGING -D -o PermitTTY=yes -o PasswordAuthentication=no -o AuthorizedKeysCommand="$KEYS_COMMAND" -o AuthorizedKeysCommandUser=root |
| 295 | EOF |
| 296 | } |
| 297 | |
| 298 | @@ -40,7 +57,12 @@ echo "To open a remote shell:" |
| 299 | echo "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no demo@127.0.0.1 -p 2222" |
| 300 | echo "Or run scripts/ayllu_shell_ssh.sh" |
| 301 | |
| 302 | + make_cfg |
| 303 | + |
| 304 | podman run \ |
| 305 | --name ayllu-shell-test \ |
| 306 | - --rm -ti --user root -v $AYLLU_SRC:/src -v $PWD/config.example.toml:/etc/ayllu/config.example.toml:ro \ |
| 307 | - -p $LOCAL_SSH_PORT:22 registry.ayllu-forge.org/ayllu/ayllu:multiuser-main sh -c "$(init_env)" |
| 308 | + --rm --replace -ti --user root \ |
| 309 | + -v $AYLLU_SHELL_BINARY:/usr/bin/ayllu-shell \ |
| 310 | + -v $AYLLU_KEYS_BINARY:/usr/bin/ayllu-keys \ |
| 311 | + -v $PWD/$TEST_CONFIG_FILE:/etc/ayllu/config.toml:ro \ |
| 312 | + -p $LOCAL_SSH_PORT:22 "$IMAGE_NAME" sh -c "$(init_env)" |