Skip to content

Commit 3c5259e

Browse files
author
Michael du Breuil
committed
Sandbox: Add a linux landlock implementation
1 parent 009c40f commit 3c5259e

File tree

8 files changed

+101
-28
lines changed

8 files changed

+101
-28
lines changed

cmd/gonic/gonic.go

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ import (
5252
)
5353

5454
func main() {
55-
sandbox.Init()
55+
sandbox := sandbox.Init()
5656
confListenAddr := flag.String("listen-addr", "0.0.0.0:4747", "listen address (optional)")
5757

5858
confTLSCert := flag.String("tls-cert", "", "path to TLS certificate (optional)")
@@ -102,7 +102,7 @@ func main() {
102102
flagconf.ParseEnv()
103103

104104
if *confConfigPath != "" {
105-
sandbox.ReadOnlyPath(*confConfigPath)
105+
sandbox.ReadOnlyFile(*confConfigPath)
106106
flagconf.ParseConfig(*confConfigPath)
107107
}
108108

@@ -121,21 +121,21 @@ func main() {
121121

122122
var err error
123123
for i, confMusicPath := range confMusicPaths {
124-
sandbox.ReadOnlyPath(confMusicPath.path)
124+
sandbox.ReadOnlyDir(confMusicPath.path)
125125
if confMusicPaths[i].path, err = validatePath(confMusicPath.path); err != nil {
126126
log.Fatalf("checking music dir %q: %v", confMusicPath.path, err)
127127
}
128128
}
129129

130-
sandbox.ReadWriteCreatePath(*confPodcastPath)
130+
sandbox.ReadWriteCreateDir(*confPodcastPath)
131131
if *confPodcastPath, err = validatePath(*confPodcastPath); err != nil {
132132
log.Fatalf("checking podcast directory: %v", err)
133133
}
134-
sandbox.ReadWriteCreatePath(*confCachePath)
134+
sandbox.ReadWriteCreateDir(*confCachePath)
135135
if *confCachePath, err = validatePath(*confCachePath); err != nil {
136136
log.Fatalf("checking cache directory: %v", err)
137137
}
138-
sandbox.ReadWriteCreatePath(*confPlaylistsPath)
138+
sandbox.ReadWriteCreateDir(*confPlaylistsPath)
139139
if *confPlaylistsPath, err = validatePath(*confPlaylistsPath); err != nil {
140140
log.Fatalf("checking playlist directory: %v", err)
141141
}
@@ -149,7 +149,7 @@ func main() {
149149
log.Fatalf("couldn't create covers cache path: %v\n", err)
150150
}
151151

152-
dbc, err := db.New(*confDBPath, db.DefaultOptions())
152+
dbc, err := db.New(*confDBPath, sandbox, db.DefaultOptions())
153153
if err != nil {
154154
log.Fatalf("error opening database: %v\n", err)
155155
}
@@ -161,14 +161,15 @@ func main() {
161161
OriginalMusicPath: confMusicPaths[0].path,
162162
PlaylistsPath: *confPlaylistsPath,
163163
PodcastsPath: *confPodcastPath,
164+
Sandbox: sandbox,
164165
})
165166
if err != nil {
166167
log.Panicf("error migrating database: %v\n", err)
167168
}
168169

169170
if *confTLSCert != "" && *confTLSKey != "" {
170-
sandbox.ReadOnlyPath(*confTLSCert)
171-
sandbox.ReadOnlyPath(*confTLSKey)
171+
sandbox.ReadOnlyFile(*confTLSCert)
172+
sandbox.ReadOnlyFile(*confTLSKey)
172173
}
173174

174175
sandbox.AllPathsAdded()

db/db.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,16 +44,16 @@ type DB struct {
4444
*gorm.DB
4545
}
4646

47-
func New(path string, options url.Values) (*DB, error) {
47+
func New(path string, sandbox sandbox.Sandbox, options url.Values) (*DB, error) {
4848
// https://github.com/mattn/go-sqlite3#connection-string
4949
url := url.URL{
5050
Scheme: "file",
5151
Opaque: path,
5252
}
53-
sandbox.ReadWriteCreatePath(path)
54-
sandbox.ReadWriteCreatePath(path + "-wal")
55-
sandbox.ReadWriteCreatePath(path + "-shm")
56-
sandbox.ReadWriteCreatePath(path + "-journal")
53+
sandbox.ReadWriteCreateFile(path)
54+
sandbox.ReadWriteCreateFile(path + "-wal")
55+
sandbox.ReadWriteCreateFile(path + "-shm")
56+
sandbox.ReadWriteCreateFile(path + "-journal")
5757
url.RawQuery = options.Encode()
5858
db, err := gorm.Open("sqlite3", url.String())
5959
if err != nil {
@@ -65,7 +65,7 @@ func New(path string, options url.Values) (*DB, error) {
6565
}
6666

6767
func NewMock() (*DB, error) {
68-
return New(":memory:", mockOptions())
68+
return New(":memory:", sandbox.Init(), mockOptions())
6969
}
7070

7171
func (db *DB) InsertBulkLeftMany(table string, head []string, left int, col []int) error {
@@ -628,7 +628,7 @@ func join[T fmt.Stringer](in []T, sep string) string {
628628
}
629629

630630
func Dump(ctx context.Context, db *gorm.DB, to string) error {
631-
dest, err := New(to, url.Values{})
631+
dest, err := New(to, sandbox.Init(), url.Values{})
632632
if err != nil {
633633
return fmt.Errorf("create dest db: %w", err)
634634
}

db/migrations.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ type MigrationContext struct {
2525
OriginalMusicPath string
2626
PlaylistsPath string
2727
PodcastsPath string
28+
Sandbox sandbox.Sandbox
2829
}
2930

3031
func (db *DB) Migrate(ctx MigrationContext) error {
@@ -742,7 +743,7 @@ func backupDBPre016(tx *gorm.DB, ctx MigrationContext) error {
742743
return nil
743744
}
744745
backupPath := fmt.Sprintf("%s.%d.bak", ctx.DBPath, time.Now().Unix())
745-
sandbox.ReadWriteCreatePath(backupPath)
746+
ctx.Sandbox.ReadWriteCreateFile(backupPath)
746747
return Dump(context.Background(), tx, backupPath)
747748
}
748749

go.mod

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ require (
4848
github.com/jinzhu/now v1.1.2 // indirect
4949
github.com/josharian/intern v1.0.0 // indirect
5050
github.com/json-iterator/go v1.1.12 // indirect
51+
github.com/landlock-lsm/go-landlock v0.0.0-20250303204525-1544bccde3a3 // indirect
5152
github.com/lib/pq v1.3.0 // indirect
5253
github.com/mailru/easyjson v0.7.7 // indirect
5354
github.com/mattn/go-runewidth v0.0.16 // indirect
@@ -62,9 +63,10 @@ require (
6263
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
6364
golang.org/x/crypto v0.27.0 // indirect
6465
golang.org/x/image v0.20.0 // indirect
65-
golang.org/x/sys v0.25.0 // indirect
66+
golang.org/x/sys v0.26.0 // indirect
6667
golang.org/x/text v0.18.0 // indirect
6768
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect
6869
gopkg.in/yaml.v2 v2.4.0 // indirect
6970
gopkg.in/yaml.v3 v3.0.1 // indirect
71+
kernel.org/pub/linux/libs/security/libcap/psx v1.2.70 // indirect
7072
)

go.sum

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
8585
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
8686
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
8787
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
88+
github.com/landlock-lsm/go-landlock v0.0.0-20250303204525-1544bccde3a3 h1:zcMi8R8vP0WrrXlFMNUBpDy/ydo3sTnCcUPowq1XmSc=
89+
github.com/landlock-lsm/go-landlock v0.0.0-20250303204525-1544bccde3a3/go.mod h1:RSub3ourNF8Hf+swvw49Catm3s7HVf4hzdFxDUnEzdA=
8890
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
8991
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
9092
github.com/lib/pq v1.3.0 h1:/qkRGz8zljWiDcFvgpwUpwIAPu3r07TDvs3Rws+o/pU=
@@ -184,6 +186,8 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
184186
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
185187
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
186188
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
189+
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
190+
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
187191
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
188192
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
189193
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
@@ -216,3 +220,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
216220
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
217221
jaytaylor.com/html2text v0.0.0-20230321000545-74c2419ad056 h1:6YFJoB+0fUH6X3xU/G2tQqCYg+PkGtnZ5nMR5rpw72g=
218222
jaytaylor.com/html2text v0.0.0-20230321000545-74c2419ad056/go.mod h1:OxvTsCwKosqQ1q7B+8FwXqg4rKZ/UG9dUW+g/VL2xH4=
223+
kernel.org/pub/linux/libs/security/libcap/psx v1.2.70 h1:HsB2G/rEQiYyo1bGoQqHZ/Bvd6x1rERQTNdPr1FyWjI=
224+
kernel.org/pub/linux/libs/security/libcap/psx v1.2.70/go.mod h1:+l6Ee2F59XiJ2I6WR5ObpC1utCQJZ/VLsEbQCD8RG24=

sandbox/none.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
1-
//go:build !openbsd
1+
//go:build !(openbsd || linux)
22
// +build !openbsd
33

44
package sandbox
55

66
func Init() {
77
}
88

9-
func ReadOnlyPath(path string) {
9+
func ReadOnlyDir(path string) {
1010
}
1111

12-
func ReadWritePath(path string) {
12+
func ReadOnlyFile(path string) {
1313
}
1414

15-
func ReadWriteCreatePath(path string) {
15+
func ReadWriteCreateDir(path string) {
16+
}
17+
18+
func ReadWriteCreateFile(path string) {
1619
}
1720

1821
func AllPathsAdded() {

sandbox/sandbox_linux.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package sandbox
2+
3+
import (
4+
"github.com/landlock-lsm/go-landlock/landlock"
5+
"log"
6+
"os"
7+
)
8+
9+
type Sandbox struct {
10+
paths []landlock.Rule
11+
}
12+
13+
func Init() Sandbox {
14+
return Sandbox{make([]landlock.Rule, 0)}
15+
}
16+
17+
func (box *Sandbox) ExecPath(path string) {
18+
// landlock does not currently provide anything for us here
19+
}
20+
21+
func (box *Sandbox) ReadOnlyDir(path string) {
22+
box.paths = append(box.paths, landlock.RODirs(path))
23+
}
24+
25+
func (box *Sandbox) ReadOnlyFile(path string) {
26+
box.paths = append(box.paths, landlock.ROFiles(path))
27+
}
28+
29+
func (box *Sandbox) ReadWriteCreateDir(path string) {
30+
box.paths = append(box.paths, landlock.RWDirs(path))
31+
}
32+
33+
func (box *Sandbox) ReadWriteCreateFile(path string) {
34+
// landlock requires the file to already exist
35+
// so create it if it wasn't already present
36+
_, err := os.Stat(path)
37+
switch {
38+
case os.IsNotExist(err):
39+
file, err := os.Create(path)
40+
if err != nil {
41+
log.Fatal("Could not create file for landlock:", err)
42+
} else {
43+
file.Close()
44+
}
45+
}
46+
box.paths = append(box.paths, landlock.RWFiles(path))
47+
}
48+
49+
func (box *Sandbox) AllPathsAdded() {
50+
if err := landlock.V5.BestEffort().RestrictPaths(box.paths...); err != nil {
51+
log.Fatal("Could not enable landlock:", err)
52+
}
53+
// FIXME: clear paths
54+
}

sandbox/sandbox_openbsd.go

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ func Init() {
2929
}
3030
}
3131
// needed to enable certificate validation
32-
ReadOnlyPath("/etc/ssl/cert.pem")
32+
ReadOnlyFile("/etc/ssl/cert.pem")
3333
}
3434

3535
func ExecPath(path string) {
@@ -38,19 +38,25 @@ func ExecPath(path string) {
3838
}
3939
}
4040

41-
func ReadOnlyPath(path string) {
41+
func ReadOnlyDir(path string) {
4242
if err := unix.Unveil(path, "r"); err != nil {
4343
log.Fatalf("failed to unveil read for %s: %v", path, err)
4444
}
4545
}
4646

47-
func ReadWritePath(path string) {
48-
if err := unix.Unveil(path, "rw"); err != nil {
49-
log.Fatalf("failed to unveil read/write for %s: %v", path, err)
47+
func ReadOnlyFile(path string) {
48+
if err := unix.Unveil(path, "r"); err != nil {
49+
log.Fatalf("failed to unveil read for %s: %v", path, err)
50+
}
51+
}
52+
53+
func ReadWriteCreateDir(path string) {
54+
if err := unix.Unveil(path, "rwc"); err != nil {
55+
log.Fatalf("failed to unveil read/write/create for %s: %v", path, err)
5056
}
5157
}
5258

53-
func ReadWriteCreatePath(path string) {
59+
func ReadWriteCreateFile(path string) {
5460
if err := unix.Unveil(path, "rwc"); err != nil {
5561
log.Fatalf("failed to unveil read/write/create for %s: %v", path, err)
5662
}

0 commit comments

Comments
 (0)