Compare commits

...

10 Commits

Author SHA1 Message Date
c638a35432 plemya-x 2024-06-01 16:10:03 +03:00
b5c16221ff Update go.mod 2024-06-01 15:49:20 +03:00
8b34ccd530 Update go.mod 2024-06-01 15:48:58 +03:00
a407c76760 Update go.mod
module plemya-x.ru/fakeroot
2024-06-01 15:37:08 +03:00
Elara Musayelyan
b2da39c1be Add README section about nesting 2023-10-24 13:51:52 -07:00
Elara Musayelyan
d34dcaca37 Propagate exit code for nsfakeroot command 2023-10-24 13:03:22 -07:00
Elara Musayelyan
bb05559c1f Add pkgsite badge 2023-10-23 21:58:39 -07:00
Elara Musayelyan
4c69f15363 Add nsfakeroot to README 2023-10-23 21:56:50 -07:00
Elara Musayelyan
c9e6f61ce1 Rename cmd/gofakeroot to cmd/nsfakeroot 2023-10-23 21:49:32 -07:00
Elara Musayelyan
b130d64a68 Add tests 2023-10-23 17:01:08 -07:00
6 changed files with 95 additions and 8 deletions

2
.gitignore vendored
View File

@@ -1 +1 @@
/gofakeroot
/nsfakeroot

View File

@@ -1,5 +1,7 @@
# fakeroot
[![Go Reference](https://pkg.go.dev/badge/lure.sh/fakeroot.svg)](https://pkg.go.dev/lure.sh/fakeroot)
A pure-Go implementation of fakeroot using Linux user namespaces.
### What is fakeroot?
@@ -8,10 +10,33 @@ Fakeroot is a utility that runs commands in an environment where they appear to
### How is this library different?
Instead of injecting custom libc functions, this library uses the Linux kernel's built-in isolation features to make a sort of container where the user is root. That means even programs that don't use libc (such as Go programs), or programs with a statically-linked libc, will believe they're running as root. However, this approach will only work on Linux kernels new enough (3.8+) and on distros that don't disable this functionality. Most modern Linux systems support it though, so it should work in most cases.
Instead of injecting custom libc functions, this library uses the Linux kernel's built-in isolation features to make a sort of container where the user is root. That means even programs that don't use libc (such as Go programs), or programs with a statically-linked libc, will believe they're running as root.
You can also nest this type of fakeroot up to 32 times, unlike the original libc-based one, which doesn't support nesting at all.
However, this approach will only work on Linux kernels new enough (3.8+) and on distros that don't disable this functionality. Most modern Linux systems support it though, so it should work in most cases.
### Why?
Many utilities depend on file permissions and user ownership. For instance, the tar command creates files within a tar archive with the same permissions as the original files. This means that if the files were owned by a specific user, they will retain that ownership when the tar archive is extracted. This can become problematic when building packages because it could lead to system files in a package being owned by non-root users. By making it seem as if the current user is root and therefore all the files are owned by root, fakeroot tricks utilities like `tar` into making its files owned by root.
Also, many utilities may require root privileges for certain operations but might return errors even when the specific task doesn't necessarily need those elevated permissions. Fakeroot can be used to execute these programs without actually granting them root privileges, which provides some extra security.
### nsfakeroot
This repo includdes a command-line utility called `nsfakeroot`. To install it, run the following command:
```bash
go install lure.sh/fakeroot/cmd/nsfakeroot@latest
```
Running `nsfakeroot` on its own will start your login shell in the fakeroot environment. If you provide arguments, those will be used as the command.
Examples:
```bash
nsfakeroot # -> (login shell)
nsfakeroot whoami # -> root
nsfakeroot id -u # -> 0
nsfakeroot id -g # -> 0
```

View File

@@ -5,9 +5,10 @@ import (
"fmt"
"log"
"os"
"os/exec"
"lure.sh/fakeroot"
"lure.sh/fakeroot/loginshell"
"plemya-x.ru/fakeroot"
"plemya-x.ru/fakeroot/loginshell"
)
func main() {
@@ -26,7 +27,7 @@ func main() {
args []string
err error
)
if flag.NArg() > 1 {
if flag.NArg() > 0 {
cmd = flag.Arg(0)
args = flag.Args()[1:]
} else {
@@ -46,13 +47,15 @@ func main() {
c.Stderr = os.Stderr
err = c.Run()
if err != nil {
if err, ok := err.(*exec.ExitError); ok {
os.Exit(err.ExitCode())
} else if err != nil {
log.Fatalln(err)
}
}
func printHelp() {
fmt.Print("Fakeroot implementation written in Go.\n\n")
fmt.Print("Go implementation of fakeroot using Linux user namespaces.\n\n")
fmt.Print("Usage: fakeroot [cmd] [args...]\n\n")
fmt.Print("Arguments:\n")
fmt.Print(" [cmd] Command to execute in fakeroot environment. If not specified, the user's login shell will be executed.\n")

59
fakeroot_test.go Normal file
View File

@@ -0,0 +1,59 @@
package fakeroot_test
import (
"errors"
"os/exec"
"syscall"
"testing"
"plemya-x.ru/fakeroot"
)
func TestCommand(t *testing.T) {
cmd, err := fakeroot.Command("whoami")
if err != nil {
t.Errorf("Unexpected error while creating command: %q", err)
}
data, err := cmd.CombinedOutput()
if err != nil {
t.Errorf("Unexpected error while executing command: %q", err)
}
if sdata := string(data); sdata != "root\n" {
t.Errorf("Expected %q, got %q", "root\n", sdata)
}
}
func TestCommandUIDError(t *testing.T) {
cmd := exec.Command("whoami")
cmd.SysProcAttr = &syscall.SysProcAttr{
UidMappings: []syscall.SysProcIDMap{
{
ContainerID: 0,
HostID: 1000,
Size: 1,
},
},
}
err := fakeroot.Apply(cmd)
if !errors.Is(err, fakeroot.ErrRootUIDAlreadyMapped) {
t.Errorf("Expected error %q, got %q", fakeroot.ErrRootUIDAlreadyMapped, err)
}
}
func TestCommandGIDError(t *testing.T) {
cmd := exec.Command("whoami")
cmd.SysProcAttr = &syscall.SysProcAttr{
GidMappings: []syscall.SysProcIDMap{
{
ContainerID: 0,
HostID: 1000,
Size: 1,
},
},
}
err := fakeroot.Apply(cmd)
if !errors.Is(err, fakeroot.ErrRootGIDAlreadyMapped) {
t.Errorf("Expected error %q, got %q", fakeroot.ErrRootGIDAlreadyMapped, err)
}
}

2
go.mod
View File

@@ -1,3 +1,3 @@
module lure.sh/fakeroot
module plemya-x.ru/fakeroot
go 1.21

0
go.sum Normal file
View File