aboutsummaryrefslogtreecommitdiff
path: root/README.md
blob: 719beac879d3b8494906bdf86eeccfe5587d4429 (plain) (blame)
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
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
# slcl, a simple and lightweight cloud

`slcl` is a simple and lightweight implementation of a web file server,
commonly known as "cloud storage" or simply "cloud", written in C99 plus
POSIX.1-2008 extensions.

## Screenshots

![Screenshot of slcl's login page](doc/login.png)

![Screenshot of an example user directory](doc/user.png)

## Disclaimer

Intentionally, `slcl` does not share some of the philosophical views from the
[suckless project](https://suckless.org). However, it still strives towards
portability, minimalism, simplicity and efficiency.

## Features

- Private access directory with file uploading, with configurable quota.
- Read-only public file sharing.
- Download directories as `.zip` files.
- Uses [`libweb`](https://gitea.privatedns.org/xavi/libweb), a tiny web framework.
- A simple JSON file as the credentials database.
- No JavaScript.
- Thumbnail generation can be optionally provided via a
[separate application](thumbnail/README-md) and enabled by `slcl` via a command
line option. Inter-process communication is achieved via a named pipe.

### TLS

In order to maintain simplicity and reduce the risk for security bugs, `slcl`
does **not** implement TLS support. Instead, this should be provided by a
reverse proxy, such as [`caddy`](https://caddyserver.com/).

### Root permissions

`slcl` is expected to listen to connections from any port number so that `root`
access is not required. So, in order to avoid the risk for security bugs,
**please do not run `slcl` as `root`**.

### Encryption

Since no client-side JavaScript is used, files are **uploaded unencrypted**
to `slcl`. If required, encryption should be done before uploading e.g.: using
[`gpg`](https://gnupg.org/).

## Requirements

- A POSIX environment.
- [`libsodium`](https://www.libsodium.org/).
- cJSON >= 1.7.15.
- ZLIB.
- [`dynstr`](https://gitea.privatedns.org/xavi/dynstr)
(provided as a `git` submodule by `libweb`).
- [`libweb`](https://gitea.privatedns.org/xavi/libweb)
(provided as a `git` submodule).
- [`fdzipstream`](https://github.com/CTrabant/fdzipstream.git)
(provided as a `git` submodule).
- ImageMagick >= 6.0 (optional, for [`thumbnail`](thumbnail/) only).
- CMake (optional).

### Ubuntu / Debian

#### Mandatory packages

```sh
sudo apt install build-essential libcjson-dev libsodium-dev zlib1g-dev
```

#### Optional packages

```sh
sudo apt install cmake libmagickcore-6.q16-dev
```

### Alpine Linux

#### Mandatory packages

```sh
apk add make gcc musl-dev libsodium-dev cjson-dev zlib-dev
```

#### Optional packages

```sh
apk add cmake
```

Unfortunately, [ImageMagick6](https://legacy.imagemagick.org/), which is
required by the [thumbnail generation tool](thumbnail/), might not be available
from Alpine Linux's repositories.

## How to use
### Build

Two build environments are provided for `slcl` - feel free to choose any of
them:

- A [`configure`](configure) POSIX shell and mostly POSIX-compliant
[`Makefile`](Makefile).
- A [`CMakeLists.txt`](CMakeLists.txt).

`slcl` can be built using the standard build process:

#### Make

```sh
$ ./configure
$ make
```

To enable the thumbnail generation program:

```sh
$ ./configure --thumbnail
$ make
```

#### CMake

```sh
$ cmake -B build
$ cmake --build build/
```

To enable the thumbnail generation program:

```sh
$ cmake -B build -DTHUMBNAIL=ON
$ cmake --build build/
```

### Setting up

`slcl` consumes a path to a directory with the following tree structure:

```
.
├── db.json
├── public
└── user
```

Where:

- `db.json` is the credentials database. Details are explained below.
    - **Note:** `slcl` creates a database with no users if not found, with
    file mode bits set to `0600`.
- `public` is a directory containing read-only files that can be accessed
without authentication. Internally, they are implemented as simlinks to
other files.
    - **Note:** `slcl` creates this directory if it does not exist, with
    directory mode bits set to `0700`.
- `user` is a directory containing user directories, which in turn contain
anything users put into them.
    - **Note:** `slcl` creates this directory if it does not exist, with
    directory mode bits set to `0700`.

**Note:** `slcl` creates the given directory if it does not exist.

[Generated thumbnails](thumbnail/README.md) are stored into another directory,
namely `thumbnails`, which is automatically created with directory mode bits
set to `0700`.

```
.
├── db.json
├── public
├── thumbnails
└── user
```

A more complete example:

```
.
├── db.json
├── public
│   └── 44e03ab1bc3b0eff1567c76619186596 -> user/alice/file.jpg
├── thumbnails
│   ├── alice
│   │   └── file.jpg
│   └── john
└── user
    ├── alice
    │   └── file.jpg
    └── john
        └── file2.txt
```

### Credentials database

`slcl` reads credentials from the `db.json` database, with the following
schema:

```json
{
    "users": [{
        "name":	"...",
        "password":	"...",
        "salt":	"...",
        "key":	"...",
        "quota": "..."
    }]
}
```

[`usergen`](usergen) is an interactive program that consumes a directory,
a username, a password and, optionally, a user quota in MiB. A salt is
randomly generated using `openssl` and passwords are hashed multiple times
beforehand - see [`usergen`](usergen) and [`auth.c`](auth.c) for further
reference. Also, a random key is generated that is later used to sign HTTP
cookies.

Then, [`usergen`](usergen) appends a JSON object to the `users` JSON array in
the `db.json` file located inside the given directory. Also,
[`usergen`](usergen) creates the user directory inside the `user/` directory.

When users authenticate from a web browser, `slcl` sends a SHA256HMAC-signed
[JSON Web Token](https://jwt.io), using the random key generated by
[`usergen`](usergen). No session data is kept on the server.

### Running

To run `slcl`, simply run the executable with the path to a directory including
the files listed above. By default, `slcl` will listen to incoming connections
on a random TCP port number. To set a specific port number, use the `-p`
command line option. For example:

```sh
slcl -p 7822 ~/my-db/
```

`slcl` requires a temporary directory where files uploaded by users are
temporarily stored until moved to the user directory. By default, `slcl`
attempts to retrieve the path to the temporary directory by inspecting the
`TMPDIR` environment variable, and falls back to `/tmp` if undefined.

If a custom temporary directory is required, it can be defined via command
line option `-t`. For example:

```sh
slcl -t ~/my-tmp -p 7822 ~/my-db
```

**Note:** system-level temporary directories such as `/tmp` might reside
on a filesytem different than the one where the database resides. This
would force `slcl` to copy the contents from uploaded files from the
temporary directory to the database, which might be an expensive operation.
Therefore, in order to avoid expensive copies, define a custom temporary
directory that resides on the same filesystem.

#### Thumbnail generation and rendering

Optionally, `slcl` displays thumbnails when listing a directory, if available
from the `thumbnails/` directory. In order to update these thumbnails when
files are added/removed to/from the database, a separate application must be
executed. See its [`README.md`](thumbnail/README.md) for further reference.

`slcl` provides the `-F` command line option to enable the use of a named pipe
that shall be used by the [thumbnail generation tool](thumbnail/).

## Why this project?

Previously, I had been recommended Nextcloud as an alternative to proprietary
services like Dropbox. Unfortunately, despite being a very flexible piece of
software, Nextcloud is _way too_ heavy on resources, specially on lower end
hardware such as the Raspberry Pi 3:

- It uses around **30%** RAM on my Raspberry Pi 3, configured with 973 MiB of
RAM, and of course it gets worse with several simultaneous users.
- Simple operations like searching and previewing files cause large amounts
of I/O and RAM usage, so much that it locks the whole server up more often than
not.
- Nextcloud pages are bloated. Even the login page is over **15 MiB** (!).
- Requires clients to run JavaScript, which also has a significant performance
penalty on the web browser. Also, some users do not feel comfortable running
JavaScript from their web browsers, and thus prefer to disable it.

After years of recurring frustration as a Nextcloud administrator and user,
I looked for alternatives that stripped out most of the unneeded bloat from
Nextcloud, while providing the required features listed above. However,
I could not find any that fit them, so I felt challenged to design a new
implementation.

On the other hand, command line-based solutions like `rsync` might not be as
convenient for non-technical people, compared to a web browser, or might not
be even available e.g.: phones.

## License

```
slcl, a simple and lightweight cloud.
Copyright (C) 2023-2025  Xavier Del Campo Romero

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.
```

Also, see [`LICENSE`](LICENSE).