Skip to content

Commit cf322bb

Browse files
Run flatpak-spawn when pkexec is needed (#86)
This PR checks the env vars to see if we're running inside of flatpak, and if saw prepends the pkexec with flatpak-spawn in order rerun tsrelay as sudo. We also run `flatpak override` so the user doesn't have to. Fixes #76
1 parent cf36726 commit cf322bb

File tree

4 files changed

+75
-8
lines changed

4 files changed

+75
-8
lines changed

src/tailscale/cli.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -154,11 +154,17 @@ export class Tailscale {
154154
Logger.info(`path: ${binPath}`, LOG_COMPONENT);
155155
this.notifyExit = () => {
156156
Logger.info('starting sudo tsrelay');
157-
const childProcess = cp.spawn(`/usr/bin/pkexec`, [
158-
'--disable-internal-agent',
159-
binPath,
160-
...args,
161-
]);
157+
let authCmd = `/usr/bin/pkexec`;
158+
let authArgs = ['--disable-internal-agent', binPath, ...args];
159+
if (
160+
process.env['container'] === 'flatpak' &&
161+
process.env['FLATPAK_ID'] &&
162+
process.env['FLATPAK_ID'].startsWith('com.visualstudio.code')
163+
) {
164+
authCmd = 'flatpak-spawn';
165+
authArgs = ['--host', 'pkexec', '--disable-internal-agent', binPath, ...args];
166+
}
167+
const childProcess = cp.spawn(authCmd, authArgs);
162168
childProcess.on('exit', async (code) => {
163169
Logger.warn(`sudo child process exited with code ${code}`, LOG_COMPONENT);
164170
if (code === 0) {

src/tailscale/error.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,11 @@ export function errorForType(type: string): TailscaleError {
4343
},
4444
],
4545
};
46+
case 'FLATPAK_REQUIRES_RESTART':
47+
return {
48+
title: 'Restart Flatpak Container',
49+
message: 'Please quit VSCode and restart the container to finish setting up Tailscale',
50+
};
4651
default:
4752
return {
4853
title: 'Unknown error',

src/types.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,14 @@ export interface WithErrors {
3333
Errors?: RelayError[];
3434
}
3535

36-
interface RelayError {
37-
Type: 'FUNNEL_OFF' | 'HTTPS_OFF' | 'OFFLINE' | 'REQUIRES_SUDO' | 'NOT_RUNNING';
36+
export interface RelayError {
37+
Type:
38+
| 'FUNNEL_OFF'
39+
| 'HTTPS_OFF'
40+
| 'OFFLINE'
41+
| 'REQUIRES_SUDO'
42+
| 'NOT_RUNNING'
43+
| 'FLATPAK_REQUIRES_RESTART';
3844
}
3945

4046
interface PeerStatus {

tsrelay/main.go

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"net/http"
1414
"net/url"
1515
"os"
16+
"os/exec"
1617
"os/signal"
1718
"strconv"
1819
"strings"
@@ -56,8 +57,13 @@ const (
5657
// NotRunning indicates tailscaled is
5758
// not running
5859
NotRunning = "NOT_RUNNING"
60+
// FlatpakRequiresRestart indicates that the flatpak
61+
// container needs to be fully restarted
62+
FlatpakRequiresRestart = "FLATPAK_REQUIRES_RESTART"
5963
)
6064

65+
var requiresRestart bool
66+
6167
func main() {
6268
must(run())
6369
}
@@ -78,12 +84,50 @@ func run() error {
7884
Logger: log.New(logOut, "", 0),
7985
}
8086

87+
flatpakID := os.Getenv("FLATPAK_ID")
88+
isFlatpak := os.Getenv("container") == "flatpak" && strings.HasPrefix(flatpakID, "com.visualstudio.code")
89+
if isFlatpak {
90+
lggr.Println("running inside flatpak")
91+
var err error
92+
requiresRestart, err = ensureTailscaledAccessible(lggr, flatpakID)
93+
if err != nil {
94+
return err
95+
}
96+
lggr.Printf("requires restart: %v", requiresRestart)
97+
}
98+
8199
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
82100
defer cancel()
83101

84102
return runHTTPServer(ctx, lggr, *port, *nonce)
85103
}
86104

105+
func ensureTailscaledAccessible(lggr *logger, flatpakID string) (bool, error) {
106+
_, err := os.Stat("/run/tailscale")
107+
if err == nil {
108+
lggr.Println("tailscaled is accessible")
109+
return false, nil
110+
}
111+
if !errors.Is(err, os.ErrNotExist) {
112+
return false, fmt.Errorf("error checking /run/tailscale: %w", err)
113+
}
114+
lggr.Println("running flatpak override")
115+
cmd := exec.Command(
116+
"flatpak-spawn",
117+
"--host",
118+
"flatpak",
119+
"override",
120+
"--user",
121+
flatpakID,
122+
"--filesystem=/run/tailscale",
123+
)
124+
output, err := cmd.Output()
125+
if err != nil {
126+
return false, fmt.Errorf("error running flatpak override: %s - %w", output, err)
127+
}
128+
return true, nil
129+
}
130+
87131
type serverDetails struct {
88132
Address string `json:"address,omitempty"`
89133
Nonce string `json:"nonce,omitempty"`
@@ -252,6 +296,12 @@ func (h *httpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
252296
}
253297
w.Write([]byte(`{}`))
254298
case http.MethodGet:
299+
if requiresRestart {
300+
json.NewEncoder(w).Encode(RelayError{
301+
Errors: []Error{{Type: FlatpakRequiresRestart}},
302+
})
303+
return
304+
}
255305
var wg sync.WaitGroup
256306
wg.Add(1)
257307
portMap := map[uint16]string{}
@@ -414,8 +464,8 @@ func (h *httpHandler) getConfigs(ctx context.Context) (*ipnstate.Status, *ipn.Se
414464
var (
415465
st *ipnstate.Status
416466
sc *ipn.ServeConfig
417-
g errgroup.Group
418467
)
468+
g, ctx := errgroup.WithContext(ctx)
419469
g.Go(func() error {
420470
var err error
421471
sc, err = h.lc.GetServeConfig(ctx)

0 commit comments

Comments
 (0)