Skip to content

Commit

Permalink
Feat: Tailnet org config
Browse files Browse the repository at this point in the history
  • Loading branch information
aaanh committed Apr 3, 2024
1 parent 2e578ac commit 9c38dd8
Show file tree
Hide file tree
Showing 14 changed files with 238 additions and 122 deletions.
28 changes: 28 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# This workflow will build a golang project
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go

name: Continuous Integration

on:
push:
branches: ["master"]
pull_request:
branches: ["master"]
workflow_dispatch:

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: "1.22"

- name: Build
run: make build

- name: Test
run: make test
25 changes: 25 additions & 0 deletions .github/workflows/pr.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# This workflow will build a golang project
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go

name: Go

on:
pull_request:
branches: ["master"]

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: "1.22"

- name: Build
run: go build -v ./...

- name: Test
run: go test -v ./...
21 changes: 21 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# This workflow will build a golang project
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go

name: Go

on:
workflow_dispatch:

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: "1.22"

- name: Build
run: go build -v ./...
10 changes: 5 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ GOGET=$(GOCMD) get
# Name of the executable
BINARY_NAME=tailflare

all: test build
all: cd test build

build:
$(GOBUILD) -o build/$(BINARY_NAME) -v
cd src && $(GOBUILD) -o ../build/$(BINARY_NAME) -v

test:
$(GOTEST) -v ./...
cd src $(GOTEST) -v ./...

clean:
$(GOCLEAN)
clean: cd
cd src && $(GOCLEAN)
rm -f $(BINARY_NAME)
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Tailflare

![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/aaanh/tailflare/ci.yaml)

Sync your **Tail**scale devices to Cloud**flare** DNS.

The functionality is based on this documentation on Tailscale: https://tailscale.com/kb/1054/dns?q=subdomain#using-a-public-dns-subdomain

It is basically taking the Tailscale IP addresses and put them under a subdomain A record on the DNS provider, which is Cloudflare in our case.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
172 changes: 55 additions & 117 deletions main.go → src/lib.go
Original file line number Diff line number Diff line change
@@ -1,58 +1,86 @@
// License: MIT Open Source

package main

import (
"fmt"
"os"
"sort"

"github.com/gofor-little/env"
_ "golang.org/x/term"
)

type Keys struct {
TailscaleApiKey string
CloudflareApiKey string
func updateStates(config *Config) {
if len(config.Keys.CloudflareApiKey) > 0 {
config.States.CloudflareKeyExist = true
} else {
config.States.CloudflareKeyExist = false
}

if len(config.Keys.TailscaleApiKey) > 0 {
config.States.TailscaleKeyExist = true
} else {
config.States.TailscaleKeyExist = false
}

if len(config.TailnetOrg) > 0 {
config.States.TailnetOrgExist = true
} else {
config.States.TailnetOrgExist = false
}
}

type States struct {
TailscaleKeyExist bool
CloudflareKeyExist bool
func configureTailscale(config *Config) {
fmt.Println("Enter Tailscale API key")
fmt.Printf("Get your Tailscale API key: https://login.tailscale.com/login?next_url=%%2Fadmin%%2Fsettings%%2Fkeys\n\n")
fmt.Print("> ")
var apiKey string
fmt.Scan(&apiKey)
config.Keys.TailscaleApiKey = apiKey
fmt.Printf("\n\n")
}

type Config struct {
Version string
States States
Keys Keys
TailnetOrg string
func configureCloudflare(config *Config) {
fmt.Println("Enter Cloudflare API key")
fmt.Printf("How to get your Cloudflare API key: https://developers.cloudflare.com/fundamentals/api/get-started/create-token/\n\n")
fmt.Print("> ")
var apiKey string
fmt.Scan(&apiKey)
config.Keys.CloudflareApiKey = apiKey
fmt.Printf("\n\n")
}

func displayHeader(version string) {
fmt.Printf(">>> Tailflare v%s <<<\n", version)
fmt.Println("Sync your Tailflare hosts with Cloudflare DNS")
fmt.Println()
func configureTailnetOrg(config *Config) {
fmt.Println("Enter Tailnet organization")
fmt.Printf("Should be under the Organization section at https://login.tailscale.com/admin/settings/general\n\n")
fmt.Print("> ")
var tailnetOrg string
fmt.Scan(&tailnetOrg)
config.TailnetOrg = tailnetOrg
fmt.Printf("\n\n")
}

func menu(states States) int {
func menu(cfg Config) int {
menuOptions := map[int]string{
1: fmt.Sprintf("Configure Tailscale API key (%s)", func() string {
if states.TailscaleKeyExist {
if cfg.States.TailscaleKeyExist {
return "Added"
} else {
return "Not added"
}
}()),
2: fmt.Sprintf("Configure Cloudflare API key (%s)", func() string {
if states.CloudflareKeyExist {
if cfg.States.CloudflareKeyExist {
return "Added"
} else {
return "Not added"
}
}()),
3: "Dry run (What-If)",
4: "Perform Sync",
5: "Exit",
3: fmt.Sprintf("Configure Tailnet Organization (%s)", func() string {
if cfg.States.TailnetOrgExist {
return cfg.TailnetOrg
} else {
return "Not added"
}
}()),
4: "Dry run (What-If)",
5: "Perform Sync",
6: "Exit",
}

// Solve the misordered printing by sorting the keys in the map
Expand All @@ -74,93 +102,3 @@ func menu(states States) int {
fmt.Scan(&choice)
return choice
}

func configureTailscale(config *Config) {
fmt.Println("Enter Tailscale API key")
fmt.Printf("Get your Tailscale API key: https://login.tailscale.com/login?next_url=%%2Fadmin%%2Fsettings%%2Fkeys\n\n")
fmt.Print("> ")
var apiKey string
fmt.Scan(&apiKey)
config.Keys.TailscaleApiKey = apiKey
fmt.Printf("\n\n")
}

func configureCloudflare(config *Config) {
fmt.Println("Enter Cloudflare API key")
fmt.Printf("How to get your Cloudflare API key: https://developers.cloudflare.com/fundamentals/api/get-started/create-token/\n\n")
fmt.Print("> ")
var apiKey string
fmt.Scan(&apiKey)
config.Keys.CloudflareApiKey = apiKey
fmt.Printf("\n\n")
}

func dryRun(config Config) any {
fmt.Printf("\n\nPerforming dry run and display what-if results.\n\n")
return nil
}

func runSync(config *Config) {
getTailscaleDevices(config)
}

func choiceHandler(choice int, config *Config) {
switch choice {
case 1:
configureTailscale(config)
case 2:
configureCloudflare(config)
case 3:
dryRun(*config)
case 4:
runSync(config)
case 5:
{
fmt.Println("\n=== Thanks for using Tailflare :) ===")
os.Exit(0)
}
default:
{
fmt.Printf("\n\n> Invalid choice :(\n\n")
}
}
}

func updateStates(config *Config) {
if len(config.Keys.CloudflareApiKey) > 0 {
config.States.CloudflareKeyExist = true
} else {
config.States.CloudflareKeyExist = false
}

if len(config.Keys.TailscaleApiKey) > 0 {
config.States.TailscaleKeyExist = true
} else {
config.States.TailscaleKeyExist = false
}
}

func program(config *Config) {
for {
updateStates(config)
displayHeader(config.Version)
choice := menu(config.States)
choiceHandler(choice, config)
}
}

func main() {
states := States{false, false}

env.Load("./config.cfg", "./.env")
version := env.Get("version", "0.0.0-undefined")
tailscaleApiKey := env.Get("TAILSCALE_API_KEY", "")
cloudflareApiKey := env.Get("CLOUDFLARE_API_KEY", "")

config := Config{version, states, Keys{
tailscaleApiKey, cloudflareApiKey,
}, env.Get("TAILNET_ORG", "undefined")}

program(&config)

}
76 changes: 76 additions & 0 deletions src/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// License: MIT Open Source

package main

import (
"fmt"
"os"

"github.com/gofor-little/env"
_ "golang.org/x/term"
)

func displayHeader(version string) {
fmt.Printf(">>> Tailflare v%s <<<\n", version)
fmt.Println("Sync your Tailflare hosts with Cloudflare DNS")
fmt.Println()
}

func dryRun(config Config) any {
fmt.Printf("\n\nPerforming dry run and display what-if results.\n\n")
return nil
}

func runSync(config *Config) {
getTailscaleDevices(config)
}

func choiceHandler(choice int, config *Config) {
switch choice {
case 1:
configureTailscale(config)
case 2:
configureCloudflare(config)
case 3:
configureTailnetOrg(config)
case 4:
dryRun(*config)
case 5:
runSync(config)
case 6:
{
fmt.Println("\n=== Thanks for using Tailflare :) ===")
os.Exit(0)
}
default:
{
fmt.Printf("\n\n> Invalid choice :(\n\n")
}
}
}

func program(config *Config) {
for {
updateStates(config)
displayHeader(config.Version)
choice := menu(*config)
choiceHandler(choice, config)
}
}

func main() {
states := States{false, false, false}

env.Load("./config.cfg", "./.env")
version := env.Get("version", "0.0.0-undefined")
tailscaleApiKey := env.Get("TAILSCALE_API_KEY", "")
cloudflareApiKey := env.Get("CLOUDFLARE_API_KEY", "")
tailnetOrg := env.Get("TAILNET_ORG", "")

config := Config{version, states,
Keys{tailscaleApiKey, cloudflareApiKey},
tailnetOrg}

program(&config)

}
Loading

0 comments on commit 9c38dd8

Please sign in to comment.