disabling cache; it's not really necessary.

This commit is contained in:
brent saner
2024-12-20 01:29:56 -05:00
parent c0af14d890
commit 3b4d712722
24 changed files with 941 additions and 112 deletions

View File

@@ -1,3 +1,10 @@
# This file is heavily commented explaining various configuration options.
# The other configuration file examples are uncommented, but their field names
# should be easily visually mapped to the ones in here.
# All example configuration files evaluate to the same configuration.
# The test_uncommented.toml file is the exact same is this but without
# empty newlines and comments.
# DefaultUsername specifies the default username to use for
# authenticating to tunnelbroker.net.
# It is optional, as the username can be specified for each Tunnel,
@@ -50,16 +57,17 @@ CacheDbPath = '/var/cache/gobroke.db'
# and the parent directory (see below).
[CacheDbPerms.File]
# The User is optional.
# If unspecified, the default behavir mentioned above is performed.
# If specified as an empty string, the runtime EUID is enforced.
# Otherwise it may be a username or a UID (checked in that order).
# If specified as '-1', the owner will not be modified/enforced.
# If specified as an empty string (the default), the runtime EUID is enforced.
# Otherwise, it may be a username or a UID (checked in that order).
# (For new files/directories, the OS default behavior is used.)
User = ""
# Group is also optional, and follows the same logic except
# Group is also optional, and follows the same exact logic as User except
# for EGID/groupnames/GIDs.
Group = ""
# Mode is optional also.
# It *must* be equal to the octal mode bits (e.g. it must be an
# unsigned integer), but may be represented in multiple ways.
# unsigned integer 0-4095), but may be represented in multiple ways.
# e.g.:
# Mode = 0o0600
# Mode = 0o600
@@ -75,12 +83,15 @@ CacheDbPath = '/var/cache/gobroke.db'
# you can use the calculator here:
# https://rubendougall.co.uk/projects/permissions-calculator/
# (source: https://github.com/Ruben9922/permissions-calculator )
# (Supports "special" bits)
# (Supports/includes "special" bits)
# or here:
# https://wintelguy.com/permissions-calc.pl
# (beware of ads)
# (provides an explanation of the bits)
# Or see https://en.wikipedia.org/wiki/Chmod
# Note that this does, technically, work on Windows but only read vs. read-write
# for the User is used (https://pkg.go.dev/os?GOOS=windows#Chmod).
# If not specified, the default is 0o0600 for files and 0o0700 for directories.
Mode = 0o0600
# Dir permissions specifiy permissions/ownership of the parent directory of the cache DB.
# The same rules, logic, behavior, etc. as in CacheDbPerms.File apply here.

View File

@@ -0,0 +1,57 @@
DefaultUsername = "default_user"
SingleTunnel = true
CacheDbPath = '/var/cache/gobroke.db'
[CacheDbPerms]
[CacheDbPerms.File]
User = ""
Group = ""
Mode = 0o0600
[CacheDbPerms.Dir]
User = ""
Group = ""
Mode = 0o0700
[[Tunnel]]
TunnelID = 123
ExplicitClientIP = '203.0.113.1'
MTU = 1450
Username = "specific_user"
UpdateKey = "abcdef"
[[Tunnel.ConfigTemplate]]
Template = "/etc/gobroke/tpl/dnsmasq/ra_dhcpv6.conf.tpl"
Destination = "/etc/dnsmasq.d/ra_dhcpv6.conf"
[[Tunnel.ConfigTemplate.Permissions]]
[[Tunnel.ConfigTemplate.Permissions.File]]
User = ""
Group = ""
Mode = 0o0600
[[Tunnel.ConfigTemplate.Permissions.Dir]]
User = ""
Group = ""
Mode = 0o0700
[[Tunnel.ConfigTemplate.Command]]
ProgramPath = '/usr/local/bin/somecmd'
Args = [
'-f', 'foo',
]
IsolatedEnv = false
EnvVars = [
'SOMEENV=SOMEVAL',
]
OnChange = true
IsTemplate = false
[[Tunnel.ConfigTemplate]]
Template = "/etc/gobroke/tpl/stat.tpl"
Destination = "/tmp/gobroke.dump"
[[Tunnel.Command]]
ProgramPath = 'systemctl'
Args = [
'restart',
'someservice',
]
OnChange = true
[[Tunnel]]
TunnelID = 456
Username = "specific_user"
UpdateKey = "defghi"
[[Command]]
ProgramPath = "/usr/local/bin/alltunpsrogram"

View File

@@ -9,6 +9,7 @@ import (
"r00t2.io/sysutils/paths"
)
// NewConfig returns a conf.Config from filepath path.
func NewConfig(path string) (cfg *Config, err error) {
var b []byte
@@ -17,11 +18,16 @@ func NewConfig(path string) (cfg *Config, err error) {
return
}
cfg, err = NewConfigFromBytes(b)
if cfg, err = NewConfigFromBytes(b); err != nil {
return
}
cfg.confPath = new(string)
*cfg.confPath = path
return
}
// NewConfigFromBytes returns a conf.Config from bytes b. b may be a JSON, TOML, XML, or YAML representation.
func NewConfigFromBytes(b []byte) (cfg *Config, err error) {
if err = json.Unmarshal(b, &cfg); err != nil {
@@ -43,6 +49,12 @@ func NewConfigFromBytes(b []byte) (cfg *Config, err error) {
if err = paths.RealPath(&cfg.CacheDB); err != nil {
return
}
if cfg.CacheDbPerms == nil {
cfg.CacheDbPerms = new(Perms)
}
if err = cfg.CacheDbPerms.SetMissing(); err != nil {
return
}
}
if err = validate.Struct(cfg); err != nil {
@@ -67,6 +79,11 @@ func NewConfigFromBytes(b []byte) (cfg *Config, err error) {
if err = paths.RealPath(&tpl.Dest); err != nil {
return
}
if tpl.Perms != nil {
if err = tpl.Perms.SetMissing(); err != nil {
return
}
}
}
}
}

106
conf/funcs_perms.go Normal file
View File

@@ -0,0 +1,106 @@
package conf
import (
`io/fs`
`os`
`os/user`
`strconv`
`r00t2.io/sysutils/paths`
)
// Chmod enforces perms for a file or directory.
func (p *Perms) Chmod(path string, isNew bool) (err error) {
var fi fs.FileInfo
if err = paths.RealPath(&path); err != nil {
return
}
if fi, err = os.Stat(path); err != nil {
return
}
// If we add additional spec types (e.g. sockets, etc.), make this a switch.
if fi.IsDir() {
if p.ParentDir != nil {
if err = p.ParentDir.chmod(path, isNew); err != nil {
return
}
}
} else {
if p.File != nil {
if err = p.File.chmod(path, isNew); err != nil {
return
}
}
}
return
}
// Chown enforces owner/group for a file or directory.
func (p *Perms) Chown(path string) (err error) {
var fi fs.FileInfo
if err = paths.RealPath(&path); err != nil {
return
}
// If we add additional spec types (e.g. sockets, etc.), make this a switch.
if fi.IsDir() {
if p.ParentDir != nil {
if err = p.ParentDir.chown(path); err != nil {
return
}
}
} else {
if p.File != nil {
if err = p.File.chown(path); err != nil {
return
}
}
}
return
}
// SetMissing populates any missing fields.
func (p *Perms) SetMissing() (err error) {
if p.curUser == nil {
if p.curUser, err = user.Current(); err != nil {
return
}
if p.curUid, err = strconv.Atoi(p.curUser.Uid); err != nil {
return
}
}
if p.curGroup == nil {
if p.curGroup, err = user.LookupGroupId(p.curGroup.Gid); err != nil {
return
}
if p.curGid, err = strconv.Atoi(p.curGroup.Gid); err != nil {
return
}
}
if p.File == nil {
p.File = new(PermSpec)
}
p.File.parent = p
if p.ParentDir == nil {
p.ParentDir = new(PermSpec)
}
p.ParentDir.parent = p
if err = p.File.setMissing(false); err != nil {
return
}
if err = p.ParentDir.setMissing(true); err != nil {
return
}
return
}

123
conf/funcs_permspec.go Normal file
View File

@@ -0,0 +1,123 @@
package conf
import (
`errors`
`io/fs`
`os`
`os/user`
`strconv`
)
// chmod applies the PermSpec.Mode to path.
func (p *PermSpec) chmod(path string, isNew bool) (err error) {
if p.Mode == nil || (!isNew && p.explicitMode == false) {
return
}
if err = os.Chmod(path, *p.Mode); err != nil {
return
}
return
}
// chown applies the Permspec.User and PermSpec.Group to path.
func (p *PermSpec) chown(path string) (err error) {
/*
ORIGINALLY, I thought I'd have to fetch the original UID/GID from fs.FileInfo.Sys().
Linux uses https://pkg.go.dev/syscall?GOOS=linux#Stat_t
macOS uses a https://pkg.go.dev/syscall?GOOS=darwin#Stat_t
Windows uses a https://pkg.go.dev/syscall?GOOS=windows#Win32FileAttributeData which is completely useless.
(And AIX, Plan9, JS don't have a Stat_t.)
But per os.Chown, a -1 means "do not change", which is what we want.
*/
if (p.realUid == -1) || (p.realGid == -1) {
// This evaluates as a no-op.
return
}
if err = os.Chown(path, p.realUid, p.realGid); err != nil {
return
}
return
}
// setMissing populates missing information from a PermSpec. It should only be invoked by the parent Perms.SetMissing.
func (p *PermSpec) setMissing(isDir bool) (err error) {
var tmpUser *user.User
var tmpGroup *user.Group
var unameUnknown user.UnknownUserError
// var uidUnknown user.UnknownUserIdError
var gnameUnknown user.UnknownGroupError
// var gidUnknown user.UnknownGroupIdError
if p.idsSet {
return
}
// MODE
if p.Mode == nil {
p.Mode = new(fs.FileMode)
if isDir {
*p.Mode = fs.FileMode(0o0700)
} else {
*p.Mode = fs.FileMode(0o0600)
}
} else {
p.explicitMode = true
}
// OWNER/GROUP
// If nil, no change from current on-disk.
switch p.User {
case "":
p.realUid = p.parent.curUid
case "-1":
p.realUid = -1
default:
// Lookup, try username first then uid.
if tmpUser, err = user.Lookup(p.User); err != nil {
if errors.As(err, &unameUnknown) {
err = nil
if tmpUser, err = user.LookupId(p.User); err != nil {
return
}
} else {
return
}
}
if p.realUid, err = strconv.Atoi(tmpUser.Uid); err != nil {
return
}
}
switch p.Group {
case "":
p.realGid = p.parent.curGid
case "-1":
p.realGid = -1
default:
// Lookup, try groupname first then gid.
if tmpGroup, err = user.LookupGroup(p.Group); err != nil {
if errors.As(err, &gnameUnknown) {
err = nil
if tmpGroup, err = user.LookupGroupId(p.Group); err != nil {
return
}
} else {
return
}
}
if p.realGid, err = strconv.Atoi(tmpGroup.Gid); err != nil {
return
}
}
p.idsSet = true
return
}

View File

@@ -2,8 +2,9 @@ package conf
import (
"encoding/xml"
`io/fs`
`net`
`os`
`os/user`
`r00t2.io/gobroke/tplCmd`
)
@@ -27,12 +28,13 @@ type Config struct {
// CacheDbPerms specifies the optional permissions for the file and parent directory for CacheDB; only used if persistent cache.
CacheDbPerms *Perms `json:"cache_perms,omitempty" toml:"CacheDbPerms,omitempty" xml:"cachePerms,omitempty" yaml:"Cache Database Permissions,omitempty"`
// Tunnels contains one or more tunnel configurations.
Tunnels []*Tunnel `json:"tunnels" toml:"Tunnel" xml:"tunnels>tunnel" yaml:"Tunnels" validate:"required"`
Tunnels []*Tunnel `json:"tunnels" toml:"Tunnel" xml:"tunnels>tunnel" yaml:"Tunnels" validate:"required,dive,required"`
/*
Cmds are executed, in order, *after* all Tunnel configurations have been run.
Unlike in Tunnel and ConfigTemplate, no templating on these commands is performed.
*/
Cmds []tplCmd.Cmd `json:"cmds,omitempty" toml:"Command,omitempty" xml:"commands>cmd,omitempty" yaml:"Commands,omitempty"`
Cmds []tplCmd.Cmd `json:"cmds,omitempty" toml:"Command,omitempty" xml:"commands>cmd,omitempty" yaml:"Commands,omitempty" validate:"omitempty,dive"`
confPath *string
}
// Tunnel represents a single tunnel configuration from tunnelbroker.net.
@@ -49,7 +51,7 @@ type Tunnel struct {
ExplicitAddr, if provided, will be used as the tunnelbroker.FetchedTunnel.CurrentIPv4.
If not provided, this will be fetched dynamically from an external source.
*/
ExplicitAddr *net.IP `json:"addr,omitempty" toml:"ExplicitClientIP,omitempty" xml:"addr,attr,omitempty" yaml:"Explicit Client IP Address,omitempty"`
ExplicitAddr *net.IP `json:"addr,omitempty" toml:"ExplicitClientIP,omitempty" xml:"addr,attr,omitempty" yaml:"Explicit Client IP Address,omitempty" validate:"omitempty,ipv4"`
/*
MTU should be specified if you have defined a custom one (under the "Advanced" tab for this tunnel at tunnlebroker.net).
If you did not change this, the default is 1480 (the maximum allowed), and the default value of this struct field
@@ -69,13 +71,13 @@ type Tunnel struct {
*/
UpdateKey string `json:"update_key" toml:"UpdateKey" xml:"key,attr" yaml:"Update Key" validate:"required"`
// TemplateConfgs is optional. It holds templates that will be executed in order given. See ConfigTemplate.
TemplateConfigs []ConfigTemplate `json:"cfg_tpls" toml:"ConfigTemplate" xml:"config>tpl" yaml:"Configuration File Templates"`
TemplateConfigs []ConfigTemplate `json:"cfg_tpls" toml:"ConfigTemplate" xml:"config>tpl" yaml:"Configuration File Templates" validate:"omitempty,dive"`
/*
Cmds are executed, in order, *after* all tunnel updates/fetching and the templating has completed (if any specified).
Each command will also have tunnelbroker.FetchedTunnel templated to it like TemplateConfigs/ConfigTemplate.Commands,
so they may be templated as necessary.
*/
Cmds []tplCmd.TemplateCmd `json:"cmds,omitempty" toml:"Command,omitempty" xml:"commands>cmd,omitempty" yaml:"Commands,omitempty"`
Cmds []tplCmd.TemplateCmd `json:"cmds,omitempty" toml:"Command,omitempty" xml:"commands>cmd,omitempty" yaml:"Commands,omitempty" validate:"omitempty,dive"`
// cfg is the parent Config.
cfg *Config
}
@@ -101,7 +103,7 @@ type ConfigTemplate struct {
// Perms allows specifying permissions/ownerships, if the curent user has the capability to do so.
Perms *Perms `json:"perms,omitempty" toml:"Permissions,omitempty" xml:"perms,omitempty" yaml:"Permissions and Ownership,omitempty"`
// Commands specifiies commands to run after this ConfigTemplate run.
Commands []tplCmd.TemplateCmd `json:"cmds,omitempty" toml:"Command,omitempty" xml:"cmds>cmd,omitempty" yaml:"Commands,omitempty"`
Commands []tplCmd.TemplateCmd `json:"cmds,omitempty" toml:"Command,omitempty" xml:"cmds>cmd,omitempty" yaml:"Commands,omitempty" validate:"omitempty,dive"`
}
type Perms struct {
@@ -109,6 +111,10 @@ type Perms struct {
File *PermSpec `json:"file,omitempty" toml:"File,omitempty" xml:"file,omitempty" yaml:"File,omitempty"`
// ParentDir specifies the desired permissions/ownership of the parent ("dirname") of File.
ParentDir *PermSpec `json:"dir,omitempty" toml:"Dir,omitempty" xml:"dir,omitempty" yaml:"Directory,omitempty"`
curUser *user.User
curGroup *user.Group
curUid int
curGid int
}
type PermSpec struct {
@@ -117,13 +123,18 @@ type PermSpec struct {
If specified as an empty string, the current/runtime UID will be used.
If unspecified, UID will not be enforced.
*/
User *string `json:"user,omitempty" toml:"User,omitempty" xml:"user,attr,omitempty" yaml:"User,omitempty"`
User string `json:"user,omitempty" toml:"User,omitempty" xml:"user,attr,omitempty" yaml:"User,omitempty"`
/*
Group is the groupname or GID (tried in that order) to chown.
If specified as an empty string, the current/runtime GID will be used.
If unspecified, GID will not be enforced.
*/
Group *string `json:"group,omitempty" toml:"Group,omitempty" xml:"group,attr,omitempty" yaml:"Group,omitempty"`
Group string `json:"group,omitempty" toml:"Group,omitempty" xml:"group,attr,omitempty" yaml:"Group,omitempty"`
// Mode is the permission mode bitset. If unspecified, mode will not be enforced.
Mode *os.FileMode `json:"mode,omitempty" toml:"Mode,omitempty" xml:"mode,attr,omitempty" yaml:"Mode,omitempty"`
Mode *fs.FileMode `json:"mode,omitempty" toml:"Mode,omitempty" xml:"mode,attr,omitempty" yaml:"Mode,omitempty" validate:"omitempty,ge=0,le=4095"`
explicitMode bool
realUid int
realGid int
idsSet bool
parent *Perms
}