Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial implementation of a sandbox for OpenBSD #545

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion cmd/gonic/gonic.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import (
"go.senan.xyz/gonic/listenbrainz"
"go.senan.xyz/gonic/playlist"
"go.senan.xyz/gonic/podcast"
"go.senan.xyz/gonic/sandbox"
"go.senan.xyz/gonic/scanner"
"go.senan.xyz/gonic/scrobble"
"go.senan.xyz/gonic/server/ctrladmin"
Expand All @@ -51,6 +52,7 @@ import (
)

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

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

flag.Parse()
flagconf.ParseEnv()
flagconf.ParseConfig(*confConfigPath)

if *confConfigPath != "" {
sandbox.ReadOnlyPath(*confConfigPath)
flagconf.ParseConfig(*confConfigPath)
}

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

var err error
for i, confMusicPath := range confMusicPaths {
sandbox.ReadOnlyPath(confMusicPath.path)
if confMusicPaths[i].path, err = validatePath(confMusicPath.path); err != nil {
log.Fatalf("checking music dir %q: %v", confMusicPath.path, err)
}
}

sandbox.ReadWriteCreatePath(*confPodcastPath)
if *confPodcastPath, err = validatePath(*confPodcastPath); err != nil {
log.Fatalf("checking podcast directory: %v", err)
}
sandbox.ReadWriteCreatePath(*confCachePath)
if *confCachePath, err = validatePath(*confCachePath); err != nil {
log.Fatalf("checking cache directory: %v", err)
}
sandbox.ReadWriteCreatePath(*confPlaylistsPath)
if *confPlaylistsPath, err = validatePath(*confPlaylistsPath); err != nil {
log.Fatalf("checking playlist directory: %v", err)
}
Expand Down Expand Up @@ -156,6 +166,13 @@ func main() {
log.Panicf("error migrating database: %v\n", err)
}

if *confTLSCert != "" && *confTLSKey != "" {
sandbox.ReadOnlyPath(*confTLSCert)
sandbox.ReadOnlyPath(*confTLSKey)
}

sandbox.AllPathsAdded()

var musicPaths []ctrlsubsonic.MusicPath
for _, pa := range confMusicPaths {
musicPaths = append(musicPaths, ctrlsubsonic.MusicPath{Alias: pa.alias, Path: pa.path})
Expand Down
6 changes: 6 additions & 0 deletions db/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import (
"github.com/jinzhu/gorm"
"github.com/mattn/go-sqlite3"

"go.senan.xyz/gonic/sandbox"

// TODO: remove this dep
"go.senan.xyz/gonic/server/ctrlsubsonic/specid"
)
Expand Down Expand Up @@ -48,6 +50,10 @@ func New(path string, options url.Values) (*DB, error) {
Scheme: "file",
Opaque: path,
}
sandbox.ReadWriteCreatePath(path)
sandbox.ReadWriteCreatePath(path + "-wal")
sandbox.ReadWriteCreatePath(path + "-shm")
sandbox.ReadWriteCreatePath(path + "-journal")
url.RawQuery = options.Encode()
db, err := gorm.Open("sqlite3", url.String())
if err != nil {
Expand Down
5 changes: 4 additions & 1 deletion db/migrations.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/jinzhu/gorm"
"go.senan.xyz/gonic/fileutil"
"go.senan.xyz/gonic/playlist"
"go.senan.xyz/gonic/sandbox"
"go.senan.xyz/gonic/server/ctrlsubsonic/specid"
"gopkg.in/gormigrate.v1"
)
Expand Down Expand Up @@ -734,7 +735,9 @@ func backupDBPre016(tx *gorm.DB, ctx MigrationContext) error {
if !ctx.Production {
return nil
}
return Dump(context.Background(), tx, fmt.Sprintf("%s.%d.bak", ctx.DBPath, time.Now().Unix()))
backupPath := fmt.Sprintf("%s.%d.bak", ctx.DBPath, time.Now().Unix())
sandbox.ReadWriteCreatePath(backupPath)
return Dump(context.Background(), tx, backupPath)
}

func migrateAlbumTagArtistString(tx *gorm.DB, _ MigrationContext) error {
Expand Down
19 changes: 19 additions & 0 deletions sandbox/none.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//go:build !openbsd
// +build !openbsd

package sandbox

func Init() {
}

func ReadOnlyPath(path string) {
}

func ReadWritePath(path string) {
}

func ReadWriteCreatePath(path string) {
}

func AllPathsAdded() {
}
63 changes: 63 additions & 0 deletions sandbox/sandbox_openbsd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package sandbox

import (
"log"
"os/exec"

"golang.org/x/sys/unix"
)

func Init() {
if err := unix.PledgePromises("stdio rpath cpath wpath flock inet unveil dns proc exec fattr"); err != nil {
log.Fatalf("failed to pledge: %v", err)
}
// find the transcoding and jukebox paths before doing any other unveils
// otherwise looking for it will fail
ffmpegPath, ffmpegErr := exec.LookPath("ffmpeg")
mpvPath, mpvErr := exec.LookPath("mpv")
if ffmpegErr == nil || mpvErr == nil {
if ffmpegErr == nil {
ExecPath(ffmpegPath)
}
if mpvErr == nil {
ExecPath(mpvPath)
}
} else {
// we can restrict our permissions
if err := unix.PledgePromises("stdio rpath cpath wpath flock inet unveil dns"); err != nil {
log.Fatalf("failed to pledge: %v", err)
}
}
// needed to enable certificate validation
ReadOnlyPath("/etc/ssl/cert.pem")
}

func ExecPath(path string) {
if err := unix.Unveil(path, "rx"); err != nil {
log.Fatalf("failed to unveil exec for %s: %v", path, err)
}
}

func ReadOnlyPath(path string) {
if err := unix.Unveil(path, "r"); err != nil {
log.Fatalf("failed to unveil read for %s: %v", path, err)
}
}

func ReadWritePath(path string) {
if err := unix.Unveil(path, "rw"); err != nil {
log.Fatalf("failed to unveil read/write for %s: %v", path, err)
}
}

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

func AllPathsAdded() {
if err := unix.UnveilBlock(); err != nil {
log.Fatalf("failed to finalize unveil: %v", err)
}
}
Loading