Compare commits

..

No commits in common. "master" and "v0.0.1" have entirely different histories.

25 changed files with 113 additions and 215 deletions

View File

@ -73,20 +73,20 @@ jobs:
path: result/*
retention-days: 1
planterette:
name: Planterette
fpkg:
name: Fpkg
runs-on: nix
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Run NixOS test
run: nix build --out-link "result" --print-out-paths --print-build-logs .#checks.x86_64-linux.planterette
run: nix build --out-link "result" --print-out-paths --print-build-logs .#checks.x86_64-linux.fpkg
- name: Upload test output
uses: actions/upload-artifact@v3
with:
name: "planterette-vm-output"
name: "fpkg-vm-output"
path: result/*
retention-days: 1
@ -97,7 +97,7 @@ jobs:
- race
- sandbox
- sandbox-race
- planterette
- fpkg
runs-on: nix
steps:
- name: Checkout

View File

@ -1 +0,0 @@
This port is solely for releasing to the github mirror and serves no purpose during development.

View File

@ -1,46 +0,0 @@
name: Release
on:
push:
tags:
- 'v*'
jobs:
release:
name: Create release
runs-on: ubuntu-latest
permissions:
packages: write
contents: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Nix
uses: nixbuild/nix-quick-install-action@v32
with:
nix_conf: |
keep-env-derivations = true
keep-outputs = true
- name: Restore and cache Nix store
uses: nix-community/cache-nix-action@v6
with:
primary-key: build-${{ runner.os }}-${{ hashFiles('**/*.nix') }}
restore-prefixes-first-match: build-${{ runner.os }}-
gc-max-store-size-linux: 1G
purge: true
purge-prefixes: build-${{ runner.os }}-
purge-created: 60
purge-primary-key: never
- name: Build for release
run: nix build --print-out-paths --print-build-logs .#dist
- name: Release
uses: softprops/action-gh-release@v2
with:
files: |-
result/hakurei-**

View File

@ -1,48 +0,0 @@
name: Test
on:
- push
jobs:
dist:
name: Create distribution
runs-on: ubuntu-latest
permissions:
actions: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Nix
uses: nixbuild/nix-quick-install-action@v32
with:
nix_conf: |
keep-env-derivations = true
keep-outputs = true
- name: Restore and cache Nix store
uses: nix-community/cache-nix-action@v6
with:
primary-key: build-${{ runner.os }}-${{ hashFiles('**/*.nix') }}
restore-prefixes-first-match: build-${{ runner.os }}-
gc-max-store-size-linux: 1G
purge: true
purge-prefixes: build-${{ runner.os }}-
purge-created: 60
purge-primary-key: never
- name: Build for test
id: build-test
run: >-
export HAKUREI_REV="$(git rev-parse --short HEAD)" &&
sed -i.old 's/version = /version = "0.0.0-'$HAKUREI_REV'"; # version = /' package.nix &&
nix build --print-out-paths --print-build-logs .#dist &&
mv package.nix.old package.nix &&
echo "rev=$HAKUREI_REV" >> $GITHUB_OUTPUT
- name: Upload test build
uses: actions/upload-artifact@v4
with:
name: "hakurei-${{ steps.build-test.outputs.rev }}"
path: result/*
retention-days: 1

View File

@ -1,29 +1,38 @@
<p align="center">
<a href="https://git.gensokyo.uk/security/hakurei">
<picture>
<img src="https://basement.gensokyo.uk/images/yukari1.png" width="200px" alt="Yukari">
</picture>
</a>
</p>
Hakurei
=======
<p align="center">
<a href="https://pkg.go.dev/git.gensokyo.uk/security/hakurei"><img src="https://pkg.go.dev/badge/git.gensokyo.uk/security/hakurei.svg" alt="Go Reference" /></a>
<a href="https://goreportcard.com/report/git.gensokyo.uk/security/hakurei"><img src="https://goreportcard.com/badge/git.gensokyo.uk/security/hakurei" alt="Go Report Card" /></a>
</p>
[![Go Reference](https://pkg.go.dev/badge/git.gensokyo.uk/security/hakurei.svg)](https://pkg.go.dev/git.gensokyo.uk/security/hakurei)
[![Go Report Card](https://goreportcard.com/badge/git.gensokyo.uk/security/hakurei)](https://goreportcard.com/report/git.gensokyo.uk/security/hakurei)
Hakurei is a tool for running sandboxed graphical applications as dedicated subordinate users on the Linux kernel.
It also implements [planterette (WIP)](cmd/planterette), a self-contained Android-like package manager with modern security features.
Lets you run graphical applications as dedicated subordinate users in a container environment with a nice NixOS
module to configure target users and provide launch scripts and desktop files.
## NixOS Module usage
Why would you want this?
The NixOS module currently requires home-manager to configure subordinate users. Full module documentation can be found [here](options.md).
- It protects the desktop environment from applications.
- It protects applications from each other.
- It provides UID isolation on top of the standard application sandbox.
If you have a flakes-enabled nix environment, you can try out the tool by running:
```shell
nix run git+https://git.gensokyo.uk/security/hakurei -- help
```
## Module usage
The NixOS module currently requires home-manager to configure subordinate users.
Full module documentation can be found [here](options.md).
To use the module, import it into your configuration with
```nix
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11";
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05";
hakurei = {
url = "git+https://git.gensokyo.uk/security/hakurei";

View File

@ -13,23 +13,36 @@ import (
"git.gensokyo.uk/security/hakurei/command"
"git.gensokyo.uk/security/hakurei/hst"
"git.gensokyo.uk/security/hakurei/internal"
"git.gensokyo.uk/security/hakurei/internal/app/instance"
"git.gensokyo.uk/security/hakurei/internal/hlog"
"git.gensokyo.uk/security/hakurei/internal/sys"
"git.gensokyo.uk/security/hakurei/sandbox"
)
const shellPath = "/run/current-system/sw/bin/bash"
var (
errSuccess = errors.New("success")
std sys.State = new(sys.Std)
)
func init() {
hlog.Prepare("planterette")
hlog.Prepare("fpkg")
if err := os.Setenv("SHELL", shellPath); err != nil {
log.Fatalf("cannot set $SHELL: %v", err)
}
}
func main() {
// early init path, skips root check and duplicate PR_SET_DUMPABLE
sandbox.TryArgv0(hlog.Output{}, hlog.Prepare, internal.InstallFmsg)
if err := sandbox.SetDumpable(sandbox.SUID_DUMP_DISABLE); err != nil {
log.Printf("cannot set SUID_DUMP_DISABLE: %s", err)
// not fatal: this program runs as the privileged user
}
if os.Geteuid() == 0 {
log.Fatal("this program must not run as root")
}
@ -42,10 +55,15 @@ func main() {
flagVerbose bool
flagDropShell bool
)
c := command.New(os.Stderr, log.Printf, "planterette", func([]string) error { internal.InstallFmsg(flagVerbose); return nil }).
c := command.New(os.Stderr, log.Printf, "fpkg", func([]string) error {
internal.InstallFmsg(flagVerbose)
return nil
}).
Flag(&flagVerbose, "v", command.BoolFlag(false), "Print debug messages to the console").
Flag(&flagDropShell, "s", command.BoolFlag(false), "Drop to a shell in place of next hakurei action")
c.Command("shim", command.UsageInternal, func([]string) error { instance.ShimMain(); return errSuccess })
{
var (
flagDropShellActivate bool
@ -66,7 +84,7 @@ func main() {
}
/*
Look up paths to programs started by planterette.
Look up paths to programs started by fpkg.
This is done here to ease error handling as cleanup is not yet required.
*/
@ -82,7 +100,7 @@ func main() {
*/
var workDir string
if p, err := os.MkdirTemp("", "planterette.*"); err != nil {
if p, err := os.MkdirTemp("", "fpkg.*"); err != nil {
log.Printf("cannot create temporary directory: %v", err)
return err
} else {

29
cmd/fpkg/proc.go Normal file
View File

@ -0,0 +1,29 @@
package main
import (
"context"
"os"
"git.gensokyo.uk/security/hakurei/hst"
"git.gensokyo.uk/security/hakurei/internal/app"
"git.gensokyo.uk/security/hakurei/internal/app/instance"
"git.gensokyo.uk/security/hakurei/internal/hlog"
)
func mustRunApp(ctx context.Context, config *hst.Config, beforeFail func()) {
rs := new(app.RunState)
a := instance.MustNew(instance.ISetuid, ctx, std)
var code int
if sa, err := a.Seal(config); err != nil {
hlog.PrintBaseError(err, "cannot seal app:")
code = 1
} else {
code = instance.PrintRunStateErr(instance.ISetuid, rs, sa.Run(rs))
}
if code != 0 {
beforeFail()
os.Exit(code)
}
}

View File

@ -9,7 +9,7 @@ let
buildPackage = self.buildPackage.${system};
in
nixosTest {
name = "planterette";
name = "fpkg";
nodes.machine = {
environment.etc = {
"foot.pkg".source = callPackage ./foot.nix { inherit buildPackage; };

View File

@ -79,15 +79,15 @@ print(machine.succeed("sudo -u alice -i hakurei version"))
machine.wait_for_file("/run/user/1000/wayland-1")
machine.wait_for_file("/tmp/sway-ipc.sock")
# Prepare planterette directory:
# Prepare fpkg directory:
machine.succeed("install -dm 0700 -o alice -g users /var/lib/hakurei/1000")
# Install planterette app:
swaymsg("exec planterette -v install /etc/foot.pkg && touch /tmp/planterette-install-ok")
machine.wait_for_file("/tmp/planterette-install-ok")
# Install fpkg app:
swaymsg("exec fpkg -v install /etc/foot.pkg && touch /tmp/fpkg-install-done")
machine.wait_for_file("/tmp/fpkg-install-done")
# Start app (foot) with Wayland enablement:
swaymsg("exec planterette -v start org.codeberg.dnkl.foot")
swaymsg("exec fpkg -v start org.codeberg.dnkl.foot")
wait_for_window("hakurei@machine-foot")
machine.send_chars("clear; wayland-info && touch /tmp/success-client\n")
machine.wait_for_file("/tmp/hakurei.1000/tmpdir/2/success-client")

View File

@ -41,7 +41,7 @@ func main() {
log.Fatalf("cannot read parent executable path: %v", err)
} else if strings.HasSuffix(p, " (deleted)") {
log.Fatal("hakurei executable has been deleted")
} else if p != mustCheckPath(hmain) {
} else if p != mustCheckPath(hmain) && p != mustCheckPath(fpkg) {
log.Fatal("this program must be started by hakurei")
} else {
toolPath = p

View File

@ -16,8 +16,15 @@ buildGoModule {
go mod init hsu >& /dev/null
'';
ldflags = lib.attrsets.foldlAttrs (
ldflags: name: value:
ldflags ++ [ "-X main.${name}=${value}" ]
) [ "-s -w" ] { hmain = "${hakurei}/libexec/hakurei"; };
ldflags =
lib.attrsets.foldlAttrs
(
ldflags: name: value:
ldflags ++ [ "-X main.${name}=${value}" ]
)
[ "-s -w" ]
{
hmain = "${hakurei}/libexec/hakurei";
fpkg = "${hakurei}/libexec/fpkg";
};
}

View File

@ -9,6 +9,7 @@ const compPoison = "INVALIDINVALIDINVALIDINVALIDINVALID"
var (
hmain = compPoison
fpkg = compPoison
)
func mustCheckPath(p string) string {

View File

@ -1,60 +0,0 @@
package main
import (
"context"
"encoding/json"
"errors"
"io"
"log"
"os"
"os/exec"
"git.gensokyo.uk/security/hakurei/hst"
"git.gensokyo.uk/security/hakurei/internal"
"git.gensokyo.uk/security/hakurei/internal/hlog"
)
var hakureiPath = internal.MustHakureiPath()
func mustRunApp(ctx context.Context, config *hst.Config, beforeFail func()) {
var (
cmd *exec.Cmd
st io.WriteCloser
)
if r, w, err := os.Pipe(); err != nil {
beforeFail()
log.Fatalf("cannot pipe: %v", err)
} else {
if hlog.Load() {
cmd = exec.CommandContext(ctx, hakureiPath, "-v", "app", "3")
} else {
cmd = exec.CommandContext(ctx, hakureiPath, "app", "3")
}
cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
cmd.ExtraFiles = []*os.File{r}
st = w
}
go func() {
if err := json.NewEncoder(st).Encode(config); err != nil {
beforeFail()
log.Fatalf("cannot send configuration: %v", err)
}
}()
if err := cmd.Start(); err != nil {
beforeFail()
log.Fatalf("cannot start hakurei: %v", err)
}
if err := cmd.Wait(); err != nil {
var exitError *exec.ExitError
if errors.As(err, &exitError) {
beforeFail()
internal.Exit(exitError.ExitCode())
} else {
beforeFail()
log.Fatalf("cannot wait: %v", err)
}
}
}

2
dist/install.sh vendored
View File

@ -2,7 +2,7 @@
cd "$(dirname -- "$0")" || exit 1
install -vDm0755 "bin/hakurei" "${HAKUREI_INSTALL_PREFIX}/usr/bin/hakurei"
install -vDm0755 "bin/planterette" "${HAKUREI_INSTALL_PREFIX}/usr/bin/planterette"
install -vDm0755 "bin/fpkg" "${HAKUREI_INSTALL_PREFIX}/usr/bin/fpkg"
install -vDm6511 "bin/hsu" "${HAKUREI_INSTALL_PREFIX}/usr/bin/hsu"
if [ ! -f "${HAKUREI_INSTALL_PREFIX}/etc/hsurc" ]; then

4
dist/release.sh vendored
View File

@ -11,9 +11,9 @@ cp -rv "dist/comp" "${out}"
go generate ./...
go build -trimpath -v -o "${out}/bin/" -ldflags "-s -w -buildid= -extldflags '-static'
-X git.gensokyo.uk/security/hakurei/internal.version=${VERSION}
-X git.gensokyo.uk/security/hakurei/internal.hakurei=/usr/bin/hakurei
-X git.gensokyo.uk/security/hakurei/internal.hsu=/usr/bin/hsu
-X main.hmain=/usr/bin/hakurei" ./...
-X main.hmain=/usr/bin/hakurei
-X main.fpkg=/usr/bin/fpkg" ./...
rm -f "./${out}.tar.gz" && tar -C dist -czf "${out}.tar.gz" "${pname}"
rm -rf "./${out}"

View File

@ -32,7 +32,7 @@
buildPackage = forAllSystems (
system:
nixpkgsFor.${system}.callPackage (
import ./cmd/planterette/build.nix {
import ./cmd/fpkg/build.nix {
inherit
nixpkgsFor
system
@ -69,7 +69,7 @@
withRace = true;
};
planterette = callPackage ./cmd/planterette/test { inherit system self; };
fpkg = callPackage ./cmd/fpkg/test { inherit system self; };
formatting = runCommandLocal "check-formatting" { nativeBuildInputs = [ nixfmt-rfc-style ]; } ''
cd ${./.}
@ -125,7 +125,7 @@
glibc
xdg-dbus-proxy
# planterette
# fpkg
zstd
gnutar
coreutils

View File

@ -8,26 +8,16 @@ import (
)
var (
hakurei = compPoison
hsu = compPoison
hsu = compPoison
)
func MustHakureiPath() string {
if name, ok := checkPath(hakurei); ok {
return name
}
hlog.BeforeExit()
log.Fatal("invalid hakurei path, this program is compiled incorrectly")
return compPoison // unreachable
}
func MustHsuPath() string {
if name, ok := checkPath(hsu); ok {
return name
}
hlog.BeforeExit()
log.Fatal("invalid hsu path, this program is compiled incorrectly")
return compPoison // unreachable
return compPoison
}
func checkPath(p string) (string, bool) { return p, p != compPoison && p != "" && path.IsAbs(p) }

View File

@ -35,7 +35,7 @@ package
*Default:*
` <derivation hakurei-static-x86_64-unknown-linux-musl-0.0.2> `
` <derivation hakurei-static-x86_64-unknown-linux-musl-0.4.1> `
@ -916,7 +916,7 @@ package
*Default:*
` <derivation hakurei-hsu-0.0.2> `
` <derivation hakurei-hsu-0.4.1> `

View File

@ -13,7 +13,7 @@
wayland-scanner,
xorg,
# for planterette
# for fpkg
zstd,
gnutar,
coreutils,
@ -31,7 +31,7 @@
buildGoModule rec {
pname = "hakurei";
version = "0.0.2";
version = "0.0.1";
srcFiltered = builtins.path {
name = "${pname}-src";
@ -76,7 +76,6 @@ buildGoModule rec {
)
{
version = "v${version}";
hakurei = "${placeholder "out"}/libexec/hakurei";
hsu = "/run/wrappers/bin/hsu";
};
@ -117,7 +116,7 @@ buildGoModule rec {
makeBinaryWrapper "$out/libexec/hakurei" "$out/bin/hakurei" \
--inherit-argv0 --prefix PATH : ${lib.makeBinPath appPackages}
makeBinaryWrapper "$out/libexec/planterette" "$out/bin/planterette" \
makeBinaryWrapper "$out/libexec/fpkg" "$out/bin/fpkg" \
--inherit-argv0 --prefix PATH : ${
lib.makeBinPath (
appPackages