From 03618ba72cc5ed9f02569ff0129e0b3a06d386dd Mon Sep 17 00:00:00 2001 From: Brian Picciano Date: Wed, 26 Oct 2022 22:18:16 +0200 Subject: [PATCH] Reimplement dnsmasq-entrypoint in go This allowed for deleting all script utilities and environment variable logic. --- default.nix | 16 ++--- dnsmasq/bin/dnsmasq-entrypoint | 36 ------------ dnsmasq/default.nix | 39 ------------- dnsmasq/etc/base.conf | 41 ------------- entrypoint/src/cmd/entrypoint/daemon.go | 23 ++------ entrypoint/src/cmd/entrypoint/dnsmasq_util.go | 45 ++++++++++++++ entrypoint/src/dnsmasq/dnsmasq.go | 3 + entrypoint/src/dnsmasq/tpl.go | 58 +++++++++++++++++++ entrypoint/src/env.go | 53 ----------------- entrypoint/src/garage/tpl.go | 4 +- nix/dnsmasq.nix | 25 ++++++++ nix/pkgs.nix | 13 +---- nix/rebase.nix | 18 ------ nix/yq-go.nix | 19 ------ 14 files changed, 146 insertions(+), 247 deletions(-) delete mode 100644 dnsmasq/bin/dnsmasq-entrypoint delete mode 100644 dnsmasq/default.nix delete mode 100644 dnsmasq/etc/base.conf create mode 100644 entrypoint/src/cmd/entrypoint/dnsmasq_util.go create mode 100644 entrypoint/src/dnsmasq/dnsmasq.go create mode 100644 entrypoint/src/dnsmasq/tpl.go create mode 100644 nix/dnsmasq.nix delete mode 100644 nix/rebase.nix delete mode 100644 nix/yq-go.nix diff --git a/default.nix b/default.nix index 903334f..525ff1a 100644 --- a/default.nix +++ b/default.nix @@ -57,9 +57,11 @@ in rec { entrypoint = pkgs.callPackage ./entrypoint {}; - dnsmasq = (pkgs.callPackage ./dnsmasq { + dnsmasq = (pkgs.callPackage ./nix/dnsmasq.nix { glibcStatic = pkgs.glibc.static; - }).env; + }); + + nebula = pkgs.callPackage ./nix/nebula.nix {}; garage = (pkgs.callPackage ./nix/garage.nix {}).env; @@ -69,18 +71,10 @@ in rec { name = "cryptic-net-AppDir"; paths = [ - pkgs.pkgsStatic.bash - pkgs.pkgsStatic.coreutils - pkgs.pkgsStatic.gnutar - pkgs.pkgsStatic.gzip - - # custom packages from ./pkgs.nix - pkgs.yq-go - pkgs.nebula - ./AppDir version dnsmasq + nebula garage entrypoint diff --git a/dnsmasq/bin/dnsmasq-entrypoint b/dnsmasq/bin/dnsmasq-entrypoint deleted file mode 100644 index ceac0ea..0000000 --- a/dnsmasq/bin/dnsmasq-entrypoint +++ /dev/null @@ -1,36 +0,0 @@ -# TODO implement this in go - -set -e -o pipefail -cd "$APPDIR" - -conf_path="$_RUNTIME_DIR_PATH"/dnsmasq.conf - -cat etc/dnsmasq/base.conf > "$conf_path" - -tmp="$(mktemp -d -t cryptic-net-dnsmasq-entrypoint-XXX)" - -( trap "rm -rf '$tmp'" EXIT - - tar xzf "$_BOOTSTRAP_PATH" -C "$tmp" ./hosts - - thisHostName=$(tar xzf "$_BOOTSTRAP_PATH" --to-stdout ./hostname) - thisHostIP=$(cat "$tmp"/hosts/"$thisHostName".yml | yq '.nebula.ip') - - domain=$(tar xzf "$_BOOTSTRAP_PATH" --to-stdout ./admin/creation-params.yml | yq '.domain') - - echo "listen-address=$thisHostIP" >> "$conf_path" - - ls -1 "$tmp"/hosts | while read hostYml; do - - hostName=$(echo "$hostYml" | cut -d. -f1) - hostIP=$(cat "$tmp"/hosts/"$hostYml" | yq '.nebula.ip') - echo "address=/${hostName}.hosts.$domain/$hostIP" >> "$conf_path" - - done -) - -cat "$_DAEMON_YML_PATH" | \ - yq '.dns.resolvers | .[] | "server=" + .' \ - >> "$conf_path" - -exec bin/dnsmasq -d -C "$conf_path" diff --git a/dnsmasq/default.nix b/dnsmasq/default.nix deleted file mode 100644 index 7202339..0000000 --- a/dnsmasq/default.nix +++ /dev/null @@ -1,39 +0,0 @@ -{ - - stdenv, - buildEnv, - glibcStatic, - rebase, - -}: rec { - - dnsmasq = stdenv.mkDerivation rec { - pname = "dnsmasq"; - version = "2.85"; - - src = builtins.fetchurl { - url = "https://www.thekelleys.org.uk/dnsmasq/${pname}-${version}.tar.xz"; - sha256 = "sha256-rZjTgD32h+W5OAgPPSXGKP5ByHh1LQP7xhmXh/7jEvo="; - }; - - nativeBuildInputs = [ glibcStatic ]; - - makeFlags = [ - "LDFLAGS=-static" - "DESTDIR=" - "BINDIR=$(out)/bin" - "MANDIR=$(out)/man" - "LOCALEDIR=$(out)/share/locale" - ]; - }; - - env = buildEnv { - name = "cryptic-net-dnsmasq"; - paths = [ - (rebase "cryptic-net-dnsmasq-bin" ./bin "bin") - (rebase "cryptic-net-dnsmasq-etc" ./etc "etc/dnsmasq") - dnsmasq - ]; - }; - -} diff --git a/dnsmasq/etc/base.conf b/dnsmasq/etc/base.conf deleted file mode 100644 index 58501ff..0000000 --- a/dnsmasq/etc/base.conf +++ /dev/null @@ -1,41 +0,0 @@ -# Configuration file for dnsmasq. -# -# Format is one option per line, legal options are the same -# as the long options legal on the command line. See -# "/usr/sbin/dnsmasq --help" or "man 8 dnsmasq" for details. - -# Listen on this specific port instead of the standard DNS port -# (53). Setting this to zero completely disables DNS function, -# leaving only DHCP and/or TFTP. -port=53 - -# If you don't want dnsmasq to read /etc/resolv.conf or any other -# file, getting its servers from this file instead (see below), then -# uncomment this. -no-resolv - -# On systems which support it, dnsmasq binds the wildcard address, -# even when it is listening on only some interfaces. It then discards -# requests that it shouldn't reply to. This has the advantage of -# working even when interfaces come and go and change address. If you -# want dnsmasq to really bind only the interfaces it is listening on, -# uncomment this option. About the only time you may need this is when -# running another nameserver on the same machine. -bind-interfaces - -# If you don't want dnsmasq to read /etc/hosts, uncomment the -# following line. -no-hosts - -# Unset user and group so that dnsmasq doesn't drop privileges to another user. -# If this isn't done then dnsmasq fails to start up, since it fails to access -# /etc/passwd correctly, probably due to nix. -user= -group= - -# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -# -# Everything below is generated dynamically based on runtime configuration -# -# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - diff --git a/entrypoint/src/cmd/entrypoint/daemon.go b/entrypoint/src/cmd/entrypoint/daemon.go index 2d2581a..38d7e0f 100644 --- a/entrypoint/src/cmd/entrypoint/daemon.go +++ b/entrypoint/src/cmd/entrypoint/daemon.go @@ -94,16 +94,14 @@ func runDaemonPmuxOnce(env crypticnet.Env) (crypticnet.Env, error) { return crypticnet.Env{}, fmt.Errorf("generating nebula config: %w", err) } + dnsmasqPmuxProcConfig, err := dnsmasqPmuxProcConfig(env) + if err != nil { + return crypticnet.Env{}, fmt.Errorf("generating dnsmasq config: %w", err) + } + pmuxProcConfigs := []pmuxlib.ProcessConfig{ nebulaPmuxProcConfig, - { - Name: "dnsmasq", - Cmd: "bash", - Args: []string{"dnsmasq-entrypoint"}, - StartAfterFunc: func(ctx context.Context) error { - return waitForNebula(ctx, env) - }, - }, + dnsmasqPmuxProcConfig, } if len(thisDaemon.Storage.Allocations) > 0 { @@ -302,15 +300,6 @@ var subCmdDaemon = subCmd{ return fmt.Errorf("merging daemon.yml into bootstrap data: %w", err) } - // TODO once dnsmasq entrypoint is written in go and the config - // generation is inlined into this process then this Setenv won't be - // necessary. - for key, val := range env.ToMap() { - if err := os.Setenv(key, val); err != nil { - return fmt.Errorf("failed to set %q to %q: %w", key, val, err) - } - } - for { if env, err = runDaemonPmuxOnce(env); errors.Is(err, context.Canceled) { diff --git a/entrypoint/src/cmd/entrypoint/dnsmasq_util.go b/entrypoint/src/cmd/entrypoint/dnsmasq_util.go new file mode 100644 index 0000000..6616f39 --- /dev/null +++ b/entrypoint/src/cmd/entrypoint/dnsmasq_util.go @@ -0,0 +1,45 @@ +package main + +import ( + crypticnet "cryptic-net" + "cryptic-net/bootstrap" + "cryptic-net/dnsmasq" + "fmt" + "path/filepath" + "sort" + + "code.betamike.com/cryptic-io/pmux/pmuxlib" +) + +func dnsmasqPmuxProcConfig(env crypticnet.Env) (pmuxlib.ProcessConfig, error) { + + thisDaemon := env.ThisDaemon() + + confPath := filepath.Join(env.RuntimeDirPath, "dnsmasq.conf") + + hostsSlice := make([]bootstrap.Host, 0, len(env.Bootstrap.Hosts)) + for _, host := range env.Bootstrap.Hosts { + hostsSlice = append(hostsSlice, host) + } + + sort.Slice(hostsSlice, func(i, j int) bool { + return hostsSlice[i].Nebula.IP < hostsSlice[j].Nebula.IP + }) + + confData := dnsmasq.ConfData{ + Resolvers: thisDaemon.DNS.Resolvers, + Domain: env.Bootstrap.AdminCreationParams.Domain, + IP: env.Bootstrap.ThisHost().Nebula.IP, + Hosts: hostsSlice, + } + + if err := dnsmasq.WriteConfFile(confPath, confData); err != nil { + return pmuxlib.ProcessConfig{}, fmt.Errorf("writing dnsmasq.conf to %q: %w", confPath, err) + } + + return pmuxlib.ProcessConfig{ + Name: "dnsmasq", + Cmd: "dnsmasq", + Args: []string{"-d", "-C", confPath}, + }, nil +} diff --git a/entrypoint/src/dnsmasq/dnsmasq.go b/entrypoint/src/dnsmasq/dnsmasq.go new file mode 100644 index 0000000..0d5d593 --- /dev/null +++ b/entrypoint/src/dnsmasq/dnsmasq.go @@ -0,0 +1,3 @@ +// Package dnsmasq contains helper functions and types which are useful for +// setting up dnsmasq configs, processes, and deployments. +package dnsmasq diff --git a/entrypoint/src/dnsmasq/tpl.go b/entrypoint/src/dnsmasq/tpl.go new file mode 100644 index 0000000..9822d75 --- /dev/null +++ b/entrypoint/src/dnsmasq/tpl.go @@ -0,0 +1,58 @@ +package dnsmasq + +import ( + "cryptic-net/bootstrap" + "fmt" + "os" + "text/template" +) + +// ConfData describes all the data needed to populate a dnsmasq.conf file. +type ConfData struct { + Resolvers []string + Domain string + IP string + Hosts []bootstrap.Host +} + +var confTpl = template.Must(template.New("").Parse(` +port=53 + +bind-interfaces +listen-address={{ .IP }} + +no-resolv +no-hosts + +user= +group= + +{{- range $host := .Hosts }} +address=/{{ $host.Name }}.hosts.{{ .Domain }}/{{ $host.Nebula.IP }} +{{ end -}} + +{{- range .Resolvers }} +server={{ . }} +{{ end -}} +`)) + +// WriteConfFile renders a dnsmasq.conf using the given data to a new +// file at the given path. +func WriteConfFile(path string, data ConfData) error { + + file, err := os.OpenFile( + path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0640, + ) + + if err != nil { + return fmt.Errorf("creating file: %w", err) + } + + defer file.Close() + + if err := confTpl.Execute(file, data); err != nil { + return fmt.Errorf("rendering template to file: %w", err) + } + + return nil +} diff --git a/entrypoint/src/env.go b/entrypoint/src/env.go index cc8d34e..19a3b86 100644 --- a/entrypoint/src/env.go +++ b/entrypoint/src/env.go @@ -19,14 +19,6 @@ import ( "github.com/adrg/xdg" ) -// Names of various environment variables which get set by the entrypoint. -const ( - DaemonYmlPathEnvVar = "_DAEMON_YML_PATH" - BootstrapPathEnvVar = "_BOOTSTRAP_PATH" - RuntimeDirPathEnvVar = "_RUNTIME_DIR_PATH" - DataDirPathEnvVar = "_DATA_DIR_PATH" -) - // Env contains the values of environment variables, as well as other entities // which are useful across all processes. type Env struct { @@ -74,40 +66,6 @@ func NewEnv(bootstrapOptional bool) (Env, error) { return env.init(bootstrapOptional) } -// ReadEnv reads an Env from the process's environment variables, rather than -// calculating like NewEnv does. -func ReadEnv() (Env, error) { - - var err error - - readEnv := func(key string) string { - if err != nil { - return "" - } - - val := os.Getenv(key) - - if val == "" { - err = fmt.Errorf("envvar %q not set", key) - } - - return val - } - - env := Env{ - AppDirPath: getAppDirPath(), - DaemonYmlPath: readEnv(DaemonYmlPathEnvVar), - RuntimeDirPath: readEnv(RuntimeDirPathEnvVar), - DataDirPath: readEnv(DataDirPathEnvVar), - } - - if err != nil { - return Env{}, err - } - - return env.init(false) -} - // DataDirBootstrapPath returns the path to the bootstrap file within the user's // data dir. If the file does not exist there it will be found in the AppDirPath // by ReloadBootstrap. @@ -197,17 +155,6 @@ func (e Env) init(bootstrapOptional bool) (Env, error) { return e.initBootstrap(bootstrapOptional) } -// ToMap returns the Env as a map of key/value strings. If this map is set into -// a process's environment, then that process can read it back using ReadEnv. -func (e Env) ToMap() map[string]string { - return map[string]string{ - DaemonYmlPathEnvVar: e.DaemonYmlPath, - BootstrapPathEnvVar: e.BootstrapPath, - RuntimeDirPathEnvVar: e.RuntimeDirPath, - DataDirPathEnvVar: e.DataDirPath, - } -} - // ThisDaemon returns the DaemonYml (loaded from DaemonYmlPath) for the // currently running process. func (e Env) ThisDaemon() DaemonYml { diff --git a/entrypoint/src/garage/tpl.go b/entrypoint/src/garage/tpl.go index 9101230..351f03c 100644 --- a/entrypoint/src/garage/tpl.go +++ b/entrypoint/src/garage/tpl.go @@ -67,9 +67,7 @@ func WriteGarageTomlFile(path string, data GarageTomlData) error { defer file.Close() - err = RenderGarageToml(file, data) - - if err != nil { + if err := garageTomlTpl.Execute(file, data); err != nil { return fmt.Errorf("rendering template to file: %w", err) } diff --git a/nix/dnsmasq.nix b/nix/dnsmasq.nix new file mode 100644 index 0000000..debc7e9 --- /dev/null +++ b/nix/dnsmasq.nix @@ -0,0 +1,25 @@ +{ + + stdenv, + glibcStatic, + +}: stdenv.mkDerivation rec { + + pname = "dnsmasq"; + version = "2.85"; + + src = builtins.fetchurl { + url = "https://www.thekelleys.org.uk/dnsmasq/${pname}-${version}.tar.xz"; + sha256 = "sha256-rZjTgD32h+W5OAgPPSXGKP5ByHh1LQP7xhmXh/7jEvo="; + }; + + nativeBuildInputs = [ glibcStatic ]; + + makeFlags = [ + "LDFLAGS=-static" + "DESTDIR=" + "BINDIR=$(out)/bin" + "MANDIR=$(out)/man" + "LOCALEDIR=$(out)/share/locale" + ]; +} diff --git a/nix/pkgs.nix b/nix/pkgs.nix index b7ceca5..c17ea25 100644 --- a/nix/pkgs.nix +++ b/nix/pkgs.nix @@ -2,7 +2,8 @@ rec { overlays = [ - # Make both buildGoModules use static compilation by default. + # Make buildGoModules use static compilation by default, and use go 1.18 + # everywhere. (final: prev: let @@ -17,19 +18,11 @@ rec { in { go = prev.go_1_18; - buildGoModule = args: prev.buildGoModule (buildArgs // args); + buildGoModule = args: prev.buildGo118Module (buildArgs // args); buildGo118Module = args: prev.buildGo118Module (buildArgs // args); } ) - - (final: prev: { rebase = prev.callPackage ./rebase.nix {}; }) - (final: prev: { yq-go = prev.callPackage ./yq-go.nix {}; }) - - (final: prev: { nebula = prev.callPackage ./nebula.nix { - buildGoModule = prev.buildGo118Module; - }; }) - ]; version = "22-05"; diff --git a/nix/rebase.nix b/nix/rebase.nix deleted file mode 100644 index 1f557ce..0000000 --- a/nix/rebase.nix +++ /dev/null @@ -1,18 +0,0 @@ -# rebase is a helper which takes all files/dirs under oldroot, and -# creates a new derivation with those files/dirs copied under newroot -# (where newroot is a relative path to the root of the derivation). - -{ - - stdenv, - -}: name: oldroot: newroot: stdenv.mkDerivation { - - inherit name oldroot newroot; - - builder = builtins.toFile "builder.sh" '' - source $stdenv/setup - mkdir -p "$out"/"$newroot" - cp -rL "$oldroot"/* "$out"/"$newroot" - ''; -} diff --git a/nix/yq-go.nix b/nix/yq-go.nix deleted file mode 100644 index 8689bca..0000000 --- a/nix/yq-go.nix +++ /dev/null @@ -1,19 +0,0 @@ -{ - - buildGoModule, - fetchFromGitHub, - -}: buildGoModule rec { - - pname = "yq-go"; - version = "4.21.1"; - - src = fetchFromGitHub { - owner = "mikefarah"; - repo = "yq"; - rev = "v${version}"; - sha256 = "sha256-283xe7FVHYSsRl4cZD7WDzIW1gqNAFsNrWYJkthZheU="; - }; - - vendorSha256 = "sha256-F11FnDYJ59aKrdRXDPpKlhX52yQXdaN1sblSkVI2j9w="; -}