Skip to content

Commit

Permalink
Merge branch 'main' of github.com:mmiranda/cfdtunnel
Browse files Browse the repository at this point in the history
  • Loading branch information
mmiranda committed Nov 26, 2021
2 parents c7ccf3e + d6925e9 commit 4c0d810
Show file tree
Hide file tree
Showing 4 changed files with 134 additions and 33 deletions.
40 changes: 40 additions & 0 deletions .github/workflows/golangci-lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: golangci-lint
on:
push:
branches:
- main
pull_request:
permissions:
contents: read
# Optional: allow read access to pull request. Use with `only-new-issues` option.
# pull-requests: read
jobs:
golangci:
name: lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: golangci-lint
uses: golangci/golangci-lint-action@v2
with:
# Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version
version: v1.29

# Optional: working directory, useful for monorepos
# working-directory: somedir

# Optional: golangci-lint command line arguments.
# args: --issues-exit-code=0

# Optional: show only new issues if it's a pull request. The default value is `false`.
# only-new-issues: true

# Optional: if set to true then the action will use pre-installed Go.
# skip-go-installation: true

# Optional: if set to true then the action don't cache or restore ~/go/pkg.
# skip-pkg-cache: true

# Optional: if set to true then the action don't cache or restore ~/.cache/go-build.
# skip-build-cache: true

78 changes: 51 additions & 27 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,36 @@ import (
"fmt"
"os"
"os/exec"
"strings"
"time"

log "github.com/sirupsen/logrus"
"gopkg.in/ini.v1"
)

const (
IniConfigFile = ".cfdtunnel/config"
DefaultPort = "5555"
iniConfigFile = ".cfdtunnel/config"
localClientDefaultPort = "5555"
)

var (
LogLevel = log.WarnLevel
AppVersion = "Development"
logLevel = log.WarnLevel
appVersion = "Development"
)

type tunnelConfig struct {
host string
port string
// TunnelConfig struct stores data to launch cloudflared process such as hostname and port.
// It also stores preset Environment Variables needed to use together with the tunnel consumer.
type TunnelConfig struct {
host string
port string
envVars []string
}

type config struct {
ini *ini.File
}

// Arguments struct stores the arguments passed to cfdtunel such as the profile to use, the command to run and the arguments for that command
type Arguments struct {
profile *string
command string
Expand All @@ -38,14 +43,14 @@ type Arguments struct {

func init() {
log.SetOutput(os.Stdout)
log.SetLevel(LogLevel)
log.SetLevel(logLevel)
}

func main() {
args := flagArguments()
log.SetLevel(LogLevel)
log.SetLevel(logLevel)

config, err := readIniConfigFile(getHomePathIniFile(IniConfigFile))
config, err := readIniConfigFile(getHomePathIniFile(iniConfigFile))

if err != nil {
log.Fatalf("An error occured reading your INI file: %v", err.Error())
Expand All @@ -56,15 +61,15 @@ func main() {
if err != nil {
log.Fatalf("An error occured reading your INI file: %v", err.Error())
}

cmd := startProxyTunnel(tunnelConfig)
os.Setenv("HTTPS_PROXY", "socks5://127.0.0.1:"+tunnelConfig.port)
runSubCommand(args)
tunnelConfig.setupEnvironmentVariables()
cmd := tunnelConfig.startProxyTunnel()
args.runSubCommand()

// Kill it:
commandKill(cmd)
}

// commandKill Kills an specific *exec.Cmd command
func commandKill(cmd *exec.Cmd) {
log.Debugf("Trying to kill PID: %v", cmd.Process.Pid)

Expand All @@ -73,7 +78,8 @@ func commandKill(cmd *exec.Cmd) {
}
}

func runSubCommand(args Arguments) {
// runSubCommand Runs the SubCommand and its arguments passed to cfdtunnel
func (args Arguments) runSubCommand() {
log.Debugf("Running subcommand: %v", args.command)
if !checkSubCommandExists(args.command) {
os.Exit(1)
Expand All @@ -89,10 +95,10 @@ func runSubCommand(args Arguments) {

}

// readIniConfigFile checks if the file exists
// readIniConfigFile reads and load the config file.
func readIniConfigFile(configFile string) (config, error) {

cfg, err := ini.Load(configFile)
cfg, err := ini.ShadowLoad(configFile)

if err != nil {
return config{}, err
Expand All @@ -103,7 +109,17 @@ func readIniConfigFile(configFile string) (config, error) {
}, err
}

func startProxyTunnel(tunnelConfig tunnelConfig) *exec.Cmd {
// setupEnvironmentVariables Sets every environment variables that are expected and informed on the config file
func (tunnelConfig TunnelConfig) setupEnvironmentVariables() {
for _, env := range tunnelConfig.envVars {
iniEnv := strings.Split(env, "=")
log.Debugf("Exporting Environment variable: %v", env)
os.Setenv(iniEnv[0], iniEnv[1])
}
}

// startProxyTunnel Starts the proxy tunnel (cloudflared process) and return its command instance
func (tunnelConfig TunnelConfig) startProxyTunnel() *exec.Cmd {
log.Debugf("Starting proxy tunnel for %v on port: %v", tunnelConfig.host, tunnelConfig.port)

cmd := exec.Command("cloudflared", "access", "tcp", "--hostname", tunnelConfig.host, "--url", "127.0.0.1:"+tunnelConfig.port)
Expand All @@ -122,36 +138,43 @@ func startProxyTunnel(tunnelConfig tunnelConfig) *exec.Cmd {
return cmd
}

func (cfg config) readConfigSection(section string) (tunnelConfig, error) {
// readConfigSection reads an specific section from a config file.
// It returns a tunnelConfig struct containing the hostname, port and any environment variable needed
func (cfg config) readConfigSection(section string) (TunnelConfig, error) {

secs, err := cfg.ini.GetSection(section)

if err != nil {
log.Debugf("An error occured: %v", err.Error())
return tunnelConfig{}, err
return TunnelConfig{}, err
}

host, _ := secs.GetKey("host")

port := secs.Key("port").Validate(func(port string) string {
if len(port) == 0 {
return DefaultPort
return localClientDefaultPort
}
return port
})

return tunnelConfig{
host: host.String(),
port: port,
envVars := secs.Key("env").ValueWithShadows()

return TunnelConfig{
host: host.String(),
port: port,
envVars: envVars,
}, nil
}

// getHomePathIniFile Returns the full path of config file based on users home directory
func getHomePathIniFile(file string) string {
home, _ := os.UserHomeDir()

return home + "/" + file
}

// flagArguments Reads and parde the arguments passed to cfdtunnel.
// It returns an Argument Struct containing the profile, subcommand to run and all the arguments for the subcommand
func flagArguments() Arguments {
profile := flag.String("profile", "", "Which cfdtunnel profile to use")
version := flag.Bool("version", false, "Show cfdtunnel version")
Expand All @@ -160,12 +183,12 @@ func flagArguments() Arguments {
flag.Parse()

if *version {
fmt.Println(AppVersion)
fmt.Println(appVersion)
os.Exit(0)
}

if *debug {
LogLevel = log.DebugLevel
logLevel = log.DebugLevel
}

if *profile == "" {
Expand All @@ -183,6 +206,7 @@ func flagArguments() Arguments {
}
}

// checkSubCommandExists simple check if an specific binary exists in the OS
func checkSubCommandExists(command string) bool {
_, err := exec.LookPath(command)
if err != nil {
Expand Down
40 changes: 34 additions & 6 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ func TestSectionComplete(t *testing.T) {

tunnelCfg, err := config.readConfigSection("alias1")

assert.IsType(t, tunnelConfig{}, tunnelCfg)
assert.IsType(t, TunnelConfig{}, tunnelCfg)
assert.Equal(t, "https://kubernetes.foo.bar.com", tunnelCfg.host)
assert.Equal(t, "1234", tunnelCfg.port)
assert.NoError(t, err)
Expand All @@ -72,24 +72,52 @@ func TestSectionDefaultPort(t *testing.T) {

tunnelCfg, err := config.readConfigSection("alias2")

assert.IsType(t, tunnelConfig{}, tunnelCfg)
assert.IsType(t, TunnelConfig{}, tunnelCfg)
assert.Equal(t, "sql.foo.bar.com", tunnelCfg.host)
assert.Equal(t, DefaultPort, tunnelCfg.port)
assert.Equal(t, localClientDefaultPort, tunnelCfg.port)
assert.NoError(t, err)
}

func TestIniEnvVars(t *testing.T) {
config, _ := readIniConfigFile("test/config")

tunnelCfg, err := config.readConfigSection("test-env-var")
assert.Equal(t, []string{"MY_ENV_VAR=value"}, tunnelCfg.envVars)
assert.NoError(t, err)

tunnelCfg, err = config.readConfigSection("test-multi-env-var")
assert.Equal(t, []string{"MY_ENV_VAR=value", "HTTPS_PROXY=127.0.0.1:5555"}, tunnelCfg.envVars)
assert.NoError(t, err)

}

func TestOSEnvVars(t *testing.T) {
config, _ := readIniConfigFile("test/config")
tunnelCfg, _ := config.readConfigSection("test-multi-env-var")

tunnelCfg.setupEnvironmentVariables()

assert.Equal(t, "value", os.Getenv("MY_ENV_VAR"))
assert.Equal(t, "127.0.0.1:5555", os.Getenv("HTTPS_PROXY"))

}

func TestProxyTunnel(t *testing.T) {
cmd := startProxyTunnel(tunnelConfig{"foo.bar", "1234"})
tunnelConfig := TunnelConfig{"foo.bar", "1234", nil}
cmd := tunnelConfig.startProxyTunnel()
osPid, _ := os.FindProcess(cmd.Process.Pid)
assert.Equal(t, cmd.Process.Pid, osPid.Pid)
commandKill(cmd)

}

func TestTunnelSamePort(t *testing.T) {
cmd1 := startProxyTunnel(tunnelConfig{"foo.bar.first", "1234"})

cmd2 := startProxyTunnel(tunnelConfig{"foo.bar.second", "1234"})
tunnelCfg := TunnelConfig{"foo.bar.first", "1234", nil}
cmd1 := tunnelCfg.startProxyTunnel()

tunnelCfg = TunnelConfig{"foo.bar.first", "1234", nil}
cmd2 := tunnelCfg.startProxyTunnel()

err := cmd2.Wait()
assert.Error(t, err)
Expand Down
9 changes: 9 additions & 0 deletions test/config
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,12 @@ port = 1234

[alias2]
host = sql.foo.bar.com

[test-multi-env-var]
host = foo.bar.com
env = MY_ENV_VAR=value
env = HTTPS_PROXY=127.0.0.1:5555

[test-env-var]
host = foo.bar.com
env = MY_ENV_VAR=value

0 comments on commit 4c0d810

Please sign in to comment.