From 7557f755797fea2587de0aa043935f75a7d84553 Mon Sep 17 00:00:00 2001 From: Daylin Morgan Date: Fri, 16 Aug 2024 11:02:24 -0500 Subject: [PATCH] wip --- LICENSE | 21 ++++++++++++ flake.lock | 27 ++++++++++++++++ flake.nix | 32 +++++++++++++++++++ tunnel-go/default.nix | 27 ++++++++++++++++ tunnel-go/go.mod | 16 ++++++++++ tunnel-go/go.sum | 32 +++++++++++++++++++ tunnel-go/main.go | 74 +++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 229 insertions(+) create mode 100644 LICENSE create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 tunnel-go/default.nix create mode 100644 tunnel-go/go.mod create mode 100644 tunnel-go/go.sum create mode 100644 tunnel-go/main.go diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..9a84c9e --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright © 2024 Daylin Morgan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..eabe566 --- /dev/null +++ b/flake.lock @@ -0,0 +1,27 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1723637854, + "narHash": "sha256-med8+5DSWa2UnOqtdICndjDAEjxr5D7zaIiK4pn0Q7c=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "c3aa7b8938b17aebd2deecf7be0636000d62a2b9", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..9a402e2 --- /dev/null +++ b/flake.nix @@ -0,0 +1,32 @@ +{ + description = "small utilities"; + + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + }; + + outputs = + { nixpkgs, ... }: + let + inherit (nixpkgs.lib) genAttrs; + forAllSystems = + f: + genAttrs [ + "x86_64-linux" + "x86_64-darwin" + "aarch64-linux" + "aarch64-darwin" + ] (system: f nixpkgs.legacyPackages.${system}); + in + { + devShells = forAllSystems (pkgs: { + default = pkgs.mkShell { + packages = with pkgs; [ + go + nim + ]; + }; + }); + formatter = forAllSystems (pkgs: pkgs.nixfmt-rfc-style); + }; +} diff --git a/tunnel-go/default.nix b/tunnel-go/default.nix new file mode 100644 index 0000000..005bc7b --- /dev/null +++ b/tunnel-go/default.nix @@ -0,0 +1,27 @@ +{ + lib, + installShellFiles, + buildGoModule, + makeWrapper, + ... +}: +let + inherit (lib) cleanSource; +in +buildGoModule rec { + pname = "tunnel"; + version = "unstable"; + + src = cleanSource ./.; + vendorHash = ""; + + nativeBuildInputs = [ + installShellFiles + makeWrapper + ]; + + postInstall = '' + installShellCompletion --cmd ${pname} \ + --zsh <($out/bin/${pname} completion zsh) + ''; +} diff --git a/tunnel-go/go.mod b/tunnel-go/go.mod new file mode 100644 index 0000000..8947218 --- /dev/null +++ b/tunnel-go/go.mod @@ -0,0 +1,16 @@ +module git.dayl.in/daylin/utils/tunnel + +go 1.22.5 + +require ( + github.com/bitfield/script v0.22.1 + github.com/spf13/cobra v1.8.1 +) + +require ( + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/itchyny/gojq v0.12.13 // indirect + github.com/itchyny/timefmt-go v0.1.5 // indirect + github.com/spf13/pflag v1.0.5 // indirect + mvdan.cc/sh/v3 v3.7.0 // indirect +) diff --git a/tunnel-go/go.sum b/tunnel-go/go.sum new file mode 100644 index 0000000..af39969 --- /dev/null +++ b/tunnel-go/go.sum @@ -0,0 +1,32 @@ +github.com/bitfield/script v0.22.1 h1:DphxoC5ssYciwd0ZS+N0Xae46geAD/0mVWh6a2NUxM4= +github.com/bitfield/script v0.22.1/go.mod h1:fv+6x4OzVsRs6qAlc7wiGq8fq1b5orhtQdtW0dwjUHI= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA= +github.com/frankban/quicktest v1.14.5/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/itchyny/gojq v0.12.13 h1:IxyYlHYIlspQHHTE0f3cJF0NKDMfajxViuhBLnHd/QU= +github.com/itchyny/gojq v0.12.13/go.mod h1:JzwzAqenfhrPUuwbmEz3nu3JQmFLlQTQMUcOdnu/Sf4= +github.com/itchyny/timefmt-go v0.1.5 h1:G0INE2la8S6ru/ZI5JecgyzbbJNs5lG1RcBqa7Jm6GE= +github.com/itchyny/timefmt-go v0.1.5/go.mod h1:nEP7L+2YmAbT2kZ2HfSs1d8Xtw9LY8D2stDBckWakZ8= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= +github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/tools v0.11.0 h1:EMCa6U9S2LtZXLAMoWiR/R8dAQFRqbAitmbJ2UKhoi8= +golang.org/x/tools v0.11.0/go.mod h1:anzJrxPjNtfgiYQYirP2CPGzGLxrH2u2QBhn6Bf3qY8= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +mvdan.cc/sh/v3 v3.7.0 h1:lSTjdP/1xsddtaKfGg7Myu7DnlHItd3/M2tomOcNNBg= +mvdan.cc/sh/v3 v3.7.0/go.mod h1:K2gwkaesF/D7av7Kxl0HbF5kGOd2ArupNTX3X44+8l8= diff --git a/tunnel-go/main.go b/tunnel-go/main.go new file mode 100644 index 0000000..6bc6410 --- /dev/null +++ b/tunnel-go/main.go @@ -0,0 +1,74 @@ +package main + +import ( + "fmt" + "log" + "os" + "os/exec" + + "github.com/bitfield/script" + "github.com/spf13/cobra" +) + +func logFail(cond bool, s string, args ...interface{}) { + if !cond { + log.Fatalf(s, args...) + } +} + +func ok(cmd *exec.Cmd) bool { err := cmd.Run(); return err == nil } +func portString() string { return fmt.Sprintf("%[1]d:localhost:%[1]d", port) } +func checkControl() bool { return ok(exec.Command("ssh", "-O", "check", host)) } +func activateSshControl() bool { return ok(exec.Command("ssh", "-M", "-f", "-N", host)) } +func connectPort() bool { return ok(exec.Command("ssh", "-fNL", portString(), host)) } +func deactivateSshControl() bool { return ok(exec.Command("ssh", "-O", "exit", host)) } + +func genSubCmd(use string, short string, run func()) *cobra.Command { + return &cobra.Command{ + Use: use, Short: short, Args: cobra.MinimumNArgs(1), + Run: func(cmd *cobra.Command, args []string) { + host = args[0] + run() + }, + } +} + +func tunnelUp() { + if !checkControl() { + logFail(activateSshControl(), "failed to activate ssh for host: %s", host) + } + logFail(connectPort(), "failed to connect to host: %s with port: %d", host, port) +} + +func tunnelDown() { logFail(deactivateSshControl(), "failed to disable ssh control %s", host) } +func tunnelShow() { + script.ListFiles("/home/daylin/.ssh").Stdout() +} + +var ( + rootCmd = &cobra.Command{Use: "tunnel", Short: "control ssh tunnels"} + upCmd = genSubCmd("up hostname [flags]", "activate ssh tunnel", tunnelUp) + downCmd = genSubCmd("down hostname [flags]", "deactivate ssh tunnel", tunnelDown) + showCmd = &cobra.Command{Use: "show", Short: "show activate tunnels", Run: func(cmd *cobra.Command, args []string) { tunnelShow() }} +) + +var ( + port uint64 + host string +) + +func init() { + rootCmd.CompletionOptions.HiddenDefaultCmd = true + rootCmd.AddCommand(upCmd) + rootCmd.AddCommand(downCmd) + rootCmd.AddCommand(showCmd) + upCmd.Flags().Uint64VarP(&port, "port", "p", 0, "port number") + upCmd.MarkFlagRequired("port") +} + +func main() { + err := rootCmd.Execute() + if err != nil { + os.Exit(1) + } +}