Skip to content

Commit df763bd

Browse files
Michael du BreuilWickedShell
Michael du Breuil
authored andcommitted
Initial implementation of a sandbox for OpenBSD
Leverages pledge and unveil, and leaves a public API for other systems to follow. The API was designed to match the OpenBSD side as that's the initial target, if a BPF/capsicum implementation is brought forward it may be worth changing the API, and we should be okay with that.
1 parent 643ffff commit df763bd

File tree

5 files changed

+110
-2
lines changed

5 files changed

+110
-2
lines changed

cmd/gonic/gonic.go

+19-1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import (
4141
"go.senan.xyz/gonic/listenbrainz"
4242
"go.senan.xyz/gonic/playlist"
4343
"go.senan.xyz/gonic/podcast"
44+
"go.senan.xyz/gonic/sandbox"
4445
"go.senan.xyz/gonic/scanner"
4546
"go.senan.xyz/gonic/scrobble"
4647
"go.senan.xyz/gonic/server/ctrladmin"
@@ -51,6 +52,7 @@ import (
5152
)
5253

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

5658
confTLSCert := flag.String("tls-cert", "", "path to TLS certificate (optional)")
@@ -98,7 +100,11 @@ func main() {
98100

99101
flag.Parse()
100102
flagconf.ParseEnv()
101-
flagconf.ParseConfig(*confConfigPath)
103+
104+
if *confConfigPath != "" {
105+
sandbox.ReadOnlyPath(*confConfigPath);
106+
flagconf.ParseConfig(*confConfigPath)
107+
}
102108

103109
if *confShowVersion {
104110
fmt.Printf("v%s\n", gonic.Version)
@@ -115,17 +121,21 @@ func main() {
115121

116122
var err error
117123
for i, confMusicPath := range confMusicPaths {
124+
sandbox.ReadOnlyPath (confMusicPath.path)
118125
if confMusicPaths[i].path, err = validatePath(confMusicPath.path); err != nil {
119126
log.Fatalf("checking music dir %q: %v", confMusicPath.path, err)
120127
}
121128
}
122129

130+
sandbox.ReadWriteCreatePath (*confPodcastPath)
123131
if *confPodcastPath, err = validatePath(*confPodcastPath); err != nil {
124132
log.Fatalf("checking podcast directory: %v", err)
125133
}
134+
sandbox.ReadWriteCreatePath (*confCachePath)
126135
if *confCachePath, err = validatePath(*confCachePath); err != nil {
127136
log.Fatalf("checking cache directory: %v", err)
128137
}
138+
sandbox.ReadWriteCreatePath (*confPlaylistsPath)
129139
if *confPlaylistsPath, err = validatePath(*confPlaylistsPath); err != nil {
130140
log.Fatalf("checking playlist directory: %v", err)
131141
}
@@ -156,6 +166,14 @@ func main() {
156166
log.Panicf("error migrating database: %v\n", err)
157167
}
158168

169+
170+
if *confTLSCert != "" && *confTLSKey != "" {
171+
sandbox.ReadOnlyPath(*confTLSCert);
172+
sandbox.ReadOnlyPath(*confTLSKey);
173+
}
174+
175+
sandbox.AllPathsAdded()
176+
159177
var musicPaths []ctrlsubsonic.MusicPath
160178
for _, pa := range confMusicPaths {
161179
musicPaths = append(musicPaths, ctrlsubsonic.MusicPath{Alias: pa.alias, Path: pa.path})

db/db.go

+6
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import (
1515
"github.com/jinzhu/gorm"
1616
"github.com/mattn/go-sqlite3"
1717

18+
"go.senan.xyz/gonic/sandbox"
19+
1820
// TODO: remove this dep
1921
"go.senan.xyz/gonic/server/ctrlsubsonic/specid"
2022
)
@@ -48,6 +50,10 @@ func New(path string, options url.Values) (*DB, error) {
4850
Scheme: "file",
4951
Opaque: path,
5052
}
53+
sandbox.ReadWriteCreatePath(path)
54+
sandbox.ReadWriteCreatePath(path + "-wal")
55+
sandbox.ReadWriteCreatePath(path + "-shm")
56+
sandbox.ReadWriteCreatePath(path + "-journal")
5157
url.RawQuery = options.Encode()
5258
db, err := gorm.Open("sqlite3", url.String())
5359
if err != nil {

db/migrations.go

+4-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"github.com/jinzhu/gorm"
1515
"go.senan.xyz/gonic/fileutil"
1616
"go.senan.xyz/gonic/playlist"
17+
"go.senan.xyz/gonic/sandbox"
1718
"go.senan.xyz/gonic/server/ctrlsubsonic/specid"
1819
"gopkg.in/gormigrate.v1"
1920
)
@@ -734,7 +735,9 @@ func backupDBPre016(tx *gorm.DB, ctx MigrationContext) error {
734735
if !ctx.Production {
735736
return nil
736737
}
737-
return Dump(context.Background(), tx, fmt.Sprintf("%s.%d.bak", ctx.DBPath, time.Now().Unix()))
738+
backupPath := fmt.Sprintf("%s.%d.bak", ctx.DBPath, time.Now().Unix())
739+
sandbox.ReadWriteCreatePath(backupPath)
740+
return Dump(context.Background(), tx, backupPath)
738741
}
739742

740743
func migrateAlbumTagArtistString(tx *gorm.DB, _ MigrationContext) error {

sandbox/none.go

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// +build !openbsd
2+
3+
package sandbox
4+
5+
func Init () {
6+
}
7+
8+
func ReadOnlyPath (path string) {
9+
}
10+
11+
func ReadWritePath (path string) {
12+
}
13+
14+
func ReadWriteCreatePath (path string) {
15+
}
16+
17+
func AllPathsAdded () {
18+
}

sandbox/sandbox_openbsd.go

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package sandbox
2+
3+
import (
4+
"os/exec"
5+
"log"
6+
7+
"golang.org/x/sys/unix"
8+
)
9+
10+
func Init () {
11+
if err := unix.PledgePromises("stdio rpath cpath wpath flock inet unveil dns proc exec fattr"); err != nil {
12+
log.Fatalf ("failed to pledge: %v", err)
13+
}
14+
// find the transcoding and jukebox paths before doing any other unveils
15+
// otherwise looking for it will fail
16+
ffmpegPath, ffmpegErr := exec.LookPath("ffmpeg")
17+
mpvPath, mpvErr := exec.LookPath("mpv")
18+
if ffmpegErr == nil || mpvErr == nil {
19+
if ffmpegErr == nil {
20+
ExecPath(ffmpegPath)
21+
}
22+
if mpvErr == nil {
23+
ExecPath(mpvPath)
24+
}
25+
} else {
26+
// we can restrict our permissions
27+
if err := unix.PledgePromises("stdio rpath cpath wpath flock inet unveil dns"); err != nil {
28+
log.Fatalf ("failed to pledge: %v", err)
29+
}
30+
}
31+
// needed to enable certificate validation
32+
ReadOnlyPath ("/etc/ssl/cert.pem")
33+
}
34+
35+
func ExecPath (path string) {
36+
if err := unix.Unveil (path, "rx"); err != nil {
37+
log.Fatalf ("failed to unveil exec for %s: %v", path, err)
38+
}
39+
}
40+
41+
func ReadOnlyPath (path string) {
42+
if err := unix.Unveil (path, "r"); err != nil {
43+
log.Fatalf ("failed to unveil read for %s: %v", path, err)
44+
}
45+
}
46+
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)
50+
}
51+
}
52+
53+
func ReadWriteCreatePath (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)
56+
}
57+
}
58+
59+
func AllPathsAdded () {
60+
if err := unix.UnveilBlock(); err != nil {
61+
log.Fatalf ("failed to finalize unveil: %v", err)
62+
}
63+
}

0 commit comments

Comments
 (0)