Commit

Author:

Hash:

Timestamp:

+167 -15 +/-11 browse

Kevin Schoon [me@kevinschoon.com]

e03a47c61e4b6487dd60229b4ab261ba6a0fa6c8

Sat, 12 Jul 2025 08:57:06 +0000 (4 months ago)

re-init ayllu-keys and ayllu-shell
re-init ayllu-keys and ayllu-shell

This re-enables the ayllu-keys and ayllu-shell binaries which can be used
for allocating access to a linux system via ayllu configuration.
1diff --git a/Cargo.lock b/Cargo.lock
2index f4c67eb..3cc5bcb 100644
3--- a/Cargo.lock
4+++ b/Cargo.lock
5 @@ -367,6 +367,30 @@ dependencies = [
6 ]
7
8 [[package]]
9+ name = "ayllu-keys"
10+ version = "0.1.0"
11+ dependencies = [
12+ "ayllu_config",
13+ "ayllu_logging",
14+ "clap 4.5.40",
15+ "serde",
16+ "thiserror 2.0.12",
17+ "tracing",
18+ "tracing-subscriber",
19+ ]
20+
21+ [[package]]
22+ name = "ayllu-shell"
23+ version = "0.1.0"
24+ dependencies = [
25+ "ayllu_config",
26+ "clap 4.5.40",
27+ "dialoguer",
28+ "serde",
29+ "tracing",
30+ ]
31+
32+ [[package]]
33 name = "ayllu_api"
34 version = "0.2.1"
35 dependencies = [
36 @@ -652,6 +676,19 @@ dependencies = [
37 ]
38
39 [[package]]
40+ name = "console"
41+ version = "0.15.11"
42+ source = "registry+https://github.com/rust-lang/crates.io-index"
43+ checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8"
44+ dependencies = [
45+ "encode_unicode",
46+ "libc",
47+ "once_cell",
48+ "unicode-width 0.2.0",
49+ "windows-sys 0.59.0",
50+ ]
51+
52+ [[package]]
53 name = "convert_case"
54 version = "0.6.0"
55 source = "registry+https://github.com/rust-lang/crates.io-index"
56 @@ -879,6 +916,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
57 checksum = "abd57806937c9cc163efc8ea3910e00a62e2aeb0b8119f1793a978088f8f6b04"
58
59 [[package]]
60+ name = "dialoguer"
61+ version = "0.11.0"
62+ source = "registry+https://github.com/rust-lang/crates.io-index"
63+ checksum = "658bce805d770f407bc62102fca7c2c64ceef2fbcb2b8bd19d2765ce093980de"
64+ dependencies = [
65+ "console",
66+ "shell-words",
67+ "thiserror 1.0.69",
68+ ]
69+
70+ [[package]]
71 name = "digest"
72 version = "0.10.7"
73 source = "registry+https://github.com/rust-lang/crates.io-index"
74 @@ -992,6 +1040,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
75 checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
76
77 [[package]]
78+ name = "encode_unicode"
79+ version = "1.0.0"
80+ source = "registry+https://github.com/rust-lang/crates.io-index"
81+ checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
82+
83+ [[package]]
84 name = "encoding_rs"
85 version = "0.8.35"
86 source = "registry+https://github.com/rust-lang/crates.io-index"
87 @@ -1944,7 +1998,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
88 checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667"
89 dependencies = [
90 "cfg-if",
91- "windows-targets 0.48.5",
92+ "windows-targets 0.53.0",
93 ]
94
95 [[package]]
96 @@ -3140,6 +3194,12 @@ dependencies = [
97 ]
98
99 [[package]]
100+ name = "shell-words"
101+ version = "1.1.0"
102+ source = "registry+https://github.com/rust-lang/crates.io-index"
103+ checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde"
104+
105+ [[package]]
106 name = "shlex"
107 version = "1.3.0"
108 source = "registry+https://github.com/rust-lang/crates.io-index"
109 @@ -4129,7 +4189,7 @@ version = "0.1.9"
110 source = "registry+https://github.com/rust-lang/crates.io-index"
111 checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
112 dependencies = [
113- "windows-sys 0.48.0",
114+ "windows-sys 0.59.0",
115 ]
116
117 [[package]]
118 diff --git a/Cargo.toml b/Cargo.toml
119index 2c4f50a..1181875 100644
120--- a/Cargo.toml
121+++ b/Cargo.toml
122 @@ -11,8 +11,8 @@ members = [
123 "ayllu",
124 # "ayllu-build",
125 # "ayllu-mail",
126- # "ayllu-shell",
127- # "ayllu-keys",
128+ "ayllu-shell",
129+ "ayllu-keys",
130 # "ayllu-jobs",
131 # "ayllu-xmpp",
132 "quipu",
133 diff --git a/ayllu-keys/src/config.rs b/ayllu-keys/src/config.rs
134index f6c2373..a466687 100644
135--- a/ayllu-keys/src/config.rs
136+++ b/ayllu-keys/src/config.rs
137 @@ -16,6 +16,7 @@ pub struct Config {
138 pub log_level: String,
139 // path to the ayllu-shell executable
140 pub ayllu_shell: Option<PathBuf>,
141+ #[serde(default = "Vec::new")]
142 pub identities: Vec<Identity>,
143 }
144
145 diff --git a/ayllu-shell/Cargo.toml b/ayllu-shell/Cargo.toml
146index 53aedf4..3422202 100644
147--- a/ayllu-shell/Cargo.toml
148+++ b/ayllu-shell/Cargo.toml
149 @@ -8,5 +8,6 @@ edition = "2021"
150 ayllu_config = { path = "../crates/config" }
151
152 clap = { workspace = true }
153+ dialoguer = { version = "0.11.0", default-features = false }
154 serde = { workspace = true }
155 tracing = { workspace = true }
156 diff --git a/ayllu-shell/src/config.rs b/ayllu-shell/src/config.rs
157index 62caa1a..ee43ab6 100644
158--- a/ayllu-shell/src/config.rs
159+++ b/ayllu-shell/src/config.rs
160 @@ -11,10 +11,19 @@ pub struct Identity {
161 pub authorized_keys: Option<Vec<String>>,
162 }
163
164+ /// Various Shell configuration
165+ #[derive(Serialize, Deserialize, Clone, Default)]
166+ pub struct Shell {
167+ /// Message of the Day displayed for each interactive Ayllu session
168+ pub motd: String,
169+ }
170+
171 #[derive(Serialize, Deserialize, Clone, Default)]
172 pub struct Config {
173 pub log_level: String,
174 pub identities: Vec<Identity>,
175+ #[serde(default = "Shell::default")]
176+ pub shell: Shell,
177 }
178
179 impl Configurable for Config {}
180 diff --git a/ayllu-shell/src/main.rs b/ayllu-shell/src/main.rs
181index d8d3a2d..b2450c8 100644
182--- a/ayllu-shell/src/main.rs
183+++ b/ayllu-shell/src/main.rs
184 @@ -1,10 +1,11 @@
185 use std::os::unix::process::CommandExt;
186- use std::path::{Path, PathBuf};
187+ use std::path::PathBuf;
188 use std::process::Command;
189
190 use clap::Parser;
191
192 mod config;
193+ mod ui;
194
195 #[derive(Parser, Debug)]
196 #[clap(version, about, long_about = "Ayllu Shell Access")]
197 @@ -19,19 +20,22 @@ struct Arguments {
198 fn main() -> Result<(), Box<dyn std::error::Error>> {
199 let args = Arguments::parse();
200 let config: config::Config = ayllu_config::Reader::load(args.config.as_deref())?;
201- println!("Loaded Configuration: {}", config.identities.len());
202+ // This value must already exist and is validated in ayllu-keys, thus no
203+ // authentication is required here.
204 let username = std::env::var("USER").unwrap();
205 let identity = config
206 .identities
207 .iter()
208 .find(|identity| identity.username == username)
209 .unwrap();
210- let shell = identity
211- .shell
212- .clone()
213- .unwrap_or(Path::new("/bin/sh").to_path_buf());
214- println!("Hello World from Ayllu Shell!");
215- let mut cmd = Command::new(shell);
216- let e = cmd.exec();
217- panic!("Failed to exec: {:?}", e);
218+ let motd = config.shell.motd;
219+ print!("{motd}");
220+ match ui::read_option(identity.shell.as_ref().map(|path| path.as_path())) {
221+ ui::MenuOption::Shell(path_buf) => {
222+ let mut cmd = Command::new(path_buf.as_os_str());
223+ let e = cmd.exec();
224+ panic!("Failed to exec: {:?}", e);
225+ }
226+ ui::MenuOption::Exit => Ok(()),
227+ }
228 }
229 diff --git a/ayllu-shell/src/ui.rs b/ayllu-shell/src/ui.rs
230new file mode 100644
231index 0000000..0c41c47
232--- /dev/null
233+++ b/ayllu-shell/src/ui.rs
234 @@ -0,0 +1,41 @@
235+ use std::{
236+ fmt::Display,
237+ path::{Path, PathBuf},
238+ };
239+
240+ use dialoguer::{theme::ColorfulTheme, Select};
241+
242+ #[derive(Clone)]
243+ pub enum MenuOption {
244+ Shell(PathBuf),
245+ Exit,
246+ }
247+
248+ impl Display for MenuOption {
249+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
250+ match self {
251+ MenuOption::Shell(path) => write!(f, "Enter Your Shell: {}", path.to_string_lossy()),
252+ MenuOption::Exit => write!(f, "Disconnect"),
253+ }
254+ }
255+ }
256+
257+ pub fn read_option(shell: Option<&Path>) -> MenuOption {
258+ let mut options: Vec<MenuOption> = Vec::new();
259+ if let Some(shell) = shell {
260+ options.push(MenuOption::Shell(shell.to_path_buf()));
261+ }
262+ options.push(MenuOption::Exit);
263+ println!();
264+ let choice = Select::with_theme(&ColorfulTheme::default())
265+ .with_prompt("What would you like to do?")
266+ .default(0)
267+ .items(options.as_slice())
268+ .interact_opt()
269+ .unwrap();
270+ if let Some(choice) = choice {
271+ options.get(choice).unwrap().clone()
272+ } else {
273+ read_option(shell)
274+ }
275+ }
276 diff --git a/config.example.toml b/config.example.toml
277index f6e40ec..6bb87d3 100644
278--- a/config.example.toml
279+++ b/config.example.toml
280 @@ -224,3 +224,27 @@ extensions = [".rs"]
281 # name = "attic"
282 # description = "archived code"
283 # path = "/path/to/attic"
284+
285+ [shell]
286+ motd = """
287+ ████ ████
288+ ░░███ ░░███
289+ ██████ █████ ████ ░███ ░███ █████ ████
290+ ░░░░░███ ░░███ ░███ ░███ ░███ ░░███ ░███
291+ ███████ ░███ ░███ ░███ ░███ ░███ ░███
292+ ███░░███ ░███ ░███ ░███ ░███ ░███ ░███
293+ ░░████████ ░░███████ █████ █████ ░░████████
294+ ░░░░░░░░ ░░░░░███ ░░░░░ ░░░░░ ░░░░░░░░
295+ ███ ░███
296+ ░░██████
297+ ░░░░░░
298+
299+ A Hyper Performant & Hackable Code Forge Built on Open Standards.
300+ """
301+
302+ [[identities]]
303+ username = "demo"
304+ shell = "/bin/sh"
305+ authorized_keys = [
306+ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIO9MKZZAGWfX6CqM02Q3IYns7SecAGEdi9e2aw7WPAdu kevin@hyte"
307+ ]
308 diff --git a/docs/timesharing.md b/docs/timesharing.md
309new file mode 100644
310index 0000000..56aebca
311--- /dev/null
312+++ b/docs/timesharing.md
313 @@ -0,0 +1,6 @@
314+ # Timesharing
315+
316+ Ayllu is designed to be deployed to a single server and shared by multiple
317+ users in the spirit of Unix
318+ [time-sharing](https://en.wikipedia.org/wiki/Time-sharing) systems of the
319+ 1960s and 1970s.
320 diff --git a/scripts/ayllu_shell_ssh.sh b/scripts/ayllu_shell_ssh.sh
321new file mode 100755
322index 0000000..fd7ed1c
323--- /dev/null
324+++ b/scripts/ayllu_shell_ssh.sh
325 @@ -0,0 +1,4 @@
326+ #!/bin/sh
327+ set -e
328+
329+ ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no demo@127.0.0.1 -p 2222
330 diff --git a/scripts/ayllu_shell_test.sh b/scripts/ayllu_shell_test.sh
331index 08a2ecc..de5f9eb 100755
332--- a/scripts/ayllu_shell_test.sh
333+++ b/scripts/ayllu_shell_test.sh
334 @@ -7,6 +7,7 @@ AYLLU_SRC="$PWD"
335 LOCAL_SSH_PORT="2222"
336 KEYS_COMMAND="/src/target/x86_64-unknown-linux-musl/debug/ayllu-keys --ayllu-shell=/src/target/x86_64-unknown-linux-musl/debug/ayllu-shell --log-path=/tmp/ayllu.log %%u %%h %%t %%k"
337
338+ cargo build --target x86_64-unknown-linux-musl --package ayllu-keys
339 cargo build --target x86_64-unknown-linux-musl --package ayllu-shell
340
341 init_env() {
342 @@ -14,12 +15,13 @@ init_env() {
343 printf "adduser -h /home/demo -D demo\n"
344 printf "passwd -d demo\n"
345 printf "ssh-keygen -A\n"
346- printf "/usr/sbin/sshd -d -D -o PermitUserEnvironment=AYLLU_USERNAME -o PasswordAuthentication=no -o AuthorizedKeysCommand=\"$KEYS_COMMAND\" -o AuthorizedKeysCommandUser=ayllu\n"
347+ printf "/usr/sbin/sshd -d -D -o PermitTTY=yes -o PermitUserEnvironment=AYLLU_USERNAME -o PasswordAuthentication=no -o AuthorizedKeysCommand=\"$KEYS_COMMAND\" -o AuthorizedKeysCommandUser=ayllu\n"
348 printf "cat /tmp/ayllu.log\n"
349 }
350
351 echo "To open a remote shell:"
352 echo "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no demo@127.0.0.1 -p 2222"
353+ echo "Or run scripts/ayllu_shell_ssh.sh"
354
355 podman run \
356 --name ayllu-shell-test \