nullgate 1.1.1__tar.gz → 1.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {nullgate-1.1.1/src/nullgate.egg-info → nullgate-1.2.1}/PKG-INFO +73 -37
- {nullgate-1.1.1 → nullgate-1.2.1}/README.md +72 -36
- nullgate-1.2.1/src/nullgate/__init__.py +1 -0
- nullgate-1.2.1/src/nullgate/commands.py +275 -0
- {nullgate-1.1.1 → nullgate-1.2.1}/src/nullgate/session.py +24 -12
- {nullgate-1.1.1 → nullgate-1.2.1/src/nullgate.egg-info}/PKG-INFO +73 -37
- {nullgate-1.1.1 → nullgate-1.2.1}/tests/test_commands.py +93 -20
- nullgate-1.1.1/src/nullgate/__init__.py +0 -1
- nullgate-1.1.1/src/nullgate/commands.py +0 -179
- {nullgate-1.1.1 → nullgate-1.2.1}/LICENSE +0 -0
- {nullgate-1.1.1 → nullgate-1.2.1}/pyproject.toml +0 -0
- {nullgate-1.1.1 → nullgate-1.2.1}/setup.cfg +0 -0
- {nullgate-1.1.1 → nullgate-1.2.1}/src/nullgate/account.py +0 -0
- {nullgate-1.1.1 → nullgate-1.2.1}/src/nullgate/bridge.py +0 -0
- {nullgate-1.1.1 → nullgate-1.2.1}/src/nullgate/client_config.py +0 -0
- {nullgate-1.1.1 → nullgate-1.2.1}/src/nullgate/gateway.py +0 -0
- {nullgate-1.1.1 → nullgate-1.2.1}/src/nullgate/ingress.py +0 -0
- {nullgate-1.1.1 → nullgate-1.2.1}/src/nullgate/runtime.py +0 -0
- {nullgate-1.1.1 → nullgate-1.2.1}/src/nullgate/transports.py +0 -0
- {nullgate-1.1.1 → nullgate-1.2.1}/src/nullgate/wsroute.py +0 -0
- {nullgate-1.1.1 → nullgate-1.2.1}/src/nullgate.egg-info/SOURCES.txt +0 -0
- {nullgate-1.1.1 → nullgate-1.2.1}/src/nullgate.egg-info/dependency_links.txt +0 -0
- {nullgate-1.1.1 → nullgate-1.2.1}/src/nullgate.egg-info/entry_points.txt +0 -0
- {nullgate-1.1.1 → nullgate-1.2.1}/src/nullgate.egg-info/requires.txt +0 -0
- {nullgate-1.1.1 → nullgate-1.2.1}/src/nullgate.egg-info/top_level.txt +0 -0
- {nullgate-1.1.1 → nullgate-1.2.1}/tests/test_account.py +0 -0
- {nullgate-1.1.1 → nullgate-1.2.1}/tests/test_bridge.py +0 -0
- {nullgate-1.1.1 → nullgate-1.2.1}/tests/test_gateway.py +0 -0
- {nullgate-1.1.1 → nullgate-1.2.1}/tests/test_ingress.py +0 -0
- {nullgate-1.1.1 → nullgate-1.2.1}/tests/test_install.py +0 -0
- {nullgate-1.1.1 → nullgate-1.2.1}/tests/test_wsroute.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nullgate
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2.1
|
|
4
4
|
Summary: Disposable SSH gateway into confined directory workspaces
|
|
5
5
|
Requires-Python: >=3.10
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -14,11 +14,37 @@ Dynamic: license-file
|
|
|
14
14
|
|
|
15
15
|
Disposable SSH gateway into confined directory workspaces for remote containers, ephemeral developer boxes, CI nodes, and cloud instances.
|
|
16
16
|
|
|
17
|
+
## Quick start
|
|
18
|
+
|
|
19
|
+
Run Nullgate directly from PyPI without installing it first:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
# Share the current directory over the default Upterm provider
|
|
23
|
+
uvx nullgate start
|
|
24
|
+
|
|
25
|
+
# Share a specific workspace
|
|
26
|
+
uvx nullgate start /workspace
|
|
27
|
+
|
|
28
|
+
# Compact form: workspace, SSH port, srv.us slot, provider
|
|
29
|
+
uvx nullgate start /workspace 8022 1 -t srvus
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Then check the endpoint and print the SSH connection setup:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
uvx nullgate status
|
|
36
|
+
uvx nullgate connect
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Nullgate uses authorized SSH keys by default. Upterm needs a key in
|
|
40
|
+
`~/.ssh/authorized_keys`; use `--public-access` only for an intentionally public
|
|
41
|
+
temporary endpoint.
|
|
42
|
+
|
|
17
43
|
## Features
|
|
18
44
|
|
|
19
45
|
- **Ingress Transports**: Publish endpoints via Upterm WebSocket relays (default), srv.us reverse tunnels, Cloudflare Worker relays, or Cloudflare Argo (`cloudflared`) tunnels.
|
|
20
46
|
- **Confinement**: SFTP and SCP workloads are restricted to the designated workspace root; parent traversals and out-of-root symlinks are blocked.
|
|
21
|
-
- **Host file visibility**: Relative transfer paths begin in the workspace root while absolute paths address host locations, matching shell behavior; add `--
|
|
47
|
+
- **Host file visibility**: Relative transfer paths begin in the workspace root while absolute paths address host locations, matching shell behavior; add `--restrict-file-transfer` for strict workspace-only transfers.
|
|
22
48
|
- **Direct Shell Access**: Authenticated PTY sessions run interactively under the local user credentials.
|
|
23
49
|
- **Synthetic UID Resilience**: Runs seamlessly in container environments missing user records in `/etc/passwd`.
|
|
24
50
|
- **Persistent Key Identities**: Host keys and identity seeds persist across restarts rather than being silently regenerated.
|
|
@@ -44,77 +70,82 @@ pip install nullgate
|
|
|
44
70
|
|
|
45
71
|
## Usage
|
|
46
72
|
|
|
47
|
-
###
|
|
73
|
+
### Start
|
|
48
74
|
|
|
49
75
|
Launch the gateway service and ingress transport in the background:
|
|
50
76
|
|
|
51
77
|
```bash
|
|
52
78
|
# Default Upterm transport on port 4822 sharing current directory
|
|
53
|
-
nullgate
|
|
79
|
+
nullgate start
|
|
54
80
|
|
|
55
81
|
# Custom path, port, and slot
|
|
56
|
-
nullgate
|
|
82
|
+
nullgate start /workspace --ssh-port 4822 --tunnel-slot 1
|
|
83
|
+
|
|
84
|
+
# The same values in compact positional form
|
|
85
|
+
nullgate start /workspace 4822 1
|
|
57
86
|
|
|
58
87
|
# Select specific transport provider
|
|
59
|
-
nullgate
|
|
60
|
-
nullgate
|
|
61
|
-
nullgate
|
|
62
|
-
nullgate
|
|
88
|
+
nullgate start . --provider upterm
|
|
89
|
+
nullgate start . --provider srvus
|
|
90
|
+
nullgate start . --provider cloudflare --relay-url https://relay.example.com
|
|
91
|
+
nullgate start . --provider cloudflared --public-hostname workspace.example.com --cloudflare-token "$TUNNEL_TOKEN"
|
|
63
92
|
```
|
|
64
93
|
|
|
65
94
|
Options:
|
|
66
|
-
- `--
|
|
67
|
-
- `--
|
|
68
|
-
- `--
|
|
69
|
-
- `--
|
|
70
|
-
- `--
|
|
71
|
-
- `--
|
|
72
|
-
- `--
|
|
73
|
-
- `--no-
|
|
74
|
-
- `--
|
|
75
|
-
- `--no-
|
|
76
|
-
|
|
77
|
-
|
|
95
|
+
- `-p`, `--ssh-port`: Local SSH server port.
|
|
96
|
+
- `-s`, `--tunnel-slot`: srv.us tunnel slot.
|
|
97
|
+
- `-t`, `--provider`: Public connection provider (`upterm`, `srvus`, `cloudflare`, or `cloudflared`). Defaults to `upterm`.
|
|
98
|
+
- `-e`, `--relay-url`: WebSocket relay URL used with `upterm` or `cloudflare`.
|
|
99
|
+
- `-H`, `--public-hostname`: Public hostname used with `cloudflared`.
|
|
100
|
+
- `--cloudflare-token`: Cloudflare tunnel credential, or set `NULLGATE_CLOUDFLARED_TOKEN`.
|
|
101
|
+
- `--public-access`: Allow connections without credentials. This is unsafe on public endpoints.
|
|
102
|
+
- `--no-public-access`: Require authentication.
|
|
103
|
+
- `--tcp-forwarding`: Enable SSH TCP tunneling (`ssh -L`).
|
|
104
|
+
- `--no-tcp-forwarding`: Disable SSH TCP tunneling.
|
|
105
|
+
- `--restrict-file-transfer`: Keep SFTP and SCP inside the workspace.
|
|
106
|
+
- `--no-restrict-file-transfer`: Allow host-visible file transfers.
|
|
107
|
+
|
|
108
|
+
### Status
|
|
78
109
|
|
|
79
110
|
Display operational status, running PIDs, connection URLs, and security settings:
|
|
80
111
|
|
|
81
112
|
```bash
|
|
82
|
-
nullgate
|
|
113
|
+
nullgate status
|
|
83
114
|
```
|
|
84
115
|
|
|
85
|
-
###
|
|
116
|
+
### Connect
|
|
86
117
|
|
|
87
118
|
Print client connection strings and OpenSSH client configuration directives:
|
|
88
119
|
|
|
89
120
|
```bash
|
|
90
|
-
nullgate
|
|
121
|
+
nullgate connect
|
|
91
122
|
```
|
|
92
123
|
|
|
93
|
-
###
|
|
124
|
+
### Logs
|
|
94
125
|
|
|
95
126
|
Stream or print daemon logs:
|
|
96
127
|
|
|
97
128
|
```bash
|
|
98
|
-
nullgate
|
|
99
|
-
nullgate
|
|
100
|
-
nullgate
|
|
101
|
-
nullgate
|
|
129
|
+
nullgate logs
|
|
130
|
+
nullgate logs -f
|
|
131
|
+
nullgate logs gateway
|
|
132
|
+
nullgate logs transport
|
|
102
133
|
```
|
|
103
134
|
|
|
104
|
-
###
|
|
135
|
+
### Stop
|
|
105
136
|
|
|
106
137
|
Terminate running gateway and transport background processes:
|
|
107
138
|
|
|
108
139
|
```bash
|
|
109
|
-
nullgate
|
|
140
|
+
nullgate stop
|
|
110
141
|
```
|
|
111
142
|
|
|
112
|
-
###
|
|
143
|
+
### Restart
|
|
113
144
|
|
|
114
145
|
Restart daemon instances reusing active or saved workspace settings:
|
|
115
146
|
|
|
116
147
|
```bash
|
|
117
|
-
nullgate
|
|
148
|
+
nullgate restart
|
|
118
149
|
```
|
|
119
150
|
|
|
120
151
|
### Upgrade
|
|
@@ -126,13 +157,18 @@ nullgate upgrade
|
|
|
126
157
|
nullgate upgrade --version 1.0.1
|
|
127
158
|
```
|
|
128
159
|
|
|
160
|
+
Short command aliases are `up`, `down`, `ps`, `ssh`, `log`, and `reload`.
|
|
161
|
+
The earlier names `open`, `shut`, `inspect`, `enter`, `trace`, and `cycle`
|
|
162
|
+
remain available as compatibility aliases. Their earlier option names also
|
|
163
|
+
remain accepted, but new scripts should use the names documented above.
|
|
164
|
+
|
|
129
165
|
## Authentication and Security Boundaries
|
|
130
166
|
|
|
131
167
|
- **Authentication Baseline**: Access requires authorized SSH keys in `~/.ssh/authorized_keys` or a high-entropy secret created in the runtime directory.
|
|
132
|
-
- **
|
|
133
|
-
- **Upterm Transport**: Relies on authorized SSH public keys or `--
|
|
168
|
+
- **Public access (`--public-access`)**: Bypasses authentication checks entirely; any incoming connection with network access is accepted.
|
|
169
|
+
- **Upterm Transport**: Relies on authorized SSH public keys or `--public-access` (Upterm does not support password authentication). The relay itself is trusted for inbound multiplexing.
|
|
134
170
|
- **Filesystem Confinement**: SFTP and SCP access cannot escape the designated workspace directory.
|
|
135
|
-
- **Host Transfer Mode (default)**: Relative SFTP and SCP paths begin in the workspace directory, while absolute paths refer to host filesystem locations subject to OS user permissions. Pass `--
|
|
171
|
+
- **Host Transfer Mode (default)**: Relative SFTP and SCP paths begin in the workspace directory, while absolute paths refer to host filesystem locations subject to OS user permissions. Pass `--restrict-file-transfer` to limit transfers to the workspace namespace and reject symlink escapes.
|
|
136
172
|
- **Interactive Shell Privileges**: Command and terminal sessions execute with the permissions of the host account rather than inside an OS chroot.
|
|
137
173
|
- **State Storage**: Daemon PIDs and logs are maintained in `gateway.pid`, `transport.pid`, `gateway.log`, and `transport.log`, with session records kept in `manifest.json` and `settings.json`.
|
|
138
174
|
|
|
@@ -140,7 +176,7 @@ nullgate upgrade --version 1.0.1
|
|
|
140
176
|
|
|
141
177
|
| Variable | Description |
|
|
142
178
|
|---|---|
|
|
143
|
-
| `NULLGATE_PASSWORD` | Gateway-process credential; normally managed internally by `nullgate
|
|
179
|
+
| `NULLGATE_PASSWORD` | Gateway-process credential; normally managed internally by `nullgate start` |
|
|
144
180
|
| `NULLGATE_STATE_DIR` | Directory holding runtime state and logs (defaults to `~/.local/state/nullgate`) |
|
|
145
181
|
| `NULLGATE_IDENTITY_SEED` | Seed value used for deterministic Ed25519 key derivation |
|
|
146
182
|
| `NULLGATE_TUNNEL_HOST` | Host address for srv.us ingress (default: `srv.us`) |
|
|
@@ -2,11 +2,37 @@
|
|
|
2
2
|
|
|
3
3
|
Disposable SSH gateway into confined directory workspaces for remote containers, ephemeral developer boxes, CI nodes, and cloud instances.
|
|
4
4
|
|
|
5
|
+
## Quick start
|
|
6
|
+
|
|
7
|
+
Run Nullgate directly from PyPI without installing it first:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# Share the current directory over the default Upterm provider
|
|
11
|
+
uvx nullgate start
|
|
12
|
+
|
|
13
|
+
# Share a specific workspace
|
|
14
|
+
uvx nullgate start /workspace
|
|
15
|
+
|
|
16
|
+
# Compact form: workspace, SSH port, srv.us slot, provider
|
|
17
|
+
uvx nullgate start /workspace 8022 1 -t srvus
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Then check the endpoint and print the SSH connection setup:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
uvx nullgate status
|
|
24
|
+
uvx nullgate connect
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Nullgate uses authorized SSH keys by default. Upterm needs a key in
|
|
28
|
+
`~/.ssh/authorized_keys`; use `--public-access` only for an intentionally public
|
|
29
|
+
temporary endpoint.
|
|
30
|
+
|
|
5
31
|
## Features
|
|
6
32
|
|
|
7
33
|
- **Ingress Transports**: Publish endpoints via Upterm WebSocket relays (default), srv.us reverse tunnels, Cloudflare Worker relays, or Cloudflare Argo (`cloudflared`) tunnels.
|
|
8
34
|
- **Confinement**: SFTP and SCP workloads are restricted to the designated workspace root; parent traversals and out-of-root symlinks are blocked.
|
|
9
|
-
- **Host file visibility**: Relative transfer paths begin in the workspace root while absolute paths address host locations, matching shell behavior; add `--
|
|
35
|
+
- **Host file visibility**: Relative transfer paths begin in the workspace root while absolute paths address host locations, matching shell behavior; add `--restrict-file-transfer` for strict workspace-only transfers.
|
|
10
36
|
- **Direct Shell Access**: Authenticated PTY sessions run interactively under the local user credentials.
|
|
11
37
|
- **Synthetic UID Resilience**: Runs seamlessly in container environments missing user records in `/etc/passwd`.
|
|
12
38
|
- **Persistent Key Identities**: Host keys and identity seeds persist across restarts rather than being silently regenerated.
|
|
@@ -32,77 +58,82 @@ pip install nullgate
|
|
|
32
58
|
|
|
33
59
|
## Usage
|
|
34
60
|
|
|
35
|
-
###
|
|
61
|
+
### Start
|
|
36
62
|
|
|
37
63
|
Launch the gateway service and ingress transport in the background:
|
|
38
64
|
|
|
39
65
|
```bash
|
|
40
66
|
# Default Upterm transport on port 4822 sharing current directory
|
|
41
|
-
nullgate
|
|
67
|
+
nullgate start
|
|
42
68
|
|
|
43
69
|
# Custom path, port, and slot
|
|
44
|
-
nullgate
|
|
70
|
+
nullgate start /workspace --ssh-port 4822 --tunnel-slot 1
|
|
71
|
+
|
|
72
|
+
# The same values in compact positional form
|
|
73
|
+
nullgate start /workspace 4822 1
|
|
45
74
|
|
|
46
75
|
# Select specific transport provider
|
|
47
|
-
nullgate
|
|
48
|
-
nullgate
|
|
49
|
-
nullgate
|
|
50
|
-
nullgate
|
|
76
|
+
nullgate start . --provider upterm
|
|
77
|
+
nullgate start . --provider srvus
|
|
78
|
+
nullgate start . --provider cloudflare --relay-url https://relay.example.com
|
|
79
|
+
nullgate start . --provider cloudflared --public-hostname workspace.example.com --cloudflare-token "$TUNNEL_TOKEN"
|
|
51
80
|
```
|
|
52
81
|
|
|
53
82
|
Options:
|
|
54
|
-
- `--
|
|
55
|
-
- `--
|
|
56
|
-
- `--
|
|
57
|
-
- `--
|
|
58
|
-
- `--
|
|
59
|
-
- `--
|
|
60
|
-
- `--
|
|
61
|
-
- `--no-
|
|
62
|
-
- `--
|
|
63
|
-
- `--no-
|
|
64
|
-
|
|
65
|
-
|
|
83
|
+
- `-p`, `--ssh-port`: Local SSH server port.
|
|
84
|
+
- `-s`, `--tunnel-slot`: srv.us tunnel slot.
|
|
85
|
+
- `-t`, `--provider`: Public connection provider (`upterm`, `srvus`, `cloudflare`, or `cloudflared`). Defaults to `upterm`.
|
|
86
|
+
- `-e`, `--relay-url`: WebSocket relay URL used with `upterm` or `cloudflare`.
|
|
87
|
+
- `-H`, `--public-hostname`: Public hostname used with `cloudflared`.
|
|
88
|
+
- `--cloudflare-token`: Cloudflare tunnel credential, or set `NULLGATE_CLOUDFLARED_TOKEN`.
|
|
89
|
+
- `--public-access`: Allow connections without credentials. This is unsafe on public endpoints.
|
|
90
|
+
- `--no-public-access`: Require authentication.
|
|
91
|
+
- `--tcp-forwarding`: Enable SSH TCP tunneling (`ssh -L`).
|
|
92
|
+
- `--no-tcp-forwarding`: Disable SSH TCP tunneling.
|
|
93
|
+
- `--restrict-file-transfer`: Keep SFTP and SCP inside the workspace.
|
|
94
|
+
- `--no-restrict-file-transfer`: Allow host-visible file transfers.
|
|
95
|
+
|
|
96
|
+
### Status
|
|
66
97
|
|
|
67
98
|
Display operational status, running PIDs, connection URLs, and security settings:
|
|
68
99
|
|
|
69
100
|
```bash
|
|
70
|
-
nullgate
|
|
101
|
+
nullgate status
|
|
71
102
|
```
|
|
72
103
|
|
|
73
|
-
###
|
|
104
|
+
### Connect
|
|
74
105
|
|
|
75
106
|
Print client connection strings and OpenSSH client configuration directives:
|
|
76
107
|
|
|
77
108
|
```bash
|
|
78
|
-
nullgate
|
|
109
|
+
nullgate connect
|
|
79
110
|
```
|
|
80
111
|
|
|
81
|
-
###
|
|
112
|
+
### Logs
|
|
82
113
|
|
|
83
114
|
Stream or print daemon logs:
|
|
84
115
|
|
|
85
116
|
```bash
|
|
86
|
-
nullgate
|
|
87
|
-
nullgate
|
|
88
|
-
nullgate
|
|
89
|
-
nullgate
|
|
117
|
+
nullgate logs
|
|
118
|
+
nullgate logs -f
|
|
119
|
+
nullgate logs gateway
|
|
120
|
+
nullgate logs transport
|
|
90
121
|
```
|
|
91
122
|
|
|
92
|
-
###
|
|
123
|
+
### Stop
|
|
93
124
|
|
|
94
125
|
Terminate running gateway and transport background processes:
|
|
95
126
|
|
|
96
127
|
```bash
|
|
97
|
-
nullgate
|
|
128
|
+
nullgate stop
|
|
98
129
|
```
|
|
99
130
|
|
|
100
|
-
###
|
|
131
|
+
### Restart
|
|
101
132
|
|
|
102
133
|
Restart daemon instances reusing active or saved workspace settings:
|
|
103
134
|
|
|
104
135
|
```bash
|
|
105
|
-
nullgate
|
|
136
|
+
nullgate restart
|
|
106
137
|
```
|
|
107
138
|
|
|
108
139
|
### Upgrade
|
|
@@ -114,13 +145,18 @@ nullgate upgrade
|
|
|
114
145
|
nullgate upgrade --version 1.0.1
|
|
115
146
|
```
|
|
116
147
|
|
|
148
|
+
Short command aliases are `up`, `down`, `ps`, `ssh`, `log`, and `reload`.
|
|
149
|
+
The earlier names `open`, `shut`, `inspect`, `enter`, `trace`, and `cycle`
|
|
150
|
+
remain available as compatibility aliases. Their earlier option names also
|
|
151
|
+
remain accepted, but new scripts should use the names documented above.
|
|
152
|
+
|
|
117
153
|
## Authentication and Security Boundaries
|
|
118
154
|
|
|
119
155
|
- **Authentication Baseline**: Access requires authorized SSH keys in `~/.ssh/authorized_keys` or a high-entropy secret created in the runtime directory.
|
|
120
|
-
- **
|
|
121
|
-
- **Upterm Transport**: Relies on authorized SSH public keys or `--
|
|
156
|
+
- **Public access (`--public-access`)**: Bypasses authentication checks entirely; any incoming connection with network access is accepted.
|
|
157
|
+
- **Upterm Transport**: Relies on authorized SSH public keys or `--public-access` (Upterm does not support password authentication). The relay itself is trusted for inbound multiplexing.
|
|
122
158
|
- **Filesystem Confinement**: SFTP and SCP access cannot escape the designated workspace directory.
|
|
123
|
-
- **Host Transfer Mode (default)**: Relative SFTP and SCP paths begin in the workspace directory, while absolute paths refer to host filesystem locations subject to OS user permissions. Pass `--
|
|
159
|
+
- **Host Transfer Mode (default)**: Relative SFTP and SCP paths begin in the workspace directory, while absolute paths refer to host filesystem locations subject to OS user permissions. Pass `--restrict-file-transfer` to limit transfers to the workspace namespace and reject symlink escapes.
|
|
124
160
|
- **Interactive Shell Privileges**: Command and terminal sessions execute with the permissions of the host account rather than inside an OS chroot.
|
|
125
161
|
- **State Storage**: Daemon PIDs and logs are maintained in `gateway.pid`, `transport.pid`, `gateway.log`, and `transport.log`, with session records kept in `manifest.json` and `settings.json`.
|
|
126
162
|
|
|
@@ -128,7 +164,7 @@ nullgate upgrade --version 1.0.1
|
|
|
128
164
|
|
|
129
165
|
| Variable | Description |
|
|
130
166
|
|---|---|
|
|
131
|
-
| `NULLGATE_PASSWORD` | Gateway-process credential; normally managed internally by `nullgate
|
|
167
|
+
| `NULLGATE_PASSWORD` | Gateway-process credential; normally managed internally by `nullgate start` |
|
|
132
168
|
| `NULLGATE_STATE_DIR` | Directory holding runtime state and logs (defaults to `~/.local/state/nullgate`) |
|
|
133
169
|
| `NULLGATE_IDENTITY_SEED` | Seed value used for deterministic Ed25519 key derivation |
|
|
134
170
|
| `NULLGATE_TUNNEL_HOST` | Host address for srv.us ingress (default: `srv.us`) |
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.2.1"
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
"""Command-line parser and entry point dispatch for Nullgate."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import sys
|
|
7
|
+
from typing import Sequence
|
|
8
|
+
|
|
9
|
+
from nullgate import session, transports
|
|
10
|
+
|
|
11
|
+
SUBCOMMANDS = (
|
|
12
|
+
"start",
|
|
13
|
+
"up",
|
|
14
|
+
"stop",
|
|
15
|
+
"down",
|
|
16
|
+
"status",
|
|
17
|
+
"ps",
|
|
18
|
+
"connect",
|
|
19
|
+
"ssh",
|
|
20
|
+
"logs",
|
|
21
|
+
"log",
|
|
22
|
+
"restart",
|
|
23
|
+
"reload",
|
|
24
|
+
"upgrade",
|
|
25
|
+
"open",
|
|
26
|
+
"shut",
|
|
27
|
+
"inspect",
|
|
28
|
+
"enter",
|
|
29
|
+
"trace",
|
|
30
|
+
"cycle",
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
35
|
+
parser = argparse.ArgumentParser(
|
|
36
|
+
prog="nullgate",
|
|
37
|
+
description="Disposable SSH gateway into confined directory workspaces.",
|
|
38
|
+
)
|
|
39
|
+
parser.add_argument(
|
|
40
|
+
"-V",
|
|
41
|
+
"--version",
|
|
42
|
+
action="version",
|
|
43
|
+
version=f"nullgate {session._version()}",
|
|
44
|
+
)
|
|
45
|
+
sub = parser.add_subparsers(dest="command", required=True, metavar="command")
|
|
46
|
+
|
|
47
|
+
start_parser = sub.add_parser(
|
|
48
|
+
"start",
|
|
49
|
+
aliases=["up", "open"],
|
|
50
|
+
description="Start SSH access to a workspace.",
|
|
51
|
+
help="Start SSH access to a workspace (aliases: up, open).",
|
|
52
|
+
)
|
|
53
|
+
start_parser.add_argument(
|
|
54
|
+
"workspace",
|
|
55
|
+
nargs="?",
|
|
56
|
+
help="Directory to make available (default: current directory).",
|
|
57
|
+
)
|
|
58
|
+
start_parser.add_argument(
|
|
59
|
+
"port_arg",
|
|
60
|
+
nargs="?",
|
|
61
|
+
type=int,
|
|
62
|
+
help="Local SSH server port (shorthand positional).",
|
|
63
|
+
)
|
|
64
|
+
start_parser.add_argument(
|
|
65
|
+
"slot_arg",
|
|
66
|
+
nargs="?",
|
|
67
|
+
type=int,
|
|
68
|
+
help="srv.us tunnel slot (shorthand positional).",
|
|
69
|
+
)
|
|
70
|
+
start_parser.add_argument(
|
|
71
|
+
"-p",
|
|
72
|
+
"--ssh-port",
|
|
73
|
+
dest="port",
|
|
74
|
+
type=int,
|
|
75
|
+
default=None,
|
|
76
|
+
help=f"Local SSH server port (default: {session.DEFAULT_PORT}).",
|
|
77
|
+
)
|
|
78
|
+
start_parser.add_argument("--port", dest="port", type=int, help=argparse.SUPPRESS)
|
|
79
|
+
start_parser.add_argument(
|
|
80
|
+
"-s",
|
|
81
|
+
"--tunnel-slot",
|
|
82
|
+
dest="slot",
|
|
83
|
+
type=int,
|
|
84
|
+
default=None,
|
|
85
|
+
help=f"srv.us tunnel slot (default: {session.DEFAULT_SLOT}).",
|
|
86
|
+
)
|
|
87
|
+
start_parser.add_argument("--slot", dest="slot", type=int, help=argparse.SUPPRESS)
|
|
88
|
+
start_parser.add_argument(
|
|
89
|
+
"-t",
|
|
90
|
+
"--provider",
|
|
91
|
+
dest="transport",
|
|
92
|
+
choices=transports.VALID_TRANSPORTS,
|
|
93
|
+
default=None,
|
|
94
|
+
help=f"Public connection provider (default: {transports.DEFAULT_TRANSPORT}).",
|
|
95
|
+
)
|
|
96
|
+
start_parser.add_argument(
|
|
97
|
+
"--transport",
|
|
98
|
+
dest="transport",
|
|
99
|
+
choices=transports.VALID_TRANSPORTS,
|
|
100
|
+
help=argparse.SUPPRESS,
|
|
101
|
+
)
|
|
102
|
+
start_parser.add_argument(
|
|
103
|
+
"--public-access",
|
|
104
|
+
dest="accept",
|
|
105
|
+
action=argparse.BooleanOptionalAction,
|
|
106
|
+
default=None,
|
|
107
|
+
help="Allow connections without SSH credentials (unsafe on public endpoints).",
|
|
108
|
+
)
|
|
109
|
+
start_parser.add_argument(
|
|
110
|
+
"--accept",
|
|
111
|
+
dest="accept",
|
|
112
|
+
action=argparse.BooleanOptionalAction,
|
|
113
|
+
default=None,
|
|
114
|
+
help=argparse.SUPPRESS,
|
|
115
|
+
)
|
|
116
|
+
start_parser.add_argument(
|
|
117
|
+
"--tcp-forwarding",
|
|
118
|
+
dest="allow_tcp_forwarding",
|
|
119
|
+
action=argparse.BooleanOptionalAction,
|
|
120
|
+
default=None,
|
|
121
|
+
help="Allow SSH port forwarding.",
|
|
122
|
+
)
|
|
123
|
+
start_parser.add_argument(
|
|
124
|
+
"--allow-tcp-forwarding",
|
|
125
|
+
dest="allow_tcp_forwarding",
|
|
126
|
+
action=argparse.BooleanOptionalAction,
|
|
127
|
+
default=None,
|
|
128
|
+
help=argparse.SUPPRESS,
|
|
129
|
+
)
|
|
130
|
+
start_parser.add_argument(
|
|
131
|
+
"--restrict-file-transfer",
|
|
132
|
+
dest="confine_sftp",
|
|
133
|
+
action=argparse.BooleanOptionalAction,
|
|
134
|
+
default=None,
|
|
135
|
+
help="Keep SFTP and SCP transfers inside the workspace.",
|
|
136
|
+
)
|
|
137
|
+
start_parser.add_argument(
|
|
138
|
+
"--confine-sftp",
|
|
139
|
+
dest="confine_sftp",
|
|
140
|
+
action=argparse.BooleanOptionalAction,
|
|
141
|
+
default=None,
|
|
142
|
+
help=argparse.SUPPRESS,
|
|
143
|
+
)
|
|
144
|
+
start_parser.add_argument(
|
|
145
|
+
"-e",
|
|
146
|
+
"--relay-url",
|
|
147
|
+
dest="endpoint",
|
|
148
|
+
help="WebSocket relay URL (for upterm or cloudflare providers).",
|
|
149
|
+
)
|
|
150
|
+
start_parser.add_argument("--endpoint", dest="endpoint", help=argparse.SUPPRESS)
|
|
151
|
+
start_parser.add_argument(
|
|
152
|
+
"-H",
|
|
153
|
+
"--public-hostname",
|
|
154
|
+
dest="hostname",
|
|
155
|
+
help="Public hostname (for the cloudflared provider).",
|
|
156
|
+
)
|
|
157
|
+
start_parser.add_argument("--hostname", dest="hostname", help=argparse.SUPPRESS)
|
|
158
|
+
start_parser.add_argument(
|
|
159
|
+
"--cloudflare-token",
|
|
160
|
+
dest="token",
|
|
161
|
+
help="Cloudflare tunnel token (for the cloudflared provider).",
|
|
162
|
+
)
|
|
163
|
+
start_parser.add_argument("--token", dest="token", help=argparse.SUPPRESS)
|
|
164
|
+
|
|
165
|
+
sub.add_parser(
|
|
166
|
+
"stop",
|
|
167
|
+
aliases=["down", "shut"],
|
|
168
|
+
description="Stop SSH access and its connection provider.",
|
|
169
|
+
help="Stop SSH access (aliases: down, shut).",
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
sub.add_parser(
|
|
173
|
+
"status",
|
|
174
|
+
aliases=["ps", "inspect"],
|
|
175
|
+
description="Show service health, connection details, and security settings.",
|
|
176
|
+
help="Show current status (aliases: ps, inspect).",
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
sub.add_parser(
|
|
180
|
+
"connect",
|
|
181
|
+
aliases=["ssh", "enter"],
|
|
182
|
+
description="Show how to connect with SSH.",
|
|
183
|
+
help="Show SSH connection instructions (aliases: ssh, enter).",
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
logs_parser = sub.add_parser(
|
|
187
|
+
"logs",
|
|
188
|
+
aliases=["log", "trace"],
|
|
189
|
+
description="View or follow service logs.",
|
|
190
|
+
help="View or follow service logs (aliases: log, trace).",
|
|
191
|
+
)
|
|
192
|
+
logs_parser.add_argument(
|
|
193
|
+
"target",
|
|
194
|
+
nargs="?",
|
|
195
|
+
default="all",
|
|
196
|
+
choices=["all", "gateway", "transport"],
|
|
197
|
+
help="Target log stream to inspect (default all).",
|
|
198
|
+
)
|
|
199
|
+
logs_parser.add_argument(
|
|
200
|
+
"-f",
|
|
201
|
+
"--follow",
|
|
202
|
+
dest="f",
|
|
203
|
+
action="store_true",
|
|
204
|
+
help="Follow log stream output continuously.",
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
sub.add_parser(
|
|
208
|
+
"restart",
|
|
209
|
+
aliases=["reload", "cycle"],
|
|
210
|
+
description="Restart SSH access using the saved settings.",
|
|
211
|
+
help="Restart using saved settings (aliases: reload, cycle).",
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
upgrade_parser = sub.add_parser(
|
|
215
|
+
"upgrade",
|
|
216
|
+
description="Fetch and install latest Nullgate release.",
|
|
217
|
+
help="Fetch and install latest Nullgate release.",
|
|
218
|
+
)
|
|
219
|
+
upgrade_parser.add_argument(
|
|
220
|
+
"--version",
|
|
221
|
+
help="Install a specific published version instead of the latest stable release.",
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
proxy_parser = sub.add_parser(
|
|
225
|
+
"proxy",
|
|
226
|
+
description=argparse.SUPPRESS,
|
|
227
|
+
)
|
|
228
|
+
proxy_parser.add_argument("url")
|
|
229
|
+
|
|
230
|
+
upterm_proxy = sub.add_parser(
|
|
231
|
+
"upterm-proxy",
|
|
232
|
+
description=argparse.SUPPRESS,
|
|
233
|
+
)
|
|
234
|
+
upterm_proxy.add_argument("url")
|
|
235
|
+
|
|
236
|
+
return parser
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def main(argv: Sequence[str] | None = None) -> int:
|
|
240
|
+
parser = build_parser()
|
|
241
|
+
args = parser.parse_args(argv)
|
|
242
|
+
|
|
243
|
+
handlers = {
|
|
244
|
+
"start": session.cmd_open,
|
|
245
|
+
"up": session.cmd_open,
|
|
246
|
+
"open": session.cmd_open,
|
|
247
|
+
"stop": session.cmd_shut,
|
|
248
|
+
"down": session.cmd_shut,
|
|
249
|
+
"shut": session.cmd_shut,
|
|
250
|
+
"status": session.cmd_inspect,
|
|
251
|
+
"ps": session.cmd_inspect,
|
|
252
|
+
"inspect": session.cmd_inspect,
|
|
253
|
+
"connect": session.cmd_enter,
|
|
254
|
+
"ssh": session.cmd_enter,
|
|
255
|
+
"enter": session.cmd_enter,
|
|
256
|
+
"logs": session.cmd_trace,
|
|
257
|
+
"log": session.cmd_trace,
|
|
258
|
+
"trace": session.cmd_trace,
|
|
259
|
+
"restart": session.cmd_cycle,
|
|
260
|
+
"reload": session.cmd_cycle,
|
|
261
|
+
"cycle": session.cmd_cycle,
|
|
262
|
+
"upgrade": session.cmd_upgrade,
|
|
263
|
+
"proxy": transports.cmd_proxy,
|
|
264
|
+
"upterm-proxy": transports.cmd_upterm_proxy,
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
handler = handlers.get(args.command)
|
|
268
|
+
if handler is None:
|
|
269
|
+
parser.print_help(sys.stderr)
|
|
270
|
+
return 2
|
|
271
|
+
return handler(args)
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
if __name__ == "__main__":
|
|
275
|
+
sys.exit(main())
|