Commit

Author:

Hash:

Timestamp:

+153 -32 +/-7 browse

Kevin Schoon [me@kevinschoon.com]

77f7ce72236d9a9a479350816556a5009326a0fe

Fri, 01 Aug 2025 19:07:01 +0000 (3 months ago)

support git-receive-pack in ayllu-shell
1diff --git a/.gitignore b/.gitignore
2index 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
14index 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
32index 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
69index 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
202index 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
215index 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
238index 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)"