Scert/backend/internal/nginxactivation/policy_linux_test.go

166 lines
4.7 KiB
Go

package nginxactivation
import (
"encoding/json"
"os"
"path/filepath"
"strings"
"testing"
"golang.org/x/sys/unix"
)
func newPolicyFixture(t *testing.T) (Policy, string) {
t.Helper()
directory := t.TempDir()
if err := os.Chmod(directory, 0o700); err != nil {
t.Fatal(err)
}
uid := uint32(os.Geteuid()) + 1
policy := Policy{
Version: 1, AllowedUID: &uid,
SocketPath: filepath.Join(directory, "a.sock"), Directory: directory,
Hostname: testHostname, ProxyAddress: "127.0.0.1:443", UpstreamAddress: "127.0.0.1:9004",
NginxBinary: filepath.Join(directory, "nginx"), NginxConfig: filepath.Join(directory, "nginx.conf"),
NginxPrefix: directory,
}
writeTestFile(t, policy.NginxBinary, []byte("test executable"))
if err := os.Chmod(policy.NginxBinary, 0o700); err != nil {
t.Fatal(err)
}
writeTestFile(t, policy.NginxConfig, []byte("events {}\n"))
return policy, filepath.Join(directory, "policy.json")
}
func writePolicy(t *testing.T, path string, policy Policy) {
t.Helper()
data, err := json.Marshal(policy)
if err != nil {
t.Fatal(err)
}
writeTestFile(t, path, data)
}
func TestPolicyValidation(t *testing.T) {
for _, scenario := range []string{
"valid", "version", "missing-uid", "root-uid", "socket-relative", "socket-long", "socket-parent",
"directory-mode", "hostname", "proxy-public", "upstream-public", "same-address", "binary-mode",
"binary-no-exec", "binary-setuid", "config-mode", "prefix-relative", "prefix-mode",
} {
t.Run(scenario, func(t *testing.T) {
policy, path := newPolicyFixture(t)
chmod := func(path string, mode os.FileMode) {
t.Helper()
if err := os.Chmod(path, mode); err != nil {
t.Fatal(err)
}
}
switch scenario {
case "version":
policy.Version = 2
case "missing-uid":
policy.AllowedUID = nil
case "root-uid":
*policy.AllowedUID = 0
case "socket-relative":
policy.SocketPath = "a.sock"
case "socket-long":
policy.SocketPath = "/" + strings.Repeat("a", 108)
case "socket-parent":
parent := filepath.Join(policy.Directory, "socket")
if err := os.Mkdir(parent, 0o700); err != nil {
t.Fatal(err)
}
chmod(parent, 0o777)
policy.SocketPath = filepath.Join(parent, "a.sock")
case "directory-mode":
chmod(policy.Directory, 0o755)
case "hostname":
policy.Hostname = "*.example.com"
case "proxy-public":
policy.ProxyAddress = "192.0.2.1:443"
case "upstream-public":
policy.UpstreamAddress = "0.0.0.0:9004"
case "same-address":
policy.UpstreamAddress = policy.ProxyAddress
case "binary-mode":
chmod(policy.NginxBinary, 0o777)
case "binary-no-exec":
chmod(policy.NginxBinary, 0o600)
case "binary-setuid":
chmod(policy.NginxBinary, 0o700|os.ModeSetuid)
case "config-mode":
chmod(policy.NginxConfig, 0o666)
case "prefix-relative":
policy.NginxPrefix = "."
case "prefix-mode":
policy.NginxPrefix = filepath.Join(policy.Directory, "prefix")
if err := os.Mkdir(policy.NginxPrefix, 0o700); err != nil {
t.Fatal(err)
}
chmod(policy.NginxPrefix, 0o777)
}
writePolicy(t, path, policy)
got, err := loadPolicy(path, uint32(os.Geteuid()))
if scenario == "valid" {
if err != nil || got.Hostname != policy.Hostname || got.Rotation().Directory != policy.Directory {
t.Fatal("合法策略未正确加载", err)
}
} else if err == nil {
t.Fatal("不可信策略被接受")
}
})
}
}
func TestPolicyFileSafety(t *testing.T) {
for _, scenario := range []string{"symlink", "hardlink", "fifo", "writable", "owner", "oversize", "unknown", "trailing", "null"} {
t.Run(scenario, func(t *testing.T) {
policy, path := newPolicyFixture(t)
writePolicy(t, path, policy)
owner := uint32(os.Geteuid())
switch scenario {
case "symlink":
link := path + ".link"
if err := os.Symlink(path, link); err != nil {
t.Fatal(err)
}
path = link
case "hardlink":
if err := os.Link(path, path+".link"); err != nil {
t.Fatal(err)
}
case "fifo":
path += ".fifo"
if err := unix.Mkfifo(path, 0o600); err != nil {
t.Fatal(err)
}
case "writable":
if err := os.Chmod(path, 0o666); err != nil {
t.Fatal(err)
}
case "owner":
owner++
case "oversize":
writeTestFile(t, path, []byte(strings.Repeat(" ", (16<<10)+1)))
case "unknown", "trailing":
data, err := json.Marshal(policy)
if err != nil {
t.Fatal(err)
}
if scenario == "unknown" {
data = append(data[:len(data)-1], []byte(`,"command":"id"}`)...)
} else {
data = append(data, []byte(`{}`)...)
}
writeTestFile(t, path, data)
case "null":
writeTestFile(t, path, []byte("null"))
}
if _, err := loadPolicy(path, owner); err == nil {
t.Fatal("不可信策略文件被接受")
}
})
}
}