disabling cache; it's not really necessary.
This commit is contained in:
@@ -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.
|
||||
|
||||
57
conf/_testdata/test_uncommented.toml
Normal file
57
conf/_testdata/test_uncommented.toml
Normal 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"
|
||||
@@ -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
106
conf/funcs_perms.go
Normal 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
123
conf/funcs_permspec.go
Normal 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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user