Gitea Rust Rework

This commit is contained in:
Magic_RB 2021-01-26 13:10:49 +01:00
parent af83dbf7c4
commit 7b3ab825e3
13 changed files with 377 additions and 117 deletions

80
docker/gitea/app.ini Normal file
View file

@ -0,0 +1,80 @@
APP_NAME = Red Alder Gitea
RUN_MODE = prod
RUN_USER = gitea
[repository]
ROOT = /data/gitea/git/repositories
[repository.local]
LOCAL_COPY_PATH = /data/gitea/tmp/local-repo
[repository.upload]
TEMP_PATH = /data/gitea/uploads
[server]
APP_DATA_PATH = /data/gitea
SSH_DOMAIN = localhost
HTTP_PORT = 3000
ROOT_URL = https://gitea.redalder.org/
DISABLE_SSH = false
SSH_PORT = 22
SSH_LISTEN_PORT = 22
LFS_START_SERVER = true
LFS_CONTENT_PATH = /data/gitea/git/lfs
DOMAIN = gitea.redalder.org
OFFLINE_MODE = false
[database]
PATH = /data/gitea/gitea.db
DB_TYPE = postgres
HOST = localhost
POST = 5764
NAME = gitea
USER = gitea
PASSWD = gitea
SCHEMA =
SSL_MODE = disable
CHARSET = utf8
[indexer]
ISSUE_INDEXER_PATH = /data/gitea/indexers/issues.bleve
[session]
PROVIDER_CONFIG = /data/gitea/sessions
PROVIDER = file
[picture]
AVATAR_UPLOAD_PATH = /data/gitea/avatars
REPOSITORY_AVATAR_UPLOAD_PATH = /data/gitea/repo-avatars
DISABLE_GRAVATAR = false
ENABLE_FEDERATED_AVATAR = true
[attachment]
PATH = /data/gitea/attachments
[log]
ROOT_PATH = /data/gitea/log
MODE = file
LEVEL = info
[security]
INSTALL_LOCK = true
[service]
DISABLE_REGISTRATION = false
REQUIRE_SIGNIN_VIEW = false
REGISTER_EMAIL_CONFIRM = false
ENABLE_NOTIFY_MAIL = false
ALLOW_ONLY_EXTERNAL_REGISTRATION = false
ENABLE_CAPTCHA = false
DEFAULT_KEEP_EMAIL_PRIVATE = false
DEFAULT_ALLOW_CREATE_ORGANIZATION = true
DEFAULT_ENABLE_TIMETRACKING = true
NO_REPLY_ADDRESS = noreply.localhost
[mailer]
ENABLED = false
[openid]
ENABLE_OPENID_SIGNIN = true
ENABLE_OPENID_SIGNUP = true

4
docker/gitea/config.toml Normal file
View file

@ -0,0 +1,4 @@
gid = 5000
uid = 5000
app_ini = "/app.ini"
app_ini_overwrite = true

View file

@ -1,7 +1,7 @@
{ pkgs, system, nixpkgs, ... }:
let
# pkgs = (import nixpkgs { inherit system; }).pkgsMusl;
env = let
contents = let
defaults = {
userUid = builtins.toString 5001;
userGid = builtins.toString 5001;
@ -29,29 +29,16 @@ let
};
bashLib = ../bash-lib;
in
with pkgs; with defaults; pkgs.writeShellScriptBin "conf" ''
_conf_user_uid=${userUid}
_conf_user_gid=${userGid}
_conf_user=${user}
_conf_data=${data}
_prog_busybox=${busybox}
_prog_gitea=${custom.gitea}
_prog_bash=${bash}
_prog_bash_lib=${bashLib}
'';
csiMount = pkgs.runCommandNoCC
"csiMount" {}
''${pkgs.busybox}/bin/mkdir -p $out/data/gitea'';
init = pkgs.writeShellScript "init" (builtins.readFile ./init);
pkgs.symlinkJoin { name = "contents"; paths = [ custom.gitea ]; };
init = "${pkgs.rust-runner}/bin/gitea";
in
pkgs.dockerTools.buildLayeredImage {
name = "gitea";
tag = "latest";
contents = csiMount;
inherit contents;
config = {
Entrypoint = [ "${init}" "${env}/bin/conf"];
Entrypoint = [ "${init}" ];
};
}

View file

@ -0,0 +1,13 @@
version: "3.3" # optional since v1.27.0
services:
gitea:
image: gitea:latest
ports:
- "3000:3000"
volumes:
- type: bind
source: ./config.toml
target: /config.toml
- type: bind
source: ./config.toml
target: /app.ini

View file

@ -1,90 +0,0 @@
# -*- mode: shell-script; -*-
conf=$1
source $conf
source $_prog_bash_lib/main.bash
if [[ $($_prog_busybox/bin/id -u) = 0 ]] ; then
$_prog_busybox/bin/cat << EOF
### Gitea Nix Image Manual
##
## USER_UID ? $_conf_user_uid - default user id
## USER_GID ? $_conf_user_gid - default group id
## [ APP_INI ] - app.ini file location for Gitea
## APP_INI_OVERWRITE - whether an existing app.ini shall be overwritten, anything else than "" means \`true\`!
## for other options please look at https://docs.gitea.io/en-us/install-with-docker/#environment-variables
### Recommended volumes (many directories which exist in normal Docker containers, do not exist in this one)
##
## - $_conf_data - Gitea base data folder
EOF
default_opt USER_UID "$_conf_user_uid"
default_opt USER_GID "$_conf_user_gid"
default_opt APP_INI ""
_app_ini_overwrite=$([[ ! -z "${APP_INI_OVERWRITE:-}" ]] && printf true || printf false)
$_prog_busybox/bin/cat << EOF
### Starting with options:
## USER_UID = "$_user_uid"
## USER_GID = "$_user_gid"
## APP_INI = "$_app_ini"
## APP_INI_OVERWRITE = "$_app_ini_overwrite"
EOF
$_prog_busybox/bin/env
(
set -e
echo "gitea:x:$_user_uid:$_user_gid:Gitea:$_conf_data:$_prog_bash/bin/bash" > /etc/passwd
echo "gitea:x:$_user_gid:" > /etc/group
) || echo_exit "Failed to create user and group!"
mkdir_chown $_conf_data "$_user_uid" "$_user_gid"
mkdir_chown /tmp "$_user_uid" "$_user_gid"
check_owner "$_conf_data" "$_user_uid" "$_user_gid"
save_env "_user_uid \
_user_gid \
_conf_data \
_prog_gitea \
_app_ini \
_app_ini_overwrite \
" > /env # TODO: exited even though it must have succeded || \
# echo_exit "Failed to save environment!"
check_root "$_user_uid"
exec $_prog_busybox/bin/su gitea -c "$0 $@" || \
echo_exit "Failed to switch user!"
else
source /env || \
echo_exit "Failed to source env!"
if [[ ! -z "$_app_ini" ]] ; then
if [[ -f "$_app_ini" ]] ; then
if [[ ! -f "$_conf_data/app.ini" ]] ; then
$_prog_busybox/bin/cp "$_app_ini" "$_conf_data/app.ini" || \
echo_exit "Failed to copy app.ini!"
else
if [[ "$_app_ini_overwrite" = "true" ]] ; then
$_prog_busybox/bin/cp "$_app_ini" "$_conf_data/app.ini" || \
echo_exit "Failed to copy app.ini!"
elif [[ "$_app_ini_overwrite" = "false" ]] ; then
echo_exit "APP_INI set, but $_conf_data/app.ini exists!"
else
echo_exit "\$_api_ini_overwrite has invalid value $_api_init_overwrite, this is an internal issue, please report ASAP!"
fi
fi
else
echo_exit "APP_INI set, but $_app_ini does not exist!"
fi
fi
export GITEA_WORK_DIR=$_conf_data
echo
echo "Starting Gitea!"
exec $_prog_gitea/bin/gitea -c $_conf_data/app.ini $@
fi

View file

@ -20,26 +20,37 @@
outputs = { self, nixpkgs, ... }@inputs:
let
lib =
rlib =
let
system = "x86_64-linux";
pkgs = import nixpkgs { inherit system; };
in
import ./lib.nix { inherit system nixpkgs pkgs inputs; };
flakes = lib.flakes ./nix-packages [
flakes = rlib.flakes ./nix-packages [
"klippy"
"mainsail"
"moonraker"
"rust-runner"
];
dockerImages = lib.dockerImages ./docker [
pkgs = rlib.pkgsWithFlakes flakes;
dockerImages = rlib.dockerImages pkgs ./docker [
"klippy-moonraker"
"postgresql"
"gitea"
"csi-driver-nfs"
];
pkgs = lib.pkgsWithFlakes flakes;
moduleArg = ({ inherit pkgs; } // inputs);
containerTest = let
all-modules = import <nixpkgs/nixos/modules/module-list.nix>;
custom-module = rec {
services.mysql.enable = true;
};
pkgs = import nixpkgs { system = "x86_64-linux"; };
in
pkgs.writeText "test" (pkgs.lib.evalModules {
modules = all-modules ++ [ custom-module ];
}).config.services.mysql.dataDir;
in {
inherit flakes dockerImages;
inherit flakes dockerImages containerTest;
};
}

View file

@ -6,17 +6,17 @@ with pkgs.lib; {
in
self
);
dockerImages = path: modules: genAttrs modules (module:
dockerImages = pkgs: path: modules: genAttrs modules (module:
import (path + "/${module}") ({ inherit pkgs system; } // inputs)
);
pkgsWithFlakes = flakes: import nixpkgs
{
inherit system;
overlays = builtins.concatLists (forEach flakes (flake:
overlays = builtins.concatLists (mapAttrsToList (_: flake:
if builtins.hasAttr "overlay" flake then
[ flake.overlay ]
else
[]
));
) flakes);
};
}

View file

@ -0,0 +1,2 @@
[build]
target = "x86_64-unknown-linux-musl"

14
nix-packages/rust-runner/.gitignore vendored Normal file
View file

@ -0,0 +1,14 @@
# Created by https://www.toptal.com/developers/gitignore/api/rust
# Edit at https://www.toptal.com/developers/gitignore?templates=rust
### Rust ###
# Generated by Cargo
# will have compiled files and executables
/target/
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock
# End of https://www.toptal.com/developers/gitignore/api/rust

View file

@ -0,0 +1,17 @@
[package]
name = "rust-runner"
version = "0.1.0"
authors = ["Magic_RB <magic_rb@redalder.org>"]
edition = "2018"
[[bin]]
name = "gitea"
path = "src/gitea.rs"
[dependencies]
toml = "0.5.8"
serde = { version = "1.0.122", features = ["derive"] }
nix = "0.19.1"
simplelog = "0.9.0"
log = "0.4.13"
privdrop = "0.5.0"

View file

@ -0,0 +1,32 @@
{
inputs = {
nixpkgs.url = "nixpkgs";
};
outputs = { self, nixpkgs, ... }@inputs:
let
supportedSystems = [ "x86_64-linux" "i686-linux" "aarch64-linux" ];
forAllSystems = f: nixpkgs.lib.genAttrs supportedSystems (system: f system);
in
{
overlay = final: prev:
with final; {
rust-runner = rustPlatform.buildRustPackage rec {
pname = "rust-runner";
version = "0.1";
src = ./.;
buildType = "debug";
cargoSha256 = "Z/Q66St/Q/suG8BxJXNwevdPvTQJgGzmvgGeCzP01KY=";
};
};
defaultPackage = forAllSystems (system: (import nixpkgs {
inherit system;
overlays = [ self.overlay ];
}).rust-runner);
};
}

View file

@ -0,0 +1,187 @@
extern crate log;
extern crate nix;
extern crate serde;
extern crate simplelog;
extern crate toml;
use std::{
error::Error,
fs::File,
io::Read,
path::{Path, PathBuf},
};
use log::{error, info, warn};
use serde::{Deserialize, Serialize};
fn mkdir_chown<S: AsRef<str>>(s: S, uid: u32, gid: u32) -> Result<(), String> {
use nix::{
dir::Dir,
fcntl::OFlag,
sys::stat::{stat, Mode},
unistd::{chown, mkdir},
unistd::{Gid, Uid},
};
use std::fs::create_dir_all;
let s = s.as_ref();
if let Ok(_) = Dir::open(s, OFlag::empty(), Mode::empty()) {
let stat = stat(s).map_err(|err| format!("Failed to stat: {} {}", s, err))?;
if stat.st_uid != uid || stat.st_gid != gid {
warn!(
"Directory {} exists, but has incorrect o/g: {}:{}, trying to chown...",
s, uid, gid
);
chown(s, Some(Uid::from_raw(uid)), Some(Gid::from_raw(gid)))
.map_err(|err| format!("Failed to chown: {}, {}", s, err))
} else {
Ok(())
}
} else {
create_dir_all(s).map_err(|err| format!("Failed to mkdir: {} {}", s, err))?;
chown(s, Some(Uid::from_raw(uid)), Some(Gid::from_raw(gid)))
.map_err(|err| format!("Failed to chown: {} {}", s, err))?;
Ok(())
}
}
fn drop_privileges<S: AsRef<str>>(user: S) -> Result<(), String> {
use privdrop::PrivDrop;
let user = user.as_ref();
PrivDrop::default()
.user(user)
.group(user)
.apply()
.map_err(|err| format!("Failed to drop priviledges to {}:{} {}", user, user, err))
}
fn adduser<S: AsRef<str>>(name: S, uid: u32, gid: u32, home: S, shell: S) -> Result<(), String> {
use std::{fs::OpenOptions, io::Write};
let passwd = "/etc/passwd";
let group = "/etc/group";
let mut file = OpenOptions::new()
.read(false)
.write(true)
.append(true)
.create(true)
.open(passwd)
.map_err(|_| format!("Cannot open: {}", passwd))?;
file.write_all(
format!(
"{}:x:{}:{}:User:{}:{}",
name.as_ref(),
uid,
gid,
home.as_ref(),
shell.as_ref(),
)
.as_bytes(),
)
.map_err(|_| format!("Write to {} failed", passwd))?;
let mut file = OpenOptions::new()
.read(false)
.write(true)
.append(true)
.create(true)
.open(group)
.map_err(|err| format!("Cannot open: {} {}", group, err))?;
file.write_all(format!("{}:x:{}:", name.as_ref(), gid,).as_bytes())
.map_err(|err| format!("Write to {} failed {}", passwd, err))?;
Ok(())
}
fn exec<S: AsRef<str>>(command: S, args: &[S]) -> Result<(), String> {
use std::ffi::CString;
use nix::unistd::execv;
let command = command.as_ref();
let args = args.iter().map(|arg| arg.as_ref());
let args = [command]
.iter()
.map(|command| *command)
.chain(args)
.map(|arg| CString::new(arg))
.collect::<Result<Vec<CString>, _>>()
.map_err(|err| format!("Failed to create CString {}", err))?;
let command = CString::new(command).map_err(|err| format!("Failed to create CString {}", err))?;
execv(command.as_c_str(), args.as_ref()).map_err(|err| format!("execv failed {}", err))?;
Ok(())
}
mod defaults {
pub fn uid() -> u32 {
5000
}
pub fn gid() -> u32 {
5000
}
}
#[derive(Serialize, Deserialize)]
struct Configuration<'a> {
#[serde(default = "defaults::uid")]
gid: u32,
#[serde(default = "defaults::gid")]
uid: u32,
app_ini: Option<&'a str>,
app_ini_overwrite: bool,
}
fn main() -> Result<(), Box<dyn Error>> {
{
use simplelog::{Config, LevelFilter, TermLogger, TerminalMode};
TermLogger::new(LevelFilter::Info, Config::default(), TerminalMode::Mixed)
};
let mut file = File::open("/config.toml").map_err(|err| format!("Failed to open config {}", err))?;
let mut str = String::new();
file.read_to_string(&mut str).map_err(|err| format!("Failed read config {}", err))?;
let config = toml::from_str::<Configuration>(str.as_str())?;
mkdir_chown("/tmp/", config.uid, config.gid)?;
mkdir_chown("/data/gitea/", config.uid, config.gid)?;
mkdir_chown("/etc/", 0, 0)?;
adduser("gitea", config.uid, config.gid, "/data/gitea/", "/bin/sh")?; // TODO /bin/sh doesn't exist, we need busybox
info!("Dropping privileges to config.uid:config.gid");
drop_privileges("gitea")?;
if let Some(app_ini) = config.app_ini {
use nix::sys::stat::stat;
use std::fs::copy;
let stat = stat(app_ini);
if stat.is_err() {
copy(app_ini, "/data/gitea/app.ini").map_err(|err| format!("Failed to copy app.ini {}", err))?;
} else {
if config.app_ini_overwrite {
copy(app_ini, "/data/gitea/app.ini").map_err(|err| format!("Failed to copy app.ini {}", err))?;
} else {
error!("app.ini exists, but not overwriting!");
}
}
}
use std::env::set_var;
set_var("HOME", "/data/gitea/");
set_var("GITEA_WORK_DIR", "/data/gitea/");
exec("/bin/gitea", &["-c", "/data/gitea/app.ini"])?;
Ok(())
}

View file

@ -0,0 +1,3 @@
fn main() {
println!("Hello, world!");
}