166 lines
4.7 KiB
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("不可信策略文件被接受")
|
|
}
|
|
})
|
|
}
|
|
}
|