Author:
Hash:
Timestamp:
+91 -20 +/-8 browse
Kevin Schoon [me@kevinschoon.com]
eaa0fbd8ca871db8efb65ceda8f82b4f7c94a598
Sat, 11 Jul 2026 10:56:35 +0000 (2 months ago)
| 1 | diff --git a/Cargo.lock b/Cargo.lock |
| 2 | index 9ead26e..2c3f3ef 100644 |
| 3 | --- a/Cargo.lock |
| 4 | +++ b/Cargo.lock |
| 5 | @@ -371,6 +371,7 @@ version = "0.5.0" |
| 6 | dependencies = [ |
| 7 | "ayllu_cmd", |
| 8 | "ayllu_config", |
| 9 | + "ayllu_database", |
| 10 | "ayllu_git", |
| 11 | "ayllu_identity", |
| 12 | "console", |
| 13 | diff --git a/ayllu-keys/src/main.rs b/ayllu-keys/src/main.rs |
| 14 | index 3c4b97b..2404c1e 100644 |
| 15 | --- a/ayllu-keys/src/main.rs |
| 16 | +++ b/ayllu-keys/src/main.rs |
| 17 | @@ -79,8 +79,12 @@ fn main() -> ExitCode { |
| 18 | ); |
| 19 | |
| 20 | if let Some(identity) = identify(&config, &args.ca_key_type, &args.certificate) { |
| 21 | + let ayllu_username = identity.username.to_string(); |
| 22 | identity.authorized_keys.iter().for_each(|authorized_key| { |
| 23 | - println!("restrict,pty {}", authorized_key.0); |
| 24 | + println!( |
| 25 | + "restrict,pty,environment=\"AYLLU_USER={ayllu_username}\" {}", |
| 26 | + authorized_key.0 |
| 27 | + ); |
| 28 | }); |
| 29 | } |
| 30 | |
| 31 | diff --git a/ayllu-shell/Cargo.toml b/ayllu-shell/Cargo.toml |
| 32 | index 476f5cd..0c9f999 100644 |
| 33 | --- a/ayllu-shell/Cargo.toml |
| 34 | +++ b/ayllu-shell/Cargo.toml |
| 35 | @@ -9,6 +9,7 @@ ayllu_cmd = { path = "../crates/cmd" } |
| 36 | ayllu_config = { path = "../crates/config" } |
| 37 | ayllu_git = { path = "../crates/git"} |
| 38 | ayllu_identity = { path = "../crates/identity" } |
| 39 | + ayllu_database = { path = "../crates/database" } |
| 40 | |
| 41 | serde = { workspace = true } |
| 42 | thiserror.workspace = true |
| 43 | diff --git a/ayllu-shell/src/config.rs b/ayllu-shell/src/config.rs |
| 44 | index 920fb73..ee5b8b8 100644 |
| 45 | --- a/ayllu-shell/src/config.rs |
| 46 | +++ b/ayllu-shell/src/config.rs |
| 47 | @@ -40,7 +40,21 @@ pub struct Config { |
| 48 | pub identities: Vec<Identity>, |
| 49 | } |
| 50 | |
| 51 | - impl Configurable for Config {} |
| 52 | + impl Configurable for Config { |
| 53 | + fn validate(&mut self) -> Result<(), ayllu_config::Error> { |
| 54 | + if self |
| 55 | + .identities |
| 56 | + .iter() |
| 57 | + .find(|identity| identity.username.to_lowercase() == "ayllu") |
| 58 | + .is_some() |
| 59 | + { |
| 60 | + return Err(ayllu_config::Error::Validation(String::from( |
| 61 | + "An identity with the username Ayllu is not permitted", |
| 62 | + ))); |
| 63 | + } |
| 64 | + Ok(()) |
| 65 | + } |
| 66 | + } |
| 67 | |
| 68 | #[cfg(test)] |
| 69 | mod test { |
| 70 | diff --git a/ayllu-shell/src/main.rs b/ayllu-shell/src/main.rs |
| 71 | index 713edf7..a705de0 100644 |
| 72 | --- a/ayllu-shell/src/main.rs |
| 73 | +++ b/ayllu-shell/src/main.rs |
| 74 | @@ -12,10 +12,24 @@ fn execute(args: Command) -> Result<(), Error> { |
| 75 | let username = args |
| 76 | .username |
| 77 | .unwrap_or(std::env::var("USER").expect("USER is not set")); |
| 78 | - let _identity = config |
| 79 | - .identities |
| 80 | - .iter() |
| 81 | - .find(|identity| identity.username == username); |
| 82 | + println!("System user is reported as: {username}"); |
| 83 | + let ayllu_username = std::env::var("AYLLU_USER").ok(); |
| 84 | + let identity = if let Some(ayllu_username) = ayllu_username { |
| 85 | + config |
| 86 | + .identities |
| 87 | + .iter() |
| 88 | + .find(|identity| identity.username == ayllu_username) |
| 89 | + } else { |
| 90 | + println!("AYLLU_USER was not specified and so you are an administrator!"); |
| 91 | + Some(&ayllu_identity::Identity::administrator(&username)) |
| 92 | + }; |
| 93 | + |
| 94 | + if identity.is_none() { |
| 95 | + panic!("BUG: User cannot be identitfied but the login was already authorized") |
| 96 | + }; |
| 97 | + |
| 98 | + let identity = identity.unwrap(); |
| 99 | + |
| 100 | match args.command { |
| 101 | Some(Subcommand::GitReceivePack { path }) => { |
| 102 | let resolved = if let Some(base_dir) = config.common.base_path { |
| 103 | @@ -73,7 +87,7 @@ fn execute(args: Command) -> Result<(), Error> { |
| 104 | } |
| 105 | None => { |
| 106 | print!("{}", config.shell.motd); |
| 107 | - println!("\nYou are authenticated as: {username}\n"); |
| 108 | + println!("\nYou are authenticated as: {identity}\n"); |
| 109 | crate::ui::present(&config)?; |
| 110 | } |
| 111 | } |
| 112 | diff --git a/containers/Containerfile.debian-shell-test b/containers/Containerfile.debian-shell-test |
| 113 | new file mode 100644 |
| 114 | index 0000000..59b23d3 |
| 115 | --- /dev/null |
| 116 | +++ b/containers/Containerfile.debian-shell-test |
| 117 | @@ -0,0 +1,16 @@ |
| 118 | + FROM docker.io/library/debian:testing |
| 119 | + |
| 120 | + RUN apt-get update && apt-get install -yyq \ |
| 121 | + openssh-server \ |
| 122 | + git \ |
| 123 | + sqlite3 \ |
| 124 | + sudo |
| 125 | + |
| 126 | + RUN useradd -m -d /home/ayllu -s /usr/bin/ayllu-shell ayllu |
| 127 | + RUN mkdir /src && chown ayllu:ayllu /src |
| 128 | + |
| 129 | + RUN passwd -d root |
| 130 | + RUN passwd -d ayllu |
| 131 | + |
| 132 | + USER ayllu |
| 133 | + WORKDIR /src |
| 134 | diff --git a/crates/identity/src/lib.rs b/crates/identity/src/lib.rs |
| 135 | index 84f8f05..861abe1 100644 |
| 136 | --- a/crates/identity/src/lib.rs |
| 137 | +++ b/crates/identity/src/lib.rs |
| 138 | @@ -43,3 +43,23 @@ pub struct Identity { |
| 139 | #[serde(default = "Vec::new")] |
| 140 | pub profiles: Vec<WebItem>, |
| 141 | } |
| 142 | + |
| 143 | + impl std::fmt::Display for Identity { |
| 144 | + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| 145 | + write!(f, "{} ({})", self.username, self.email) |
| 146 | + } |
| 147 | + } |
| 148 | + |
| 149 | + impl Identity { |
| 150 | + /// Return the administrator account based on the system user |
| 151 | + pub fn administrator(username: &str) -> Self { |
| 152 | + Identity { |
| 153 | + username: username.to_string(), |
| 154 | + authorized_keys: Vec::default(), |
| 155 | + email: String::default(), |
| 156 | + avatar: None, |
| 157 | + tagline: Some(String::from("Administrator")), |
| 158 | + profiles: Vec::default(), |
| 159 | + } |
| 160 | + } |
| 161 | + } |
| 162 | diff --git a/scripts/ayllu_shell_test.sh b/scripts/ayllu_shell_test.sh |
| 163 | index 4dbeefc..daf24bb 100755 |
| 164 | --- a/scripts/ayllu_shell_test.sh |
| 165 | +++ b/scripts/ayllu_shell_test.sh |
| 166 | @@ -4,27 +4,32 @@ set -e |
| 167 | # Your public SSH keys are passed into the container |
| 168 | # Expects a recent version of ayllu:multiuser-main on your system |
| 169 | |
| 170 | + podman build \ |
| 171 | + -f containers/Containerfile.debian-shell-test \ |
| 172 | + -t localhost/ayllu-debian-shell-test . |
| 173 | + |
| 174 | # NOTE that the session will terminate after closing the first connection. |
| 175 | DEBUGGING="-d" |
| 176 | |
| 177 | AYLLU_SRC="$PWD" |
| 178 | AYLLU_SHELL_BINARY="$AYLLU_SRC/target/x86_64-unknown-linux-musl/debug/ayllu-shell" |
| 179 | AYLLU_KEYS_BINARY="$AYLLU_SRC/target/x86_64-unknown-linux-musl/debug/ayllu-keys" |
| 180 | + AYLLU_MIGRATE_BINARY="$AYLLU_SRC/target/x86_64-unknown-linux-musl/debug/ayllu-migrate" |
| 181 | LOCAL_SSH_PORT="2222" |
| 182 | KEYS_COMMAND="/usr/bin/ayllu-keys --ayllu-shell=/usr/bin/ayllu-shell %u %h %t %k" |
| 183 | - IMAGE_NAME="registry.ayllu-forge.org/ayllu/ayllu:multiuser-main" |
| 184 | - IMAGE_NAME="registry-auth.ayllu-forge.org/ayllu/ayllu:multiuser-main" |
| 185 | + IMAGE_NAME="localhost/ayllu-debian-shell-test" |
| 186 | |
| 187 | TEST_CONFIG_FILE=config.ssh-test.toml |
| 188 | |
| 189 | cargo build --target x86_64-unknown-linux-musl --package ayllu-keys |
| 190 | cargo build --target x86_64-unknown-linux-musl --package ayllu-shell |
| 191 | + cargo build --target x86_64-unknown-linux-musl --package ayllu-migrate |
| 192 | |
| 193 | PUBLIC_KEY="$(find ~/.ssh -name '*.pub' -exec cat {} \; | head -n 1)" |
| 194 | |
| 195 | make_cfg() { |
| 196 | cat >$TEST_CONFIG_FILE <<EOF |
| 197 | - base_dir = "/usr/share/ayllu/repos" |
| 198 | + base_dir = "/var/lib/ayllu/repos" |
| 199 | log_level = "DEBUG" |
| 200 | [[collections]] |
| 201 | name = "demo" |
| 202 | @@ -40,21 +45,16 @@ EOF |
| 203 | init_env() { |
| 204 | cat<<EOF |
| 205 | passwd -d root |
| 206 | - adduser -h /home/demo -D demo |
| 207 | - passwd -d demo |
| 208 | passwd -d ayllu |
| 209 | - ssh-keygen -A |
| 210 | - mkdir -p /home/demo/.ssh |
| 211 | - echo $PUBLIC_KEY > /home/demo/.ssh/authorized_keys |
| 212 | - chmod 644 /home/demo/.ssh/authorized_keys |
| 213 | - mkdir -p /usr/share/ayllu/repos/demo |
| 214 | - chown -R ayllu:ayllu /usr/share/ayllu |
| 215 | - /usr/sbin/sshd $DEBUGGING -D -o PermitTTY=yes -o PasswordAuthentication=no -o AuthorizedKeysCommand="$KEYS_COMMAND" -o AuthorizedKeysCommandUser=root |
| 216 | + mkdir /var/lib/ayllu && chown ayllu:ayllu /var/lib/ayllu |
| 217 | + sudo -u ayllu ayllu-migrate |
| 218 | + sudo -u ayllu mkdir -p /var/lib/ayllu/repos/demo |
| 219 | + /usr/sbin/sshd $DEBUGGING -D -o PermitTTY=yes -o PermitUserEnvironment="AYLLU_USER" -o PasswordAuthentication=no -o AuthorizedKeysCommand="$KEYS_COMMAND" -o AuthorizedKeysCommandUser=root |
| 220 | EOF |
| 221 | } |
| 222 | |
| 223 | echo "To open a remote shell:" |
| 224 | - echo "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no demo@127.0.0.1 -p 2222" |
| 225 | + echo "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no ayllu@127.0.0.1 -p 2222" |
| 226 | echo "Or run scripts/ayllu_shell_ssh.sh" |
| 227 | |
| 228 | make_cfg |
| 229 | @@ -62,6 +62,7 @@ make_cfg |
| 230 | podman run \ |
| 231 | --name ayllu-shell-test \ |
| 232 | --rm --replace -ti --user root \ |
| 233 | + -v $AYLLU_MIGRATE_BINARY:/usr/bin/ayllu-migrate \ |
| 234 | -v $AYLLU_SHELL_BINARY:/usr/bin/ayllu-shell \ |
| 235 | -v $AYLLU_KEYS_BINARY:/usr/bin/ayllu-keys \ |
| 236 | -v $PWD/$TEST_CONFIG_FILE:/etc/ayllu/config.toml:ro \ |