249 lines
7.7 KiB
Markdown
249 lines
7.7 KiB
Markdown
# 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.
|
|
- Uses [`libweb`](https://gitea.privatedns.org/xavi/libweb), a tiny web framework.
|
|
- A simple JSON file as the credentials database.
|
|
- No JavaScript.
|
|
|
|
### 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.
|
|
- OpenSSL >= 2.0.
|
|
- cJSON >= 1.7.15.
|
|
- [`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).
|
|
- `jq` (for [`usergen`](usergen) only).
|
|
- CMake (optional).
|
|
|
|
### Ubuntu / Debian
|
|
|
|
#### Mandatory packages
|
|
|
|
```sh
|
|
sudo apt install build-essential libcjson-dev libssl-dev m4 jq
|
|
```
|
|
|
|
#### Optional packages
|
|
|
|
```sh
|
|
sudo apt install cmake
|
|
```
|
|
|
|
## 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
|
|
```
|
|
|
|
#### CMake
|
|
|
|
```sh
|
|
$ cmake -B build
|
|
$ 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.
|
|
|
|
A more complete example:
|
|
|
|
```
|
|
.
|
|
├── db.json
|
|
├── public
|
|
│ └── 44e03ab1bc3b0eff1567c76619186596 -> user/alice/file.txt
|
|
└── user
|
|
├── alice
|
|
│ └── file.txt
|
|
└── 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 script 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.
|
|
|
|
## 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 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).
|