Skip to content

Commit 6acedb7

Browse files
Merge branch 'main' of github.com:fosrl/cli
2 parents b34f562 + 52bf022 commit 6acedb7

63 files changed

Lines changed: 1707 additions & 71 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Makefile

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ go-build-release: \
5555
go-build-release-linux-amd64 \
5656
go-build-release-linux-riscv64 \
5757
go-build-release-darwin-arm64 \
58-
go-build-release-darwin-amd64
58+
go-build-release-darwin-amd64 \
59+
go-build-release-windows-amd64
5960

6061
go-build-release-linux-arm64:
6162
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -o bin/pangolin-cli_linux_arm64
@@ -76,4 +77,7 @@ go-build-release-darwin-arm64:
7677
CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -o bin/pangolin-cli_darwin_arm64
7778

7879
go-build-release-darwin-amd64:
79-
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -o bin/pangolin-cli_darwin_amd64
80+
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -o bin/pangolin-cli_darwin_amd64
81+
82+
go-build-release-windows-amd64:
83+
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o bin/pangolin-cli_windows_amd64.exe

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# [Pangolin](https://pangolin.net) CLI
22

3-
This is the official Pangolin CLI tool and VPN client for Unix devices. Pangolin CLI is currently only available on macOS and Linux. Windows support is coming soon.
3+
This is the official Pangolin CLI tool.
44

55
Since there isn't an official GUI application for Linux, this CLI serves as the official way to connect to Pangolin VPN on Linux.
66

cmd/authdaemon/authdaemon.go

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
//go:build linux
2+
3+
package authdaemon
4+
5+
import (
6+
"context"
7+
"errors"
8+
"fmt"
9+
"os"
10+
"os/signal"
11+
"syscall"
12+
13+
"github.com/fosrl/cli/internal/logger"
14+
authdaemonpkg "github.com/fosrl/newt/authdaemon"
15+
"github.com/spf13/cobra"
16+
)
17+
18+
const (
19+
defaultPort = 22123
20+
defaultPrincipalsPath = "/var/run/auth-daemon/principals"
21+
defaultCACertPath = "/etc/ssh/ca.pem"
22+
)
23+
24+
var (
25+
errPresharedKeyRequired = errors.New("pre-shared-key is required")
26+
errRootRequired = errors.New("auth-daemon must be run as root (use sudo)")
27+
)
28+
29+
func AuthDaemonCmd() *cobra.Command {
30+
opts := struct {
31+
PreSharedKey string
32+
Port int
33+
PrincipalsFile string
34+
CACertPath string
35+
GenerateRandomPassword bool
36+
}{}
37+
38+
cmd := &cobra.Command{
39+
Use: "auth-daemon",
40+
Short: "Start the auth daemon",
41+
Long: "Start the auth daemon for remote SSH authentication",
42+
PreRunE: func(c *cobra.Command, args []string) error {
43+
if opts.PreSharedKey == "" {
44+
return errPresharedKeyRequired
45+
}
46+
if os.Geteuid() != 0 {
47+
return errRootRequired
48+
}
49+
return nil
50+
},
51+
Run: func(c *cobra.Command, args []string) {
52+
runAuthDaemon(opts)
53+
},
54+
}
55+
56+
cmd.Flags().StringVar(&opts.PreSharedKey, "pre-shared-key", "", "Preshared key required for all requests to the auth daemon (required)")
57+
cmd.MarkFlagRequired("pre-shared-key")
58+
cmd.Flags().IntVar(&opts.Port, "port", defaultPort, "TCP listen port for the HTTPS server")
59+
cmd.Flags().StringVar(&opts.PrincipalsFile, "principals-file", defaultPrincipalsPath, "Path to the principals file")
60+
cmd.Flags().StringVar(&opts.CACertPath, "ca-cert-path", defaultCACertPath, "Path to the CA certificate file")
61+
cmd.Flags().BoolVar(&opts.GenerateRandomPassword, "generate-random-password", false, "Generate a random password for authenticated users")
62+
63+
cmd.AddCommand(PrincipalsCmd())
64+
65+
return cmd
66+
}
67+
68+
// PrincipalsCmd returns the "principals" subcommand for use as AuthorizedPrincipalsCommand in sshd_config.
69+
func PrincipalsCmd() *cobra.Command {
70+
opts := struct {
71+
PrincipalsFile string
72+
Username string
73+
}{}
74+
75+
cmd := &cobra.Command{
76+
Use: "principals",
77+
Short: "Output principals for a username (for AuthorizedPrincipalsCommand in sshd_config)",
78+
Long: "Read the principals file and print principals that match the given username, one per line. Configure in sshd_config with AuthorizedPrincipalsCommand and %u for the username.",
79+
PreRunE: func(c *cobra.Command, args []string) error {
80+
if opts.Username == "" {
81+
return errors.New("username is required")
82+
}
83+
return nil
84+
},
85+
Run: func(c *cobra.Command, args []string) {
86+
path := opts.PrincipalsFile
87+
if path == "" {
88+
path = defaultPrincipalsPath
89+
}
90+
runPrincipals(path, opts.Username)
91+
},
92+
}
93+
94+
cmd.Flags().StringVar(&opts.PrincipalsFile, "principals-file", defaultPrincipalsPath, "Path to the principals file written by the auth daemon")
95+
cmd.Flags().StringVar(&opts.Username, "username", "", "Username to look up (e.g. from sshd %u)")
96+
cmd.MarkFlagRequired("username")
97+
98+
return cmd
99+
}
100+
101+
func runPrincipals(principalsPath, username string) {
102+
list, err := authdaemonpkg.GetPrincipals(principalsPath, username)
103+
if err != nil {
104+
logger.Error("%v", err)
105+
os.Exit(1)
106+
}
107+
if len(list) == 0 {
108+
fmt.Println("")
109+
return
110+
}
111+
for _, principal := range list {
112+
fmt.Println(principal)
113+
}
114+
}
115+
116+
func runAuthDaemon(opts struct {
117+
PreSharedKey string
118+
Port int
119+
PrincipalsFile string
120+
CACertPath string
121+
GenerateRandomPassword bool
122+
}) {
123+
cfg := authdaemonpkg.Config{
124+
Port: opts.Port,
125+
PresharedKey: opts.PreSharedKey,
126+
PrincipalsFilePath: opts.PrincipalsFile,
127+
CACertPath: opts.CACertPath,
128+
Force: true,
129+
GenerateRandomPassword: opts.GenerateRandomPassword,
130+
}
131+
132+
srv, err := authdaemonpkg.NewServer(cfg)
133+
if err != nil {
134+
logger.Error("%v", err)
135+
os.Exit(1)
136+
}
137+
138+
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
139+
defer stop()
140+
141+
if err := srv.Run(ctx); err != nil {
142+
logger.Error("%v", err)
143+
os.Exit(1)
144+
}
145+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
//go:build darwin
2+
3+
package authdaemon
4+
5+
import (
6+
"github.com/spf13/cobra"
7+
)
8+
9+
// AuthDaemonCmd returns nil on macOS as this command is not supported.
10+
func AuthDaemonCmd() *cobra.Command {
11+
return nil
12+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
//go:build windows
2+
3+
package authdaemon
4+
5+
import (
6+
"github.com/spf13/cobra"
7+
)
8+
9+
// AuthDaemonCmd returns nil on Windows as this command is not supported.
10+
func AuthDaemonCmd() *cobra.Command {
11+
return nil
12+
}
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
//go:build !windows
2+
13
package down
24

35
import (
@@ -20,4 +22,4 @@ If ran with no subcommand, 'client' is passed.
2022
cmd.AddCommand(client.ClientDownCmd())
2123

2224
return cmd
23-
}
25+
}

cmd/down/down_windows.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
//go:build windows
2+
3+
package down
4+
5+
import (
6+
"github.com/spf13/cobra"
7+
)
8+
9+
// DownCmd returns nil on Windows as this command is not supported.
10+
func DownCmd() *cobra.Command {
11+
return nil
12+
}
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
//go:build !windows
2+
13
package logs
24

35
import (
@@ -15,4 +17,4 @@ func LogsCmd() *cobra.Command {
1517
cmd.AddCommand(client.ClientLogsCmd())
1618

1719
return cmd
18-
}
20+
}

cmd/logs/logs_windows.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
//go:build windows
2+
3+
package logs
4+
5+
import (
6+
"github.com/spf13/cobra"
7+
)
8+
9+
// LogsCmd returns nil on Windows as this command is not supported.
10+
func LogsCmd() *cobra.Command {
11+
return nil
12+
}

cmd/root.go

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@ import (
1010
"github.com/fosrl/cli/cmd/auth"
1111
"github.com/fosrl/cli/cmd/auth/login"
1212
"github.com/fosrl/cli/cmd/auth/logout"
13+
"github.com/fosrl/cli/cmd/authdaemon"
1314
"github.com/fosrl/cli/cmd/down"
1415
"github.com/fosrl/cli/cmd/logs"
1516
selectcmd "github.com/fosrl/cli/cmd/select"
17+
"github.com/fosrl/cli/cmd/ssh"
1618
"github.com/fosrl/cli/cmd/status"
1719
"github.com/fosrl/cli/cmd/up"
1820
"github.com/fosrl/cli/cmd/update"
@@ -42,12 +44,27 @@ func RootCommand(initResources bool) (*cobra.Command, error) {
4244
}
4345

4446
cmd.AddCommand(auth.AuthCommand())
47+
if authDaemonCmd := authdaemon.AuthDaemonCmd(); authDaemonCmd != nil {
48+
cmd.AddCommand(authDaemonCmd)
49+
}
4550
cmd.AddCommand(apply.ApplyCommand())
4651
cmd.AddCommand(selectcmd.SelectCmd())
47-
cmd.AddCommand(up.UpCmd())
48-
cmd.AddCommand(down.DownCmd())
49-
cmd.AddCommand(logs.LogsCmd())
50-
cmd.AddCommand(status.StatusCmd())
52+
53+
// Platform-specific commands - nil on unsupported platforms
54+
if upCmd := up.UpCmd(); upCmd != nil {
55+
cmd.AddCommand(upCmd)
56+
}
57+
if downCmd := down.DownCmd(); downCmd != nil {
58+
cmd.AddCommand(downCmd)
59+
}
60+
if logsCmd := logs.LogsCmd(); logsCmd != nil {
61+
cmd.AddCommand(logsCmd)
62+
}
63+
if statusCmd := status.StatusCmd(); statusCmd != nil {
64+
cmd.AddCommand(statusCmd)
65+
}
66+
67+
cmd.AddCommand(ssh.SSHCmd())
5168
cmd.AddCommand(update.UpdateCmd())
5269
cmd.AddCommand(version.VersionCmd())
5370
cmd.AddCommand(login.LoginCmd())

0 commit comments

Comments
 (0)