From 470111460786997a1d972d9e34240776ce73ca95 Mon Sep 17 00:00:00 2001
From: Joel Savitz <joel@underground.software>
Date: Tue, 21 Jan 2025 00:26:37 -0500
Subject: [PATCH 01/11] orbit: serve a containerfile

serve a Fedora 41 based Containefile from /Containerfile
to enable easy student environment setup, including mostly
completed .muttrc and .gitconfig files.

Signed-off-by: Joel Savitz <joel@underground.software>
---
 container-compose.yml                         |  1 +
 .../server_https/00-orbit-paths.conf          |  2 +-
 orbit/radius.py                               | 68 +++++++++++++++++--
 3 files changed, 64 insertions(+), 7 deletions(-)

diff --git a/container-compose.yml b/container-compose.yml
index 1aa71a5b..69e49db8 100644
--- a/container-compose.yml
+++ b/container-compose.yml
@@ -47,6 +47,7 @@ services:
         orbit_version_info: "singularity ${SINGULARITY_VERSION} ${SINGULARITY_DEPLOYMENT_STATUS} https://github.com/underground-software/singularity"
     environment:
       TZ: ${SINGULARITY_TIMEZONE}
+      HOSTNAME: ${SINGULARITY_HOSTNAME}
     volumes:
       - type: volume
         source: orbit-db
diff --git a/nginx_snippets/server_https/00-orbit-paths.conf b/nginx_snippets/server_https/00-orbit-paths.conf
index 104af9fa..bb80eaf0 100644
--- a/nginx_snippets/server_https/00-orbit-paths.conf
+++ b/nginx_snippets/server_https/00-orbit-paths.conf
@@ -5,7 +5,7 @@ location @login {
 	return 303 /login?target=$uri;
 }
 
-location ~* ^((.*\.md)|/log(in|out)|/activity|/dashboard|/register|/cgit.*)$ {
+location ~* ^((.*\.md)|/log(in|out)|/activity|/dashboard|/register|/Containerfile|/cgit.*)$ {
 	include uwsgi_params;
 	proxy_intercept_errors on;
 	proxy_pass http://orbit:9098;
diff --git a/orbit/radius.py b/orbit/radius.py
index 2c480949..a16cff2a 100644
--- a/orbit/radius.py
+++ b/orbit/radius.py
@@ -636,6 +636,17 @@ def form_respond():
     <h3>Password: {password}</h3><br>''')
 
 
+def extract_basic_auth(rocket):
+    if (auth_str := rocket.env.get('HTTP_AUTHORIZATION')) is None:
+        return
+    if not auth_str.startswith('Basic '):
+        return
+    cred_str = base64.b64decode(auth_str.removeprefix('Basic '))
+    username, password = cred_str.decode().split(':', maxsplit=1)
+
+    return username, password
+
+
 def determine_cache_entry(cred_str):
     import hashlib
     import time
@@ -647,15 +658,12 @@ def determine_cache_entry(cred_str):
 
 def http_basic_auth(rocket):
     import authcache
-    if (auth_str := rocket.env.get('HTTP_AUTHORIZATION')) is None:
-        return
-    if not auth_str.startswith('Basic '):
+    if not (creds := extract_basic_auth(rocket)):
         return
-    cred_str = base64.b64decode(auth_str.removeprefix('Basic '))
-    cache_entry = determine_cache_entry(cred_str)
+    username, password = creds
+    cache_entry = determine_cache_entry(':'.join(creds))
     if authcache.entry_exists(cache_entry):
         return True
-    username, password = cred_str.decode().split(':', maxsplit=1)
     if not check_credentials(username, password):
         return
     authcache.add_entry(cache_entry)
@@ -713,6 +721,52 @@ def cgit_internal_server_error(msg):
         return cgit_internal_server_error(type(ex))
 
 
+def handle_containerfile(rocket):
+    hostname = os.getenv("HOSTNAME")
+    if creds := extract_basic_auth(rocket):
+        username, password = creds
+    if not creds or not check_credentials(username, password):
+        rocket.headers.append(('WWW-Authenticate', 'Basic realm="podman"'))
+        return rocket.raw_respond(HTTPStatus.UNAUTHORIZED)
+    return rocket.raw_respond(HTTPStatus.OK, f'''
+FROM fedora:41
+
+RUN dnf -y update && dnf install -y strace && dnf clean all
+RUN dnf -y install --setopt=install_weak_deps=False git tar make gcc qemu-system-riscv binutils-riscv64-linux-gnu gcc-riscv64-linux-gnu bc flex bison openssl-devel elfutils-libelf-devel ncurses-devel dwarves git-email vim mutt cpio
+
+RUN cat <<'MUTTRC' > ~/.muttrc
+set realname="Your Name Here"
+set my_username="{username}"
+set my_password="{password}"
+set course_domain="{hostname}"
+set spoolfile=
+set record=
+set folder=
+set sort=threads
+set from="$my_username@$course_domain"
+set header_cache=~/.cache/mutt
+set smtp_url="smtps://$my_username:$my_password@$course_domain:465"
+push "&lt;change-folder&gt;pops://$my_username:$my_password@$course_domain:995"\n
+macro index l "|git am -s"\n
+MUTTRC
+
+RUN cat <<'GITCONFIG' > ~/.gitconfig
+[core]
+editor = vim # Or which ever editor you prefer
+
+[user]
+name = Your Name Here
+email = {username}@{hostname}
+[sendemail]
+smtpUser = {username}
+smtpPass = {password}
+smtpserver = {hostname}
+smtpserverport = 465
+smtpencryption = ssl
+GITCONFIG
+    '''.encode())
+
+
 def handle_error(rocket):
     error_num_str = rocket.queries_query('num')
     try:
@@ -762,6 +816,8 @@ def application(env, SR):
             return handle_logout(rocket)
         case '/mail_auth':
             return handle_mail_auth(rocket)
+        case '/Containerfile':
+            return handle_containerfile(rocket)
         case '/activity':
             return handle_activity(rocket)
         case '/error':

From 02dd96d3bacc8224c7cdad6a315e0f8d9ad1cfb4 Mon Sep 17 00:00:00 2001
From: Joel Savitz <joel@underground.software>
Date: Tue, 21 Jan 2025 14:05:34 -0500
Subject: [PATCH 02/11] drop privileges in containerfile

Signed-off-by: Joel Savitz <joel@underground.software>
---
 orbit/radius.py | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/orbit/radius.py b/orbit/radius.py
index a16cff2a..7ca4fafc 100644
--- a/orbit/radius.py
+++ b/orbit/radius.py
@@ -764,6 +764,12 @@ def handle_containerfile(rocket):
 smtpserverport = 465
 smtpencryption = ssl
 GITCONFIG
+
+RUN useradd {username} -U
+VOLUME /home/{username}/
+USER {username}:{username}
+WORKDIR /home/{username}/
+ENTRYPOINT ["/usr/bin/bash", "-l", "-i"]
     '''.encode())
 
 

From 6e70d9f60bf2c85284c3b1a13be6942c4d835c13 Mon Sep 17 00:00:00 2001
From: Joel Savitz <joel@underground.software>
Date: Tue, 21 Jan 2025 14:23:53 -0500
Subject: [PATCH 03/11] orbit: tweak Containerfile

Signed-off-by: Joel Savitz <joel@underground.software>
---
 orbit/radius.py | 31 +++++++++++++++++++++++++------
 1 file changed, 25 insertions(+), 6 deletions(-)

diff --git a/orbit/radius.py b/orbit/radius.py
index 7ca4fafc..0f98d4c6 100644
--- a/orbit/radius.py
+++ b/orbit/radius.py
@@ -731,8 +731,30 @@ def handle_containerfile(rocket):
     return rocket.raw_respond(HTTPStatus.OK, f'''
 FROM fedora:41
 
-RUN dnf -y update && dnf install -y strace && dnf clean all
-RUN dnf -y install --setopt=install_weak_deps=False git tar make gcc qemu-system-riscv binutils-riscv64-linux-gnu gcc-riscv64-linux-gnu bc flex bison openssl-devel elfutils-libelf-devel ncurses-devel dwarves git-email vim mutt cpio
+RUN <<DNF
+dnf -y update
+dnf install -y --setopt=install_weak_deps=False \
+git \
+tar \
+make \
+gcc \
+qemu-system-riscv \
+binutils-riscv64-linux-gnu \
+gcc-riscv64-linux-gnu \
+bc \
+flex \
+bison \
+openssl-devel \
+elfutils-libelf-devel \
+ncurses-devel \
+dwarves \
+git-email \
+vim \
+mutt \
+cpio \
+strace
+dnf clean all
+DNF
 
 RUN cat <<'MUTTRC' > ~/.muttrc
 set realname="Your Name Here"
@@ -746,14 +768,11 @@ def handle_containerfile(rocket):
 set from="$my_username@$course_domain"
 set header_cache=~/.cache/mutt
 set smtp_url="smtps://$my_username:$my_password@$course_domain:465"
-push "&lt;change-folder&gt;pops://$my_username:$my_password@$course_domain:995"\n
+push "<change-folder>pops://$my_username:$my_password@$course_domain:995"\n
 macro index l "|git am -s"\n
 MUTTRC
 
 RUN cat <<'GITCONFIG' > ~/.gitconfig
-[core]
-editor = vim # Or which ever editor you prefer
-
 [user]
 name = Your Name Here
 email = {username}@{hostname}

From 623aea0a1efdbd0482457736c3edac5c9931a1ca Mon Sep 17 00:00:00 2001
From: Joel Savitz <joel@underground.software>
Date: Tue, 21 Jan 2025 14:32:17 -0500
Subject: [PATCH 04/11] drop privileges before creating config files

Signed-off-by: Joel Savitz <joel@underground.software>
---
 orbit/radius.py | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/orbit/radius.py b/orbit/radius.py
index 0f98d4c6..e88eeaf9 100644
--- a/orbit/radius.py
+++ b/orbit/radius.py
@@ -756,6 +756,11 @@ def handle_containerfile(rocket):
 dnf clean all
 DNF
 
+RUN useradd {username} -U
+VOLUME /home/{username}/
+USER {username}:{username}
+WORKDIR /home/{username}/
+
 RUN cat <<'MUTTRC' > ~/.muttrc
 set realname="Your Name Here"
 set my_username="{username}"
@@ -784,10 +789,6 @@ def handle_containerfile(rocket):
 smtpencryption = ssl
 GITCONFIG
 
-RUN useradd {username} -U
-VOLUME /home/{username}/
-USER {username}:{username}
-WORKDIR /home/{username}/
 ENTRYPOINT ["/usr/bin/bash", "-l", "-i"]
     '''.encode())
 

From e4a426f0e601ffc3938b6ef7fe22b20abf3939f9 Mon Sep 17 00:00:00 2001
From: Joel Savitz <joel@underground.software>
Date: Tue, 21 Jan 2025 14:37:11 -0500
Subject: [PATCH 05/11] move volume directive below config setup

Signed-off-by: Joel Savitz <joel@underground.software>
---
 orbit/radius.py | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/orbit/radius.py b/orbit/radius.py
index e88eeaf9..c5e6958b 100644
--- a/orbit/radius.py
+++ b/orbit/radius.py
@@ -757,7 +757,6 @@ def handle_containerfile(rocket):
 DNF
 
 RUN useradd {username} -U
-VOLUME /home/{username}/
 USER {username}:{username}
 WORKDIR /home/{username}/
 
@@ -789,6 +788,8 @@ def handle_containerfile(rocket):
 smtpencryption = ssl
 GITCONFIG
 
+VOLUME /home/{username}/
+
 ENTRYPOINT ["/usr/bin/bash", "-l", "-i"]
     '''.encode())
 

From e7376a079e356539fcded353331823261f308a75 Mon Sep 17 00:00:00 2001
From: Joel Savitz <joel@underground.software>
Date: Tue, 21 Jan 2025 14:43:13 -0500
Subject: [PATCH 06/11] orbit: add nano to containerfile

Signed-off-by: Joel Savitz <joel@underground.software>
---
 orbit/radius.py | 1 +
 1 file changed, 1 insertion(+)

diff --git a/orbit/radius.py b/orbit/radius.py
index c5e6958b..f2d19581 100644
--- a/orbit/radius.py
+++ b/orbit/radius.py
@@ -750,6 +750,7 @@ def handle_containerfile(rocket):
 dwarves \
 git-email \
 vim \
+nano \
 mutt \
 cpio \
 strace

From e53f6fdfffb0a1d493f3de70a290196d3621c5f3 Mon Sep 17 00:00:00 2001
From: Joel Savitz <joel@underground.software>
Date: Tue, 21 Jan 2025 14:43:59 -0500
Subject: [PATCH 07/11] orbit: strip extra whitespace from containerfile string

Signed-off-by: Joel Savitz <joel@underground.software>
---
 orbit/radius.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/orbit/radius.py b/orbit/radius.py
index f2d19581..2495c29c 100644
--- a/orbit/radius.py
+++ b/orbit/radius.py
@@ -792,7 +792,7 @@ def handle_containerfile(rocket):
 VOLUME /home/{username}/
 
 ENTRYPOINT ["/usr/bin/bash", "-l", "-i"]
-    '''.encode())
+    '''.strip().encode())
 
 
 def handle_error(rocket):

From 5bee927a2417f8993ce4069466b74e029d618ba1 Mon Sep 17 00:00:00 2001
From: Joel Savitz <joel@underground.software>
Date: Tue, 21 Jan 2025 14:56:06 -0500
Subject: [PATCH 08/11] orbit: set nano as default editor in containerfile

Signed-off-by: Joel Savitz <joel@underground.software>
---
 orbit/radius.py | 1 +
 1 file changed, 1 insertion(+)

diff --git a/orbit/radius.py b/orbit/radius.py
index 2495c29c..5d83c05b 100644
--- a/orbit/radius.py
+++ b/orbit/radius.py
@@ -751,6 +751,7 @@ def handle_containerfile(rocket):
 git-email \
 vim \
 nano \
+nano-default-editor \
 mutt \
 cpio \
 strace

From 9648602efe0bc717e82573e11691eca344db7f69 Mon Sep 17 00:00:00 2001
From: Joel Savitz <joel@underground.software>
Date: Tue, 21 Jan 2025 15:00:29 -0500
Subject: [PATCH 09/11] allow changing default editor to vim with query arg
 ?vim=<anything>

Signed-off-by: Joel Savitz <joel@underground.software>
---
 orbit/radius.py | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/orbit/radius.py b/orbit/radius.py
index 5d83c05b..a92d20df 100644
--- a/orbit/radius.py
+++ b/orbit/radius.py
@@ -722,6 +722,9 @@ def cgit_internal_server_error(msg):
 
 
 def handle_containerfile(rocket):
+    nano_default_editor = 'nano-default-editor' \
+            if not rocket.queries_query('vim') else ''
+
     hostname = os.getenv("HOSTNAME")
     if creds := extract_basic_auth(rocket):
         username, password = creds
@@ -751,7 +754,7 @@ def handle_containerfile(rocket):
 git-email \
 vim \
 nano \
-nano-default-editor \
+{nano_default_editor} \
 mutt \
 cpio \
 strace

From 483b892ba0d235c19b9e50515aada2ec6002fffb Mon Sep 17 00:00:00 2001
From: Joel Savitz <joel@underground.software>
Date: Wed, 22 Jan 2025 16:52:45 -0500
Subject: [PATCH 10/11] orbit: whole home tree is volume to remove dependency
 on username knowledge

Signed-off-by: Joel Savitz <joel@underground.software>
---
 orbit/radius.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/orbit/radius.py b/orbit/radius.py
index a92d20df..8560ab79 100644
--- a/orbit/radius.py
+++ b/orbit/radius.py
@@ -793,7 +793,7 @@ def handle_containerfile(rocket):
 smtpencryption = ssl
 GITCONFIG
 
-VOLUME /home/{username}/
+VOLUME /home
 
 ENTRYPOINT ["/usr/bin/bash", "-l", "-i"]
     '''.strip().encode())

From 11094a16175970344cb7c4a891f295d510da74aa Mon Sep 17 00:00:00 2001
From: Joel Savitz <joel@underground.software>
Date: Wed, 22 Jan 2025 17:18:05 -0500
Subject: [PATCH 11/11] orbit: add wget to container

Signed-off-by: Joel Savitz <joel@underground.software>
---
 orbit/radius.py | 1 +
 1 file changed, 1 insertion(+)

diff --git a/orbit/radius.py b/orbit/radius.py
index 8560ab79..bd3d89fd 100644
--- a/orbit/radius.py
+++ b/orbit/radius.py
@@ -757,6 +757,7 @@ def handle_containerfile(rocket):
 {nano_default_editor} \
 mutt \
 cpio \
+wget \
 strace
 dnf clean all
 DNF
