summaryrefslogtreecommitdiffstats
path: root/applications/luci-app-dockerman/README.md
blob: 6a53eb2cc4e70a9272b93e98dcc29c8bea51eff1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
# Dockerman JS

## Notice

After dockerd _v27_, docker will **remove** the ability to listen on sockets of the form

`xxx://x.x.x.x:2375` or `xxx://x.x.x.x:2376` (or `xxx://[2001:db8::1]:2375`)

unless you run the daemon with various `--tls*` flags. That is, dockerd will *refuse*
to start unless it is configured to use TLS. See
[here](https://docs.docker.com/engine/security/#docker-daemon-attack-surface)
[here](https://docs.docker.com/engine/deprecated/#unauthenticated-tcp-connections)
and [here](https://docs.docker.com/engine/security/protect-access/).

ucode is not yet capable of TLS, so if you want dockerd to listen on a port,
you have a few options.

Issues opened in the luci repo regarding connection setup will go unanswered.
DIY.

This implementation includes three methods to connect to the API. 


# API Availability


|                  | rpcd/CGI | (Proxy+)JS API | Controller |
|------------------|----------|----------------|------------|
| API              |    ✅    |        ✅      |      ✅    |
| File Stream      |    ❌    |        ✅      |      ✅    |
| Console Start    |    ✅    |        ❌      |      ❌    |
| WS Console       |    ❌    |        ✅      |      ❌    |
| Stream endpoints |    ❌    |        ✅      |      ✅    |

* Stream endpoints are docker API paths that continue to stream data, like logs

Dockerman uses a combination of rpcd and ucode Controller so API, Console via
ttyd and File Streaming operations are available. dockerd is configured by
default to use `unix:///var/run/docker.sock`, and is secure this way.


It is possible to configure dockerd to listen on e.g.:

`['unix:///var/run/docker.sock', 'tcp://0.0.0.0:2375']`

when you have a Reverse Proxy configured and to open up the JS API.

## Reverse Proxy

Use nginx or Caddy to proxy connections to dockerd which is configured with
`--tls*` flags, or communicates directly with `unix:///var/run/docker.sock`,
which adds the necessary `Access-Control-Allow-Origin: ...`
headers for browser clients. You might even be able to run a
docker container that does this. If you don't want to set a proxy up, use a
[browser plugin](#browser-plug-in).

https://github.com/lucaslorentz/caddy-docker-proxy
https://github.com/Tecnativa/docker-socket-proxy

## LuCI

Included is a ucode rpc API interface to talk with the docker socket, so all
API calls are sent via rpcd, and appear as POST calls in your front end at e.g.

http://192.168.1.1/cgi-bin/luci


All calls to the docker API are authenticated with your session login.

### Controller

Included also is a ucode based controller to forward requests more directly to
the docker API socket to avoid the rpc penalty, and stream file uploads and
downloads. These are still authenticated with your session login. The methods
to reach the controller API are defined in the menu JSON file. The controller
API interface only exposes a limited subset of API methods.


## JS API

A JS API is included in the front-end to connect to API endpoints, and it
will detect how dockerd is configured. If dockerd is configured with any

`xxx://x.x.x.x:2375` or `xxx://x.x.x.x:2376` (or `xxx://[2001:db8::1]:2375`)

the front end will attempt to connect using the JS API. More features are
available with a more direct connection to the API (via Proxy or using 
[browser plugin](#browser-plug-in)), like WebSockets to connect to container
terminals. WebSocket connections are not currently available in LuCI, or the
LuCI CGI proxy.

CGI's job is to parse the request, send the response and disconnect.


## Browser plug-in

To avoid setting up a Proxy, and attempt to communicate directly with the API
endpoint, whether or not configured with `-tls*` options, you can use a plug-in.
One which overrides (the absence of) `Access-Control-Allow-Origin` CORS headers
(dockerd does not add these headers).
For example:

https://addons.mozilla.org/en-US/firefox/addon/cors-everywhere/

https://addons.mozilla.org/en-US/firefox/addon/access-control-allow-origin/

https://addons.mozilla.org/en-US/firefox/addon/cors-unblock/

https://addons.mozilla.org/en-US/firefox/addon/cross-domain-cors/


The browser plug-in does not magically fix TLS problems when you have mTLS
configured on dockerd (mutual CA based certificate authentication).


# Architecture

## High-Level Architecture

### rpcd and controller
```
┌──────────────────────────────────────────────────────────────────┐
│                         OpenWrt/LuCI                             │
│                                                                  │
│  ┌─────────────────────┐                                         │
│  │   Browser / UI      │                                         │
│  │  containers.js      │                                         │
│  │  images.js          │                                         │
│  └──────────┬──────────┘                                         │
│             │                                                    │
│             │ 1. GET /admin/docker/container/inspect/id?x=y      │
│             V                                                    │
│  ┌──────────────────────────┐                                    │
│  │  LuCI Dispatcher         │                                    │
│  │  (dispatcher.uc)         │                                    │
│  │  - Parses URL path       │                                    │
│  │  - Looks up action       │                                    │
│  │  - Extracts query params │                                    │
│  └──────────┬───────────────┘                                    │
│             │                                                    │
│             │ 2. Call controller function(env)                   │
│             V                                                    │
│  ┌──────────────────────────┐                                    │
│  │  HTTP Controller         │                                    │
│  │  (docker.uc)             │                                    │
│  │  - container_inspect(env)│                                    │
│  │  - Gets params from env  │                                    │
│  │  - Creates socket        │                                    │
│  └──────────┬───────────────┘                                    │
│             │                                                    │
│             │ 3. Connect to Docker socket                        │
│             V                                                    │
│  ┌──────────────────────────┐                                    │
│  │  Docker Socket           │                                    │
│  │  /var/run/docker.sock    │                                    │
│  │  (AF_UNIX socket)        │                                    │
│  └──────────┬───────────────┘                                    │
│             │                                                    │
│             │ 4. HTTP GET /v1.47/containers/{id}/json            │
│             V                                                    │
│  ┌──────────────────────────┐                                    │
│  │  Docker Daemon 200 OK    │                                    │
│  │  - Creates JSON blob     │                                    │
│  │  - Streams binary data   │                                    │
│  └──────────┬───────────────┘                                    │
│             │                                                    │
│             │ 5. data chunks (32KB blocks)                       │
│             V                                                    │
│  ┌──────────────────────────┐                                    │
│  │  UHTTPd Web Server       │                                    │
│  │  - Receives chunks       │                                    │
│  │  - Writes to HTTP socket │                                    │
│  │  (no buffering)          │                                    │
│  └──────────┬───────────────┘                                    │
│             │                                                    │
│             │ 6. HTTP 200 + data stream                          │
│             V                                                    │
│  ┌──────────────────────────┐                                    │
│  │   Browser                │                                    │
│  │  - Receives data stream  │                                    │
│  │  - Processes response    │                                    │
│  │  - Displays result       │                                    │
│  └──────────────────────────┘                                    │
│                                                                  │
└──────────────────────────────────────────────────────────────────┘
```

## Request/Response Flow

### Container Export Flow

```
Browser               Ucode Controller     Docker
  │                      │                 │
  ├─ GET /admin/docker   │                 │
  │   /container/export  │                 │
  │   /{id}?abc123 ─────>│                 │
  │                      ├─ Get param 'id' │
  │                      │ from env.http   │
  │                      │                 │
  │                      ├─ Create socket  │
  │                      │                 │
  │                      ├─ Connect to     │
  │                      │ /var/run/       │
  │                      │ docker.sock ────>
  │                      │                 │
  │                      │ <─ HTTP 200 OK  │
  │                      │                 │
  │                      │ <─ tar chunk 1  │
  │                      │ <─ tar chunk 2  │
  │ <─ HTTP 200 OK ──────│ <─ tar chunk 3  │
  │ <─ tar chunk 1 ──────│ <─ ...          │
  │ <─ tar chunk 2 ──────│ <─ EOF          │
  │ <─ ...               │                 │
  │                      │                 │
  ├─ Done                │                 │
  │                      ├─ Close socket   │
  │                      │                 │
```


## Socket Connection Details

```
┌──────────────────────────────────────┐
│     UHTTPd (Web Server)              │
│  [Controller Process]                │
└─────────────┬────────────────────────┘
              │
              │ AF_UNIX socket
              │ (named pipe)
              V
┌──────────────────────────────────────┐
│     Docker Daemon                    │
│  /var/run/docker.sock                │
└─────────────┬────────────────────────┘
              │
              │ HTTP Protocol
              │ (over socket)
              V
      Docker API Engine
      - Creates export tar
      - Sends as chunked stream
```