initial commit

This commit is contained in:
brent saner
2024-12-17 17:39:10 -05:00
commit 010643757e
29 changed files with 1644 additions and 0 deletions

82
conf/_testdata/test.json Normal file
View File

@@ -0,0 +1,82 @@
{
"default_username": "default_user",
"1tun": true,
"cache_db": "/var/cache/gobroke.db",
"cache_perms": {
"file": {
"user": "",
"group": "",
"mode": 384
},
"dir": {
"user": "",
"group": "",
"mode": 448
}
},
"tunnels": [
{
"tun_id": 123,
"addr": "203.0.113.1",
"mtu": 1450,
"username": "specific_user",
"update_key": "abcdef",
"cfg_tpls": [
{
"tpl": "/etc/gobroke/tpl/dnsmasq/ra_dhcpv6.conf.tpl",
"dest": "/etc/dnsmasq.d/ra_dhcpv6.conf",
"perms": {
"file": {
"user": "",
"group": "",
"mode": 384
},
"dir": {
"user": "",
"group": "",
"mode": 448
}
},
"cmds": [
{
"bin": "/usr/local/bin/somecmd",
"args": [
"-f", "foo"
],
"isol8_env": false,
"env": [
"SOMEENV=SOMEVAL"
],
"on_change": true,
"is_tpl": false
}
]
},
{
"tpl": "/etc/gobroke/tpl/stat.tpl",
"dest": "/tmp/gobroke.dump"
}
],
"cmds": [
{
"bin": "systemctl",
"args": [
"restart",
"someservice"
],
"on_change": true
}
]
},
{
"tun_id": 456,
"username": "specific_user",
"update_key": "defghi"
}
],
"cmds": [
{
"bin": "/usr/local/bin/alltunsprogram"
}
]
}

289
conf/_testdata/test.toml Normal file
View File

@@ -0,0 +1,289 @@
# DefaultUsername specifies the default username to use for
# authenticating to tunnelbroker.net.
# It is optional, as the username can be specified for each Tunnel,
# but at least one or the other *must* be provided.
# This makes it easier if you have multiple tunnels under the same account
# (as possible in higher levels of HE IPv6 certification).
# If a username is specified in Tunnel.Username, it will be used.
# If not (and, of course, DefaultUsername is specified), then
# DefaultUsername will be used for that Tunnel.
DefaultUsername = "default_user"
# If SingleTunnel is true, each Tunnel below will be run in order instead of
# concurrently.
# If there is any concern about race conditions (e.g. the same service being
# restarted by multiple tunnels, etc.), then it is HIGHLY RECOMMENDED
# you set this to true.
SingleTunnel = true
# CacheDbPath is entirely optional.
# If not provided, results will be cached in RAM (and thus lost on reboot
# or program termination/restart).
# (This can be explicitly specified by using the value ':memory:'.)
# If provided, it should be a path to a file to use as a SQLite3 database
# that holds cached information.
# The information that is cached contains only:
# * each Tunnel.TunnelID
# * the associated tunnelbroker.FetchedTunnel
# * a CRC32 of all configuration (as defined in this file) for that Tunnel
# The UpdateKey and other configuration defined here (aside from
# Tunnel.TunnelID, and Tunnel.ExplicitClientIP if specified) are
# NOT stored.
# Any tunnel present in a persistent cache DB but *not* defined in the
# running GoBroke config will be removed.
# Note that the cache DB primary key is based on the Tunnel.TunnelID,
# as one cannot define multiple client endpoints for the same tunnel.
CacheDbPath = '/var/cache/gobroke.db'
# CacheDbPerms specify the permissions for CacheDbPath.
# This directive is completely optional, and is
# ignored if CacheDbPath is ":memory:" (or unspecified).
# If not specified (and CacheDbPath is persistent),
# then the runtime user's umask and effective UID/GID
# is used if creating a new database file.
# If the file exists and permissions are defined, they will
# be enforced.
# If the file exists but no permissions are defined, they
# will be left as-is.
[CacheDbPerms]
# Permissions are/may be defined for both the file being written
# 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).
User = ""
# Group is also optional, and follows the same logic 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.
# e.g.:
# Mode = 0o0600
# Mode = 0o600
# Mode = 0x0180
# Mode = 0x180
# Mode = 0b110000000
# Mode = 384
# All evaluate to the exact same value in TOML:
# https://toml.io/en/v1.0.0#integer
# For consistency with `chmod(1)`, it is recommended to use the
# octal representation (0o0600 or 0o600 above).
# If you need help determining what number you should actually use,
# you can use the calculator here:
# https://rubendougall.co.uk/projects/permissions-calculator/
# (source: https://github.com/Ruben9922/permissions-calculator )
# (Supports "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
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.
[CacheDbPerms.Dir]
User = ""
Group = ""
Mode = 0o0700
#############
## Tunnels ##
#############
# Each Tunnel represents a single tunnelbroker.net tunnel configuration.
# Note that each Tunnel is run concurrently. If this is undesired due to
# potential race conditions, set the root-level directive SingleTunnel
# to true.
[[Tunnel]]
# The TunnelID can be found by logging into https://tunnelbroker.net/ and,
# at the "Main Page" that loads when logging in, clicking on the desired
# tunnel name.
# The tunnel ID is then displayed in both the URL bar:
# https://tunnelbroker.net/tunnel_detail.php?tid=<TunnelID>
# And as the first line on the first tab ("IPv6 Tunnel" tab),
# labeled "Tunnel ID".
TunnelID = 123
# If you wish to use a different or explicit "Client IPv4 address",
# this can be specified via ExplicitClientIP.
# If it is empty or is not specified, the public IP of this host will be determined
# via an external service.
# This *must* be an IPv4 address (if specified).
ExplicitClientIP = '203.0.113.1'
# If you have specified a custom MTU under the "Advanced" tab for this tunnel,
# you can set this value here.
# If you have not set a custom one, leave this option unspecified;
# the default (and maximum allowed), 1480 MTU, will be used in that case.
MTU = 1450
# The Username field is optional IF DefaultUsername was specified.
# This also allows you to specify tunnels from different accounts
# by providing a tunnel-specific username.
Username = "specific_user"
# The UpdateKey can be found under the "Advanced" tab on your tunnelbroker.net
# tunnel's page, labeled "Update Key".
# Your real token is likely to be a bit longer and more random.
# This token is used to not only update the client-side tunnel IP but also to
# query the HE Tunnelbroker "API" (it's really just a single endpoint)
# to get the tunnel configuration.
UpdateKey = "abcdef"
######################
## Config Templates ##
######################
# Each ConfigTemplate consists of a path to a template file and a destination
# file at the bere minimum. In addition, Commands may be provided.
# Any paths leading up to Destination that don't exist will (attempt to be)
# created.
# The template is always rendered in memory, but the destination is only written
# if:
# * The Destination doesn't exist
# * The Destination differs from the buffered rendering of the template
# Commands are optional, and are a list of commands to be run.
# Their running may be restricted to only if the tunnel information/IP
# information has changed, always run, or the inverse of all conditions.
[[Tunnel.ConfigTemplate]]
# Template points to where the template file can be found.
# It must be in a Golang text/template syntax/format; see:
# https://pkg.go.dev/text/template
# Refer to the library's definition of the tunnelbroker.FetchedTunnel struct;
# this is the object that is passed to the template.
Template = "/etc/gobroke/tpl/dnsmasq/ra_dhcpv6.conf.tpl"
# Destination is the file to write to.
# It will only be written to if:
# * The path does not exist
# * The path exists but is different from the in-memory rendered buffer
# An attempt will be made to create any leading components that are not
# present.
# It is recommended to enforce permissions/ownership of these via the
# Commands.
Destination = "/etc/dnsmasq.d/ra_dhcpv6.conf"
#################################
## Config Template Permissions ##
#################################
# Permissions can be defined for the Destionation file.
# They are completely optional, in which case the default umask, user,
# group, etc. for the runtime user will be used, and permissions/ownership
# will not be enforced for existing Destination files.
# It follows the same syntax, logic, behavior, etc. as CacheDbPerms.
[[Tunnel.ConfigTemplate.Permissions]]
[[Tunnel.ConfigTemplate.Permissions.File]]
User = ""
Group = ""
Mode = 0o0600
[[Tunnel.ConfigTemplate.Permissions.Dir]]
User = ""
Group = ""
Mode = 0o0700
##############################
## Config Template Commands ##
##############################
# Commands are a collection of commands to run as part of this template
# run.
# Multiple Commands may be specified; they will be run in the order specified.
# The below Command would be equivalent to:
# SOMEENV=SOMEVAL /usr/local/bin/somecmd -f foo
# on the shell.
[[Tunnel.ConfigTemplate.Command]]
# ProgramPath should be the absolute path to the binary to run.
# It behaves as an (os/)exec.Cmd.Path (https://pkg.go.dev/os/exec#Cmd),
# It is recommended to use an absolute path.
ProgramPath = '/usr/local/bin/somecmd'
# Args are optional for a Command.
# They should conform to the rules for (os/)exec.Cmd.Args.
Args = [
'-f', 'foo',
]
# If IsolatedEnv is false (the default), the runtime environment variables
# will be applied to the command.
# If true, *only* the EnvVars, if specified, will be used for the spawned
# command (an empty environment will be used if IsolateEnv is true and
# no EnvVars are specified).
IsolatedEnv = false
# If provided, EnvVars can be used to add/replace environment variables.
# They should conform to the rules for (os/)exec.Cmd.Env.
# Whether they are added to/selectively replace or completely replace
# the current runtime environment variables depends on how IsolateEnv
# is configured.
EnvVars = [
'SOMEENV=SOMEVAL',
]
# If OnChange is true, this Command will run *only if SOMETHING CHANGED*.
# (e.g. a /48 was added to the tunnel, the client IP is different, etc.)
# If false, this Command will run *only if NOTHING CHANGED*.
# If unspecified, the default is to always run this command regardless
# of change status.
# The very first (successful) run of a Tunnel is considered a "change",
# as is writing out this template to disk as a new file.
OnChange = true
# By default, this Command will be run literally/as-is.
# However, in some cases it may be useful to dynamically template out
# commands to run.
# If IsTemplate is set to true, then this Command.ProgramPath, each
# of the Command.Args, and each of the Command.EnvVars will be
# treated as Golang text/template strings as well, and will also
# be passed a tunnelbroker.FetchedTunnel.
# Note that if IsolateEnv is false, runtime/inherited environment
# variables will *not* be templated.
# It is recommended to not enable this unless necessary as it can add
# a non-negligible amount of resource overhead/execution time.
IsTemplate = false
#######################################################################
# Multiple ConfigTemplates may be specified.
[[Tunnel.ConfigTemplate]]
Template = "/etc/gobroke/tpl/stat.tpl"
Destination = "/tmp/gobroke.dump"
#####################
## Tunnel Commands ##
#####################
# Each Tunnel also supports its *own* commands. The syntax, spcification,
# behavior, etc. is the same as the Tunnel.ConfigTemplate.Command.
# These are executed after all Tunnel.ConfigTemplate (if any) are executed.
# This is particularly useful for consolidating service restarts.
[[Tunnel.Command]]
ProgramPath = 'systemctl'
Args = [
'restart',
'someservice',
]
# OnChange in a Tunnel.Command is scoped to any updates of the tunnel
# and any changes in ANY of the Tunnel.ConfigTemplate specified
# for this Tunnel (if true and ConfigTemplate were specified).
OnChange = true
###############################################################################
# Multiple tunnel configurations are supported as well.
[[Tunnel]]
TunnelID = 456
Username = "specific_user"
UpdateKey = "defghi"
######################
## General Commands ##
######################
# Command items may be specified at the root level as well.
# The syntax is like all other Commands items, with two exceptions:
# * There is no templating performed...
# * As such, there is no IsTemplate directive for these.
# A root-level Command is run after all tunnels complete.
# The OnChange directive is true if any Tunnels result in any changes.
[[Command]]
ProgramPath = "/usr/local/bin/alltunpsrogram"

68
conf/_testdata/test.xml Normal file
View File

@@ -0,0 +1,68 @@
<!--
See the example TOML for detailed comments and explanations.
-->
<config defaultUser="default_user"
oneTun="true"
cacheDb="/var/cache/gobroke.db">
<cachePerms>
<file user=""
group=""
mode="384"/>
<dir user=""
group=""
mode="448"/>
</cachePerms>
<tunnels>
<tunnel id="123"
addr="203.0.113.1"
mtu="1450"
username="specific_user"
key="abcdef">
<config>
<tpl tpl="/etc/gobroke/tpl/dnsmasq/ra_dhcpv6.conf.tpl"
dest="/etc/dnsmasq.d/ra_dhcpv6.conf">
<perms>
<file user=""
group=""
mode="384"/>
<dir user=""
group=""
mode="448"/>
</perms>
<cmds>
<cmd bin="/usr/local/bin/somecmd"
isol8Env="false"
onChange="true"
isTpl="false">
<args>
<arg>-f</arg>
<arg>foo</arg>
</args>
<envs>
<env>SOMEENV=SOMEVAL</env>
</envs>
</cmd>
</cmds>
</tpl>
<tpl tpl="/etc/gobroke/tpl/stat.tpl"
dest="/tmp/gobroke.dump"/>
</config>
<commands>
<cmd bin="systemctl"
onChange="true">
<args>
<arg>restart</arg>
<arg>someservice</arg>
</args>
</cmd>
</commands>
</tunnel>
<tunnel id="456"
mtu="1480"
username="specific_user"
key="defghi"/>
</tunnels>
<commands>
<cmd bin="/usr/local/bin/alltunsprogram" isol8Env="false"/>
</commands>
</config>

59
conf/_testdata/test.yml Normal file
View File

@@ -0,0 +1,59 @@
# See the example TOML for detailed comments and explanations.
Default Username: default_user
NoGoTunnel: true
Cache Database Path: /var/cache/gobroke.db
Cache Database Permissions:
File:
User: ''
Group: ''
Mode: 384
Directory:
User: ''
Group: ''
Mode: 448
Tunnels:
- Tunnel ID: 123
Explicit Client IP Address: 203.0.113.1
MTU: 1450
Username: specific_user
Update Key: abcdef
Configuration File Templates:
- Template File Path: /etc/gobroke/tpl/dnsmasq/ra_dhcpv6.conf.tpl
Destination File Path: /etc/dnsmasq.d/ra_dhcpv6.conf
Permissions and Ownership:
File:
User: ''
Group: ''
Mode: 384
Directory:
User: ''
Group: ''
Mode: 448
Commands:
- Program Path: /usr/local/bin/somecmd
Arguments:
- '-f'
- 'foo'
Isolated Environment: false
Environment Variables:
- SOMEENV=SOMEVAL
On Change: true
Is Template: false
- Template File Path: /etc/gobroke/tpl/stat.tpl
Destination File Path: /tmp/gobroke.dump
Commands:
- Program Path: systemctl
Arguments:
- restart
- someservice
On Change: true
- Tunnel ID: 456
Username: specific_user
Update Key: defghi
Commands:
- Program Path: /usr/local/bin/alltunsprogram

9
conf/consts.go Normal file
View File

@@ -0,0 +1,9 @@
package conf
import (
"github.com/go-playground/validator/v10"
)
var (
validate *validator.Validate = validator.New(validator.WithRequiredStructEnabled())
)

10
conf/errs.go Normal file
View File

@@ -0,0 +1,10 @@
package conf
import (
`errors`
)
var (
ErrMissingUser error = errors.New("at least one tunnel is missing a username and no default username was provided")
ErrUnkownSyntax error = errors.New("unknown/unsupported configuration syntax")
)

75
conf/funcs.go Normal file
View File

@@ -0,0 +1,75 @@
package conf
import (
`encoding/json`
"github.com/BurntSushi/toml"
"github.com/creasty/defaults"
"github.com/goccy/go-yaml"
"r00t2.io/sysutils/paths"
)
func NewConfig(path string) (cfg *Config, err error) {
var b []byte
if err = paths.RealPath(&path); err != nil {
return
}
cfg, err = NewConfigFromBytes(b)
return
}
func NewConfigFromBytes(b []byte) (cfg *Config, err error) {
if err = json.Unmarshal(b, &cfg); err != nil {
if err = yaml.Unmarshal(b, &cfg); err != nil {
if err = toml.Unmarshal(b, &cfg); err != nil {
if err = toml.Unmarshal(b, &cfg); err != nil {
err = ErrUnkownSyntax
return
}
}
}
}
if err = defaults.Set(cfg); err != nil {
return
}
if cfg.CacheDB != ":memory:" {
if err = paths.RealPath(&cfg.CacheDB); err != nil {
return
}
}
if err = validate.Struct(cfg); err != nil {
return
}
for _, t := range cfg.Tunnels {
t.cfg = cfg
if t.Username == nil {
if cfg.Username == nil {
err = ErrMissingUser
return
} else {
t.Username = cfg.Username
}
}
if t.TemplateConfigs != nil && len(t.TemplateConfigs) > 0 {
for _, tpl := range t.TemplateConfigs {
if err = paths.RealPath(&tpl.Template); err != nil {
return
}
if err = paths.RealPath(&tpl.Dest); err != nil {
return
}
}
}
}
return
}

50
conf/funcs_test.go Normal file
View File

@@ -0,0 +1,50 @@
package conf
import (
`embed`
`encoding/xml`
`fmt`
"testing"
`github.com/BurntSushi/toml`
`github.com/goccy/go-yaml`
)
var (
//go:embed "_testdata"
testData embed.FS
)
func TestConf(t *testing.T) {
var err error
var cfg *Config
var b []byte
if b, err = testData.ReadFile("_testdata/test.json"); err != nil {
t.Fatal(err)
}
if cfg, err = NewConfigFromBytes(b); err != nil {
t.Fatal(err)
}
/*
spew.Dump(cfg)
return
*/
if b, err = toml.Marshal(cfg); err != nil {
t.Fatal(err)
}
fmt.Println(string(b))
if b, err = xml.MarshalIndent(cfg, "", " "); err != nil {
t.Fatal(err)
}
fmt.Println(string(b))
if b, err = yaml.Marshal(cfg); err != nil {
t.Fatal(err)
}
fmt.Println(string(b))
}

1
conf/funcs_tunnel.go Normal file
View File

@@ -0,0 +1 @@
package conf

129
conf/types.go Normal file
View File

@@ -0,0 +1,129 @@
package conf
import (
"encoding/xml"
`net`
`os`
`r00t2.io/gobroke/tplCmd`
)
// Config represents a configuration file.
type Config struct {
XMLName xml.Name `json:"-" toml:"-" xml:"config" yaml:"-" `
/*
Username, if provided, will be the default username used for any Tunnel that does not specify one (via Tunnel.Username).
This should be the username used when authenticating to tunnelbroker.net.
It is optional, as the username can be specified/overridden for each Tunnel, but at least one or the other *must* be provided.
This makes it easier if you have multiple tunnels under the same account.
If a username is specified in Tunnel.Username, it will be used.
If not (and, of course, Config.Username is specified), then Config.Username will be used for that Tunnel.
*/
Username *string `json:"default_username,omitempty" toml:"DefaultUsername,omitempty" xml:"defaultUser,attr,omitempty" yaml:"Default Username,omitempty"`
// SingleTunnel, if true, will suppress goroutine-management of tunnels and instead execute them sequentially instead.
SingleTunnel bool `json:"1tun,omitempty" toml:"SingleTunnel,omitempty" xml:"oneTun,attr,omitempty" yaml:"NoGoTunnel,omitempty"`
// CacheDB, if specified, is a path to a SQLite3 DB on-disk to make cached information persistent across reboots.
CacheDB string `json:"cache_db,omitempty" toml:"CacheDbPath,omitempty" xml:"cacheDb,attr,omitempty" yaml:"Cache Database Path,omitempty" default:":memory:" validate:"omitempty,filepath|eq=:memory:"`
// 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"`
/*
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"`
}
// Tunnel represents a single tunnel configuration from tunnelbroker.net.
type Tunnel struct {
XMLName xml.Name `json:"-" toml:"-" xml:"tunnel" yaml:"-"`
/*
TunnelID can be found by logging into https://tunnelbroker.net/ and, at the "Main Page" that loads
when logging in, clicking on the desired tunnel name. The tunnel ID is then displayed in both the URL bar:
https://tunnelbroker.net/tunnel_detail.php?tid=<TunnelID>
And as the first line on the first tab ("IPv6 Tunnel" tab), labeled "Tunnel ID".
*/
TunnelID uint `json:"tun_id" toml:"TunnelID" xml:"id,attr" yaml:"Tunnel ID" validate:"required,ge=1"`
/*
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"`
/*
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
on configuration parsing will reflect this.
*/
MTU uint `json:"mtu,omitempty" toml:"MTU,omitempty" xml:"mtu,attr,omitempty" yaml:"MTU,omitempty" default:"1480" validate:"required,gt=0,le=1480"`
/*
Username field is optional IF DefaultUsername was specified.
This also allows you to specify tunnels from different accounts
by providing a tunnel-specific username.
*/
Username *string `json:"username,omitempty" toml:"Username,omitempty" xml:"username,attr,omitempty" yaml:"Username,omitempty"`
/*
UpdateKey can be found under the "Advanced" tab on your tunnelbroker.net tunnel's page, labeled "Update Key".
This token is used to not only update the client-side tunnel IP but also to query the HE Tunnelbroker "API"
(it's really just a single endpoint) to get the tunnel information necessary for local configuration.
*/
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"`
/*
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"`
// cfg is the parent Config.
cfg *Config
}
/*
ConfigTemplate allows the templating of configuration files, etc. from the tunnel information.
Templates are executed *after* the IP update (if an update was necessary), but are always *run*.
ConfigTemplate.Dest will only be written to if:
* The file does not exist (yet), or
* The templated content differs from the file on disk
*/
type ConfigTemplate struct {
XMLName xml.Name `json:"-" toml:"-" xml:"tpl" yaml:"-"`
/*
Template is the path to the template file on disk.
It must follow the syntax, rules, etc. of a Golang (text/)template.Template (https://pkg.go.dev/text/template#Template).
The struct passed to it is a tunnelbroker.FetchedTunnel.
*/
Template string `json:"tpl" toml:"Template" xml:"tpl,attr" yaml:"Template File Path" validate:"required,filepath"`
// Dest contains the filepath that the Template should be written out to.
Dest string `json:"dest" toml:"Destination" xml:"dest,attr" yaml:"Destination File Path" validate:"required,filepath"`
// 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"`
}
type Perms struct {
// File specifies the desired permissions/ownership of the target file.
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"`
}
type PermSpec struct {
/*
User is the username or UID (tried in that order) to chown.
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"`
/*
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"`
// 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"`
}