diff --git a/Cargo.toml b/Cargo.toml index dbd030f..f0b82b6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,3 +53,13 @@ rsass = "0.28.0" [package.metadata.deb] section = "web" +maintainer-scripts = "server_side/" +systemd-units = { enable = false } +assets = [ + [ "target/release/rgit", "/usr/bin/rgit", "755" ], + [ "server_side/default", "/etc/default/rgit", "644" ], + [ "server_side/config", "/etc/rgit/config", "644" ], + [ "server_side/rgitsss", "/usr/bin/rgitsss", "755" ], + [ "server_side/rgit_pre_flight_check", "/usr/bin/rgit_pre_flight_check", "755" ], + # FYI `cargo deb` installs the systemd service file +] diff --git a/server_side/README b/server_side/README new file mode 100644 index 0000000..404877d --- /dev/null +++ b/server_side/README @@ -0,0 +1,110 @@ + +For autostarting `rgit` is a systemd service unit available. + +Rgit only opens only self owned files. Systemd helps with running the +rgit proces as a dedicated user. + +Script rgit_pre_flight_check verifies if the user defined +in the systemd unit, is available on the system. + +For dealing with self owned files, there is `rgitsss`. The triple s +stands for Server Side Script. + +Default user for `rgitsss` is 'git'. For other username, set RGIT_ACCOUNT +environment variable. ( `export RGIT_ACCOUNT=foo` ) + + +Now much more about `rgitsss`. + +Try + + rgitsss + rgitsss help + +for getting an idea what is to come. + +Commands, subcommands, like `adduser` and `mkdir` are root privileges needed. +For the other subcommands also, unless you are willing to set and type in +the password for the rgit user. That is why rgitsss is prefixed with sudo. + +Type along while reading + + sudo rgitsss adduser + + sudo rgitsss mkdir /srv/rgit + sudo rgitsss mkdir /srv/rgit/t + sudo rgitsss mkdir /srv/rgit/cache + + sudo rgitsss clone https://gitlab.com/stappersg/bong /srv/rgit/t/bong.git + + sudo rgitss publish /srv/rgit/t/bong.git + +Now it makes sense to start rgit + + sudo systemctl start rgit + +and to visit the URL where you have rgit running, with a webbrowser. +Notice the odd description. Reproduce it with + + rgitsss describe /srv/rgit/t/bong.git + +Change it by + + echo "a ping wrapper, reduces ping output while a server reboots" > nd + sudo rgitsss describe /srv/rgit/t/bong.git nd + +Now wait for the next rgit cache update, or force that by + + sudo systemctl restart rgit + +Webbrowser shows updated description. + +For start upon reboot: + + sudo systemctl enable rgit + +By design doesn't allow `rgit` writing. For "git writes" is SSH used. +And for SSH access are ssh pub keys needed. Ask the people who you want +to grant access to do + + cat ~/.ssh/id_ed22519.pub # or simular + +Collect the public keys in a text file, say "these_keys", then: + + sudo rgitsss sshkeys these_keys + +See which SSH-keys are present by: + + sudo rgitsss sshkeys + + +And with a new git repository? + + sudo rgitsss init /srv/rgit/t/baz.git + sudo rgitsss publish /srv/rgit/t/baz.git + echo "Proof of concept" > textfile + sudo rgitsss describe /srv/rgit/t/baz.git textfile + + +Then at **other** place, either: + + git clone git@rgit.example.com:/srv/rgit/t/baz.git + # with notice on empty git repository + cd baz + git branch -m main # sets 'main' as branch name + $EDITOR content + git add content + git commit + git push + +or: + + cd directory/with/git/repository + git remote add origin git@rgit.example.com:/srv/rgit/t/baz.git + git fetch origin + git push origin + +And splitting 'git@rgit.example.com:/srv/rgit/t/baz.git' from above +into 'git', 'rgit.example.com' and '/srv/rgit/t/baz.git'. The 'git' +is RGIT_ACCOUNT, 'rgit.example.com' server running `rgit`. +The '/srv/rgit/t/baz.git' is the path of the rgitsss init command. diff --git a/server_side/config b/server_side/config new file mode 100644 index 0000000..a9b9c13 --- /dev/null +++ b/server_side/config @@ -0,0 +1,17 @@ +# +# rgit configuration file +# currently only a wish +# + +# For further discussion: +# +# - format options: +# - yaml +# - toml +# - ...... +# +# - options for location: +# - fixed, like: /etc/rgit/config +# - command line parameter +# - environment variable, e.g. RGIT_CONFIG +# diff --git a/server_side/default b/server_side/default new file mode 100644 index 0000000..866aca7 --- /dev/null +++ b/server_side/default @@ -0,0 +1,22 @@ +# +# File with `rgit` parameter values. +# It will be read by the systemd service file as environment file. +# +# Bind address and port +RGIT_BIND='[::]:49418' +RGIT_BIND='127.0.0.1:49418' +# +# scan path top directory +RGIT_PATH=/srv/rgit/t +# +RGIT_CACHE=/srv/rgit/cache +# +# mandatory +RGIT_SHIM=--db-store +# +# +# For what it is worth: +# This file with environment variables exists because at the time of writing, +# no real configuration file existed. +# +# Last Line diff --git a/server_side/rgit_pre_flight_check b/server_side/rgit_pre_flight_check new file mode 100755 index 0000000..d9bf2b5 --- /dev/null +++ b/server_side/rgit_pre_flight_check @@ -0,0 +1,42 @@ +#!/bin/sh +# rgit pre flight check +# (early release) +# does various (silent) tests +# exits with error code EX_CONFIG on first failed test +# Intented use is being executed by systemd service unit + +EX_CONFIG=78 +# comes from https://man.freebsd.org/cgi/man.cgi?query=sysexits + +SERVICE_SIZE=$( systemctl cat rgit.service 2>/dev/null | wc -l ) +if [ ${SERVICE_SIZE} -lt 4 ] ; then + echo "E: Line count of systemd rgit service is unlikely small." + echo "I: Is the rgit systemd service unit installed?" + echo "I:" + echo "I: Do know that you can start rgit without Systemd." + exit ${EX_CONFIG} +fi + +# Does user exist? +USER=$( systemctl cat rgit.service 2>/dev/null \ + | awk -F= '$1 ~ /User/ { U = $2 } END { print U}' ) +if [ -z ${USER} ] ; then + echo "E: No username found in the systemd service unit." + exit ${EX_CONFIG} +fi +id ${USER} > /dev/null 2>&1 +if [ ${?} -gt 0 ] ; then + echo "E: \`id ${USER}\` failed." + echo "I: /usr/bin/rgitsss has an add user subcommand" + exit ${EX_CONFIG} +fi + +DIR=idea # read from configuration file +DIR=/srv/rgit/t +if [ ! -d ${DIR} ] ; then + echo "E: Directory ${DIR} nout found" + exit ${EX_CONFIG} +fi + +# all good +exit 0 diff --git a/server_side/rgitsss b/server_side/rgitsss new file mode 100755 index 0000000..3bda050 --- /dev/null +++ b/server_side/rgitsss @@ -0,0 +1,197 @@ +#!/usr/bin/bash +# +# rgitsss, rgit server side script +# +# Aid for +# - creating a system user +# - git clone as "system user" +# - git init as "system user" +# and more, use 'help' as subcommand to get them showed +# + +if [ ${#} -lt 1 ] ; then + echo "E: missing subcommand" + echo "I: 'help' is a valid subcommand" + exit 1 +fi + +SUBCMD=${1} +shift # every thing after subcommand are parameters +PARAMS=${@} + +if [ "empty" = "empty${RGIT_ACCOUNT}" ] ; then + # Default + ACCOUNT=git +else + # Pick up environment variable + ACCOUNT=${RGIT_ACCOUNT} +fi + +rgitsss_help() { + cat << HELPTEXT + +rgitsss + +subcommando + what it is supposed to do +adduser + adds user account \$RGIT_ACCOUNT and + creates file ~\$RGIT_ACCOUNT/.ssh/authorized_keys +mkdir /full/path + Creates directory \`/full/path\` + and changes owner to \$RGIT_ACCOUNT +init /full/path/project.git + Does a \`git init --bare /full/path/project.git\` + as user \$RGIT_ACCOUNT +clone URL /full/path/project.git + Does a \`git clone --bare URL /full/path/project.git\` + as user \$RGIT_ACCOUNT +publish /full/path/project.git + Place "This git repo may be published marker" +unpublish /full/path/project.git + Remove "This git repo may be published marker" +describe /full/path/project.git + Print description of git repo +describe /full/path/project.git textfile + Overwrite description of git repo with textfile +sshkeys + Prints ~RGIT_ACCOUNT.ssh/authorized_keys +sshkeys textfile + Overwrite ~RGIT_ACCOUNT.ssh/authorized_keys with textfile +help + Prints this help text +? + Same as help + +\$RGIT_ACCOUNT defaults to git + +HELPTEXT +} + +rgitsss_adduser() { + adduser --system \ + --shell /usr/bin/git-shell \ + --gecos "Git Repository" \ + --home /home/${ACCOUNT} \ + ${ACCOUNT} \ + && \ + su ${ACCOUNT} --shell=/usr/bin/bash \ + --login \ + --command="umask 0077 && mkdir .ssh" \ + && \ + su ${ACCOUNT} --shell=/usr/bin/bash \ + --login \ + --command="umask 0077 && touch .ssh/authorized" +} + +rgitsss_clone() { + if [ -n "${2}" ] ; then + URL=${1} + DIR=$( dirname ${2} ) + BASE=$( basename ${2} ) + else + echo "E: not enough parameters" + echo "I: require params: URL /full/path/new_git_repo" + exit 1 + fi + su ${ACCOUNT} --shell=/usr/bin/bash \ + --command="cd ${DIR} && git clone --bare ${URL} ${BASE}" +} + +rgitsss_init() { + if [ -n "${1}" ] ; then + DIR=$( dirname ${1} ) + BASE=$( basename ${1} ) + else + echo "E: not enough parameters" + echo "I: Provide /full/path/new_git_repo" + exit 1 + fi + COMMAND="cd ${DIR} && " # for avoiding a very long physical line + COMMAND="${COMMAND}git init --bare --initial-branch=main ${BASE}" + su ${ACCOUNT} --shell=/usr/bin/bash \ + --command="${COMMAND}" +} + +rgitsss_describe() { + if [ -n "${2}" ] ; then + cat ${2} | \ + su ${ACCOUNT} --shell=/usr/bin/bash \ + --command="tee ${1}/description > /dev/null" + else + # just print it + cat ${1}/description || echo "E: Invalid git repo path" + fi +} + +rgitsss_sshkeys() { + if [ -n "${1}" ] ; then + cat ${1} | \ + su ${ACCOUNT} --shell=/usr/bin/bash \ + --login \ + --command="umask 0077 && tee .ssh/authorized_keys > /dev/null" + else + # Print currently authorized SSH keys + su ${ACCOUNT} --shell=/usr/bin/bash \ + --login \ + --command="cat .ssh/authorized_keys" + fi +} + +# FYI: Next subcommand is by design not documented in the help text. +rgitsss_shell () { + if [ -n "${1}" ] ; then + DIR=${1} + else + echo "E: not enough parameters" + echo "I: Provide /full/path/top" + exit 1 + fi + echo "I: Type \`exit\` for exiting the shell" + echo "I: The two warnings from bash are known (and consider acceptable)" + su ${ACCOUNT} --shell=/usr/bin/bash \ + --command="cd ${DIR} && bash -o vi -i" +} + +# Select +case ${SUBCMD} in + ?|help) + rgitsss_help + ;; + adduser) + rgitsss_adduser + ;; + mkdir) + mkdir --parents ${PARAMS} && chown ${ACCOUNT} ${PARAMS} + ;; + clone) + rgitsss_clone ${PARAMS} + ;; + init) + rgitsss_init ${PARAMS} + ;; + publish) + su ${ACCOUNT} --shell=/usr/bin/bash \ + --command="touch ${PARAMS}/git-daemon-export-ok" + ;; + unpublish) + su ${ACCOUNT} --shell=/usr/bin/bash \ + --command="rm ${PARAMS}/git-daemon-export-ok" + ;; + describe) + rgitsss_describe ${PARAMS} + ;; + sshkeys) + rgitsss_sshkeys ${PARAMS} + ;; + shell) + rgitsss_shell ${PARAMS} + ;; + *) + echo "E: '${SUBCMD}' is not a valid subcommand" + echo "I: 'help' is a valid subcommand" + exit 1 + ;; +esac + +# l l diff --git a/server_side/service b/server_side/service new file mode 100644 index 0000000..f974ee0 --- /dev/null +++ b/server_side/service @@ -0,0 +1,12 @@ +[Unit] +Description=gitweb/cgit interface + +[Service] +User=git +EnvironmentFile=/etc/default/rgit +ExecStartPre=/usr/bin/rgit_pre_flight_check +ExecStart=/usr/bin/rgit ${RGIT_SHIM} ${RGIT_CACHE} ${RGIT_BIND} ${RGIT_PATH} +#Wish: just 'ExecStart=/usr/bin/rgit' + +[Install] +WantedBy=multi-user.target