Compare commits

...
117 Commits
Author SHA1 Message Date
Sergio C 5f1c1b1b61 Update README.md (#431)
Updated tproxy.md location
2024-03-24 08:33:34 +01:00
Yves Rutschle 58783af410 For MacOS, do not have multiple identicat cases in switch 2024-03-23 09:53:01 +01:00
Yves Rutschle 1957be1dc3 v.2.1.0 2024-03-22 16:41:50 +01:00
Yves Rűtschlé ecca78bde7 for MacOS, define unknown symbols to equivalent values 2024-03-21 18:54:03 +01:00
Yves Rűtschlé b94060ad76 undef FD_SETSIZE to avoid compiler warning upon redefine 2024-03-15 15:09:39 +01:00
Yves Rűtschlé 736b108a75 dont log error when remote client drops connection on Windows (fix #427) 2024-03-13 16:58:09 +01:00
Yves Rutschle 7ca567fcd9 clarify the difficulty of transparent proxying 2024-03-12 22:07:17 +01:00
Yves Rutschle 3117c15fbd when sslh-fork accept() fails with some errors, retry accept instead of dying 2024-01-11 21:32:39 +01:00
Yves Rutschle e428fc505c prevent children from continuing the parent's work in case of failure 2024-01-11 21:24:19 +01:00
Yves Rutschle 4dfb4d300a add default LANDLOCK_ACCESS_FS_REFER define for Ubuntu (fix #420) 2024-01-06 11:56:57 +01:00
Yves Rutschle 39184b5622 enable landlock access to files to allow forking and name resolution 2024-01-05 13:21:55 +01:00
Yves Rutschle 940461de18 check name resolution error to avoid segfault 2024-01-05 11:15:53 +01:00
Yves Rutschle 6f949419d1 fix comment to reflect reality 2024-01-05 10:34:29 +01:00
Yves Rutschle dab5df7409 clarify where the name resolution error comes from 2024-01-05 10:32:41 +01:00
Yves Rutschle 402ca5219b fix warnings on sockaddr storage 2024-01-04 23:22:44 +01:00
Yves Rutschle 046401148d add ./configure stage to docker build 2024-01-04 23:04:26 +01:00
Yves Rutschle 780e536aeb use autoconf to crete build scripts depending on landlock presence (fix #417) 2024-01-04 22:45:10 +01:00
Yves Rutschle ed0ab12a16 reintroduce --ssl as alias to --tls 2024-01-02 21:38:23 +01:00
Yves Rutschle b65f1e8b26 Merged Landlock feature 2023-12-09 14:13:07 +01:00
Konstantin 91b649daa0 Set image tag to use github in docker-compose example (#414)
Change image tag of the docker-compose example from ```sslh:latest``` to ```ghcr.io/yrutschle/sslh:latest```
2023-11-19 20:43:13 +01:00
Latchezar Tzvetkoff 7499c26e9e Cleanup error checking logic in bind_peer() (#412)
Thanks for the cleanup!
2023-11-15 22:02:21 +01:00
Yves Rutschle 90a55b6f9d document Windows build and docker repositories 2023-11-15 21:49:44 +01:00
Yves Rutschle 1f66e2e093 add sslh-ev 2023-11-15 21:46:42 +01:00
Yves Rutschle 92d2326016 Merge pull request #408 from pcrow/master
Fix transparent mode in a multi-stage chain
2023-10-07 21:15:58 +02:00
Preston Crow 81eed9d56a Transparent mode in a multi-stage chain will fail after the first step
because the (ip,port) is already bound.  With this change, the bind is
retried with a different port to at least keep the same IP address, which
for most uses is all that is needed.  I've tested this on my own system
where sslh is used downstream from stunnel, with both in transparent mode.
2023-10-06 17:50:25 -04:00
Yves Rutschle 490a44723b fix install to build sslh-fork (fix #407) 2023-10-05 11:53:09 +02:00
Yves Rutschle 23fb1eba6f remove obsolete intermediate sslh target 2023-10-03 09:34:38 +02:00
Yves Rutschle be66848e2d Merge pull request #406 from ffontaine/master
Makefile: add USE_LIBEV
2023-10-03 09:32:42 +02:00
Fabrice Fontaine 3e93c1d43d Makefile: add USE_LIBEV
Add USE_LIBEV to avoid the following build failure without libev raised
since version 2.0 and
https://github.com/yrutschle/sslh/commit/711c11c820f8be4f1399024bcaa1d7072de33217:

sslh-ev.c:24:10: fatal error: ev.h: Aucun fichier ou dossier de ce type
   24 | #include <ev.h>
      |          ^~~~~~

Signed-off-by: Fabrice Fontaine <fontaine.fabrice@gmail.com>
2023-10-01 18:53:43 +02:00
Yves Rutschle 1b26eb50a5 Changelog for resolve_on_forward fix 2023-09-24 09:25:52 +02:00
Yves Rutschle e0f15a31b7 resolve name at connection time for UDP too 2023-09-12 21:39:51 +02:00
Yves Rutschle c2551c011e update to Conf::Libconfig 1.0.3 API 2023-09-12 21:35:23 +02:00
Yves Rutschle e2c3ed61a8 update to Conf::Libconfig 1.0.3 API 2023-09-12 21:35:10 +02:00
Yves Rutschle 1b0c6d0b8d add resolve_on_forward in tests 2023-09-06 18:01:52 +02:00
Yves Rutschle 0562eb4b07 fix resolve_on_forward use (fix #405) 2023-09-06 15:48:13 +02:00
Yves Rutschle 93600d1fb1 Merge pull request #299 from amake/patch-1
Allow supplying additional CFLAGS
2023-08-31 15:26:42 +02:00
Yves Rutschle 04f258e705 Merge branch 'master' into patch-1 2023-08-31 15:26:22 +02:00
Yves Rutschle 1fb888bbf2 remove magic numbers with correct sizeof 2023-08-31 15:18:22 +02:00
Yves Rutschle 1f0cab2aee Merge pull request #342 from utoni/fix/ovpn-probe-read-overflow
fix possible buffer overflow
2023-08-31 15:15:50 +02:00
Yves Rutschle ff9328fa6c Merge branch 'utoni-fix/cppcheck-complains' 2023-08-31 15:13:57 +02:00
Yves Rutschle e941e8dd2e fix cppcheck complains 2023-08-31 15:10:53 +02:00
Yves Rűtschlé 8930ec395e Initial support for the landlock LSM 2023-08-29 17:20:51 +02:00
Yves Rutschle 54fe4b2f47 Merge pull request #401 from rnhmjoj/master
fix handling of IPv6 UDP connections
2023-08-26 21:32:22 +02:00
rnhmjoj 33129481cf fix handling of IPv6 UDP connections
Problem:
IPv6 addresses are 4 bytes long and don't fit inside a `sockaddr`, so
`recvfrom` will truncate the address to the first half.
When generating a reply, the remaining half of the address is filled
with garbage and the packet is subsequently delivered to the wrong host,
if not immediately dropped.

Solution:
replace `sockaddr` with `sockaddr_storage`, the latter is guaranteed to
be large enough to hold an IPv6 address and pointers can be cast to
`sockaddr *` when needed.
2023-08-22 11:44:09 +02:00
Yves Rűtschlé 4cc0867753 remove dependency of sslh-conf.c to sslhconf.cfg (fix #283) 2023-08-13 10:38:39 +02:00
Yves Rutschle 4728730abc Merge pull request #392 from klementng/docker/transparent-patch
Docker: ipv6 transparent patch
2023-08-10 08:21:39 +02:00
clement 9e7b4b751f sync and resolve merge conflict 2023-08-09 23:36:01 +08:00
Clement b11f2620ab Add clarification on --transparent mode for docker 2023-08-09 22:57:47 +08:00
Yves Rutschle 056c283145 Merge pull request #393 from oliv3r/dev/cleanup
container: Cleanup some style issues
2023-08-09 08:25:22 +02:00
Olliver Schinagl 00beb9595d container: Cleanup some style issues
Commit 5635dc5142 ("Enable --transparent mode for docker") made a
little bit of a mess of the Dockerfile and container-entrypoint.sh.

A few issues are, but not limited to; trailing whitespaces, incorrect
indentation, removed final newline, component sortability just to name a
few.

This MR fixes that and cleans up those files again.

One thing not touched was the enable/disablement of `set +e` to exit the
script on error. It is nicer/cleaner to solve this in a different way,
but that adds to much complexity.

While here, make the container architecture and alpine version
configurable, allowing us to build multi-arch images from the CI in the
future.

Signed-off-by: Olliver Schinagl <oliver@schinagl.nl>
2023-08-08 09:01:51 +02:00
clement 27f3e9075a update examples 2023-08-08 08:11:08 +08:00
clement 3912330040 add proper ipv6 checking 2023-08-08 08:02:54 +08:00
Yves Rutschle 4cbff962db Merge pull request #386 from oliv3r/dev/fix_version_tag
version: Do not put a slash in the version tag
2023-07-30 23:12:32 +02:00
Yves Rutschle 29c949e051 Merge pull request #389 from jsoref/spelling
Spelling
2023-07-30 23:09:03 +02:00
Yves Rutschle e8a84e6c22 Merge pull request #388 from klementng/master
Enable --transparent mode for docker
2023-07-30 23:01:38 +02:00
Josh Soref 42476d3cdc spelling: version
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:13 -04:00
Josh Soref f48f74c004 spelling: unprivileged
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:13 -04:00
Josh Soref 3af02d5c44 spelling: transparent
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:13 -04:00
Josh Soref 9e6d5cc13a spelling: theoretical
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:12 -04:00
Josh Soref 74fe57147b spelling: tentative
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:12 -04:00
Josh Soref 9acec69b9a spelling: successful
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:12 -04:00
Josh Soref 2a81ec0650 spelling: subsequent
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:12 -04:00
Josh Soref 596a4ee5b4 spelling: shortopts
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:12 -04:00
Josh Soref dbfeb480fe spelling: return
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:12 -04:00
Josh Soref 6b8a203691 spelling: responsibility
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:12 -04:00
Josh Soref 95e351e150 spelling: privileges
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:12 -04:00
Josh Soref 4b4c585107 spelling: parenthesis
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:12 -04:00
Josh Soref 0217e842d2 spelling: overridden
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:12 -04:00
Josh Soref 877ef1d27c spelling: other
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:12 -04:00
Josh Soref b1bfd5aee3 spelling: nonexistent
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:12 -04:00
Josh Soref af38544316 spelling: listening
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:12 -04:00
Josh Soref d4908178bf spelling: initialized
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:12 -04:00
Josh Soref 9deedec029 spelling: initialised
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:12 -04:00
Josh Soref 8ef3e8ddd8 spelling: increase
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:12 -04:00
Josh Soref 1eeba07396 spelling: inconsistency
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:12 -04:00
Josh Soref 2c48b8d83b spelling: functionally
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:12 -04:00
Josh Soref 44bac28718 spelling: freebind
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:12 -04:00
Josh Soref f520c616cc spelling: explicitly
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:12 -04:00
Josh Soref 39de82ae13 spelling: echosrv
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:12 -04:00
Josh Soref 08bea0e15d spelling: discrepancy
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:12 -04:00
Josh Soref cb52f3cdb4 spelling: deleterious
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:12 -04:00
Josh Soref 306164531f spelling: defining
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:12 -04:00
Josh Soref 40ddc4900a spelling: command
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:12 -04:00
Josh Soref 32aaacf4e7 spelling: argument
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:12 -04:00
Josh Soref a6a73f4d32 spelling: arbitrary
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:12 -04:00
Josh Soref 3d959eb425 spelling: address
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:12 -04:00
Josh Soref b2fd9d9daf spelling: github
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:15:36 -04:00
clement 8ff27e931f Change ordering for better layer caching 2023-07-22 06:46:00 +08:00
Clement ecbc33ba29 Update github build link 2023-07-21 07:07:25 +08:00
clement 5635dc5142 Enable --transparent mode for docker 2023-07-21 06:58:18 +08:00
Yves Rutschle bb76bc1d31 Merge pull request #382 from oliv3r/add_default_entrypoint
docker: Add proper entrypoint
2023-06-10 19:17:12 +02:00
Yves Rutschle 4a5ccb75b2 Merge pull request #384 from oliv3r/fix/parallel_builds
CI: Do not parallelized container builds
2023-06-10 19:15:39 +02:00
Olliver Schinagl 6672cc0f86 version: Do not put a slash in the version tag
Many systems do not like having a `/` in the version tag. In some cases,
we generate a version as `head/branch`, which even gets amplified if one
uses `dev/feature` as a branch name.

So lets drop these slashes to avoid potential issues.

Signed-off-by: Olliver Schinagl <oliver@schinagl.nl>
2023-06-09 14:00:30 +02:00
Olliver Schinagl 9dd560493a container: Drop privileges
A container is best served with the least amount of privileges. This
also ensures we don't have to drop anything later.

This does require running the container with elevated capabilities.

Note, that if for whatever reason, 'root' access within the container is
needed, this can easily be accomplished by running the container with
`docker run --user root:root sslh` for example.

Signed-off-by: Olliver Schinagl <oliver@schinagl.nl>
2023-06-05 22:50:06 +02:00
Olliver Schinagl db5ed29fa2 docker: Add proper entrypoint
As per docker guidelines [0] a container should always really have a
consistent entrypoint, without having to override it or do special
tricks.

The behavior should be _identical_ as before, but will no longer trigger
errors because sslh doesn't understand certain parameters (/bin/sh
for example being common). Further more, allows a proper entrypoint for
a CI to work easily with the container as well. Allowing for scenario's
such as `apk add git && sslh --foreground` in your sslh image for example.

E.g. `docker run sslh --help` works though with the default
`--foreground` a bit weirdly, as does `docker run sslh
/bin/sh` or `docker run sslh ls`.

[0]: https://github.com/docker-library/official-images#consistency

Signed-off-by: Olliver Schinagl <oliver@schinagl.nl>
2023-06-05 22:50:06 +02:00
Olliver Schinagl 295dba93b5 docker: Do not foreground by default, this should be a choice
It is weird that when invoking sslh, that it daemonizess in foreground by
default. This should always be a user choice, and if not, it should be a
program default.

Signed-off-by: Olliver Schinagl <oliver@schinagl.nl>
2023-06-05 22:50:06 +02:00
Olliver Schinagl f23da1fc36 CI: Do not parallelized container builds
Container builds that are parallelized must be 'merged' again
afterwards. Because that makes the pipeline far more complex for a quick
compile job, we might as well just run in sequentially.

Signed-off-by: Olliver Schinagl <oliver@schinagl.nl>
2023-06-05 22:26:30 +02:00
Olliver Schinagl 20764074cb docker: Improve caching layers
Docker is most efficient if you can 'order' the layers from
least-changing to most changing to improve on cache hits.

While here, change ADD to COPY as add is really intended to download
external packages, as well as installing sslh into a proper location.

Signed-off-by: Olliver Schinagl <oliver@schinagl.nl>
2023-06-05 22:05:24 +02:00
Yves Rutschle 7b7c9231b0 Merge pull request #383 from oliv3r/dev/pipeline
docker: Automatically build and push container
2023-06-05 20:53:35 +02:00
Olliver Schinagl 397f672248 docker: Automatically build and push container
The current sslh container works fine, but needs to be created manually
and locally by the user. Instead, let the pipeline do the dirty work and
push it to this repo's own registry.

Signed-off-by: Olliver Schinagl <oliver@schinagl.nl>
2023-06-05 17:09:50 +02:00
Yves Rutschle 3707c5b8a6 fix file descriptor leak if bind_peer fails 2023-05-13 23:18:07 +02:00
Yves Rutschle 5666a1bb9d die if fd is not in cnx, which should be impossible (current behaviour results in illegal array dereferencing, which is worse) 2023-05-13 23:11:03 +02:00
Yves Rutschle 7b9c7f0fb2 check accept() return value 2023-05-13 23:00:26 +02:00
Yves Rutschle eec2446723 fix error messages 2023-05-13 22:57:08 +02:00
Yves Rutschle d29c9524bd fix unbalance of va_start/va_end 2023-05-13 22:51:04 +02:00
Yves Rutschle db4ae0ef9d fix potential memory leak if the second malloc fails 2023-05-13 22:40:53 +02:00
Yves Rutschle ee48dae8c5 fix potential file descriptor leak if set_nonblock () fails 2023-05-13 22:36:21 +02:00
Yves Rutschle 842f6b0473 Add mention of QUIC example (fix #376) 2023-02-19 17:24:51 +01:00
Yves Rutschle 1f64a71cde fix out-of-bounds read in sslh-ev (fix #368) 2023-01-08 22:50:56 +01:00
Yves Rutschle 00fc8e5d95 fix off-by-one error 2023-01-08 22:50:10 +01:00
Yves Rutschle b9602ab98b removed obsolete max_fd tracker 2023-01-08 22:32:17 +01:00
Yves Rutschle 486f8a0090 removed obsolete comments 2023-01-08 21:58:50 +01:00
Yves Rutschle 3a1ac6c8d7 add protocol name that was missing in previous commit 2023-01-08 17:45:50 +01:00
Yves Rutschle 9dc3e3ce56 connection loss before it is logged resulted in logging uninitialised characters 2023-01-08 17:31:05 +01:00
Toni Uhlig 61af0b2e09 fix cppcheck complains
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2022-09-11 22:12:33 +02:00
Toni Uhlig c7ddee0409 fix possible buffer overflow
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2022-08-19 20:17:59 +02:00
Aaron Madlon-Kay a3640775bb Allow supplying additional CFLAGS
Package managers may want to supply default CFLAGS that should be used *in addition* to project-specific ones.
2021-08-18 08:49:33 +09:00
44 changed files with 5307 additions and 145 deletions

No files matched your search

+63
View File
@@ -0,0 +1,63 @@
name: Create and publish Container image
on:
push:
branches:
- master
tags:
- 'v*'
pull_request:
branches:
- master
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build-and-push-image:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to Container registry
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Docker meta
id: meta
uses: docker/metadata-action@v4
with:
images: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=edge
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
- name: Build and push
uses: docker/build-push-action@v4
with:
platforms: linux/amd64,linux/386,linux/arm64,linux/arm/v6,linux/arm/v7
context: .
file: Dockerfile
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
+2
View File
@@ -3,8 +3,10 @@
*.o
cscope.*
echosrv
libsslh.a
sslh-fork
sslh-select
sslh-ev
sslh.8.gz
tags
version.h
+26 -10
View File
@@ -1,5 +1,21 @@
v2.1.0:
Support for the Landlock LSM. After initial setup,
sslh gives up all local file access rights.
Reintroduced --ssl as an alias to --tls.
Introduce autoconf to adapt to landlock presence.
Close connexion without error message if remote
client forcefully closes connexion, for Windows.
v2.0.1:
Fix resolve_on_forward setting, which would crash
sslh reliably.
v2.0.0:
v2.0:
New sslh-ev: this is functionaly equivalent to
New sslh-ev: this is functionally equivalent to
sslh-select (mono-process, only forks for specified
protocols), but based on libev, which should make it
scalable to large numbers of connections.
@@ -41,7 +57,7 @@ v1.22: 17AUG2021
combined with incoming TLS with SNI. UDP clients
and servers need to agree on the IPv4/IPv6 they use:
use the same protocol on all sides! Often, this
means explicitely using 'ip4-localhost'.
means explicitly using 'ip4-localhost'.
UDP sender-receiver pairs (connections, so to speak)
are kept for 60s, which can be changed with
`udp_timeout` in the configuration.
@@ -83,7 +99,7 @@ v1.21: 11JUL2020
Added TCP_FASTOPEN support for client sockets (if
tfo_ok is specified in their configuration) and for
listenint socket, if all client protocols support it.
listening socket, if all client protocols support it.
(Craig Andrews)
Added 'minlength' option to skip a probe if less
@@ -109,8 +125,8 @@ v1.20: 20NOV2018
Before, probes were tried in order, repeating on the
same probe as long it returned PROBE_AGAIN before
moving to the next one. This means a probe which
requires a lot of data (i.e. returne PROBE_AGAIN for
a long time) could prevent sucessful matches from
requires a lot of data (i.e. return PROBE_AGAIN for
a long time) could prevent successful matches from
subsequent probes. The configuration file needed to
take that into account.
@@ -171,7 +187,7 @@ v1.18: 29MAR2016
v1.17: 09MAR2015
Support RFC5952-style IPv6 addresses, e.g. [::]:443.
Transparant proxy support for FreeBSD.
Transparent proxy support for FreeBSD.
(Ruben van Staveren)
Using -F with no argument will try
@@ -200,7 +216,7 @@ v1.16: 11FEB2014
Libcap support: Keep only CAP_NET_ADMIN if started
as root with transparent proxying and dropping
priviledges (enable USELIBCAP in Makefile). This
privileges (enable USELIBCAP in Makefile). This
avoids having to mess with filesystem capabilities.
(Sebastian Schmidt/yath)
@@ -209,7 +225,7 @@ v1.16: 11FEB2014
actual errors if connections are dropped before
getting to getpeername).
Set IP_FREEDBIND if available to bind to addresses
Set IP_FREEBIND if available to bind to addresses
that don't yet exist.
v1.15: 27JUL2013
@@ -294,7 +310,7 @@ v1.11: 21APR2012
--user isn't specified, just run as current user.
No longer create PID file by default, it should be
explicitely set with --pidfile.
explicitly set with --pidfile.
No longer log to syslog if in foreground. Logs are
instead output to stderr.
@@ -385,7 +401,7 @@ v1.8: 15JUL2011
v1.7: 01FEB2010
Added CentOS init.d script (Andre Krajnik).
Fixed default ssl address inconsistancy, now
Fixed default ssl address inconsistency, now
defaults to "localhost:443" and fixed documentation
accordingly (pointed by Markus Schalke).
+32 -17
View File
@@ -1,23 +1,38 @@
FROM alpine:latest as build
ARG ALPINE_VERSION="latest"
ARG TARGET_ARCH="library"
ADD . /sslh
FROM docker.io/${TARGET_ARCH}/alpine:${ALPINE_VERSION} AS build
RUN \
apk add \
gcc \
libconfig-dev \
make \
musl-dev \
pcre2-dev \
perl && \
cd /sslh && \
make sslh-select && \
strip sslh-select
WORKDIR /sslh
FROM alpine:latest
RUN apk add --no-cache \
'gcc' \
'libconfig-dev' \
'make' \
'musl-dev' \
'pcre2-dev' \
'perl' \
;
COPY --from=build /sslh/sslh-select /sslh
COPY . /sslh
RUN apk --no-cache add libconfig pcre2
RUN ./configure && make sslh-select && strip sslh-select
ENTRYPOINT [ "/sslh", "--foreground"]
FROM docker.io/${TARGET_ARCH}/alpine:${ALPINE_VERSION}
COPY --from=build "/sslh/sslh-select" "/usr/local/bin/sslh"
RUN apk add --no-cache \
'libconfig' \
'pcre2' \
'iptables' \
'ip6tables' \
'libcap' \
&& \
adduser -s '/bin/sh' -S -D sslh && \
setcap cap_net_bind_service,cap_net_raw+ep /usr/local/bin/sslh
COPY "./container-entrypoint.sh" "/init"
ENTRYPOINT [ "/init" ]
# required for updating iptables
USER root:root
+20 -9
View File
@@ -3,9 +3,11 @@ VERSION=$(shell ./genver.sh -r)
# Configuration -- you probably need to `make clean` if you
# change any of these
# landlock is checked by `configure` and set in config.h
ENABLE_SANITIZER= # Enable ASAN/LSAN/UBSAN
ENABLE_REGEX=1 # Enable regex probes
USELIBCONFIG=1 # Use libconfig? (necessary to use configuration files)
USELIBEV=1 # Use libev?
USELIBWRAP?= # Use libwrap?
USELIBCAP= # Use libcap?
USESYSTEMD= # Make use of systemd socket activation
@@ -30,10 +32,11 @@ endif
CC ?= gcc
AR ?= ar
CFLAGS ?=-Wall -O2 -DLIBPCRE -g $(CFLAGS_COV) $(CFLAGS_SAN)
CFLAGS +=-Wall -O2 -DLIBPCRE -g $(CFLAGS_COV) $(CFLAGS_SAN)
LIBS=-lm -lpcre2-8
OBJS=sslh-conf.o common.o log.o sslh-main.o probe.o tls.o argtable3.o collection.o gap.o tcp-probe.o
OBJS=sslh-conf.o common.o log.o sslh-main.o probe.o tls.o argtable3.o collection.o gap.o tcp-probe.o landlock.o
OBJS_A=libsslh.a
FORK_OBJS=sslh-fork.o $(OBJS_A)
SELECT_OBJS=processes.o udp-listener.o sslh-select.o hash.o tcp-listener.o $(OBJS_A)
@@ -71,8 +74,11 @@ ifneq ($(strip $(USELIBBSD)),)
CPPFLAGS+=-DLIBBSD
endif
ifneq ($(strip $(USELIBEV)),)
CONDITIONAL_TARGETS+=sslh-ev
endif
all: sslh $(MAN) echosrv $(CONDITIONAL_TARGETS)
all: sslh-fork sslh-select $(MAN) echosrv $(CONDITIONAL_TARGETS)
%.o: %.c %.h version.h
$(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@
@@ -83,13 +89,15 @@ $(OBJS_A): $(OBJS)
version.h:
./genver.sh >version.h
sslh: sslh-fork sslh-select sslh-ev
$(OBJS) $(FORK_OBJS) $(SELECT_OBJS) $(EV_OBJS): argtable3.h collection.h common.h gap.h hash.h log.h probe.h processes.h sslh-conf.h tcp-listener.h tcp-probe.h tls.h udp-listener.h version.h
sslh-conf.c sslh-conf.h: sslhconf.cfg
c2s:
conf2struct sslhconf.cfg
conf2struct echosrv.cfg
sslh-conf.c sslh-conf.h: sslhconf.cfg
$(warning "sslhconf.cfg is more recent than sslh-conf.[ch]. Use `make c2s` to rebuild using `conf2struct`")
sslh-fork: version.h Makefile $(FORK_OBJS)
$(CC) $(CFLAGS) $(LDFLAGS) -o sslh-fork $(FORK_OBJS) $(LIBS)
@@ -104,11 +112,14 @@ systemd-sslh-generator: systemd-sslh-generator.o
$(CC) $(CFLAGS) $(LDFLAGS) -o systemd-sslh-generator systemd-sslh-generator.o -lconfig
echosrv-conf.c echosrv-conf.h: echosrv.cfg
conf2struct echosrv.cfg
$(warning "echosrv.cfg is more recent than echosrv-conf.[ch]. Use `make c2s` to rebuild using `conf2struct`")
echosrv: version.h echosrv-conf.c echosrv.o echosrv-conf.o argtable3.o
$(CC) $(CFLAGS) $(LDFLAGS) -o echosrv echosrv.o echosrv-conf.o argtable3.o $(LIBS)
landlock.o: config.h
$(MAN): sslh.pod Makefile
pod2man --section=8 --release=$(VERSION) --center=" " sslh.pod | gzip -9 - > $(MAN)
@@ -128,7 +139,7 @@ docker-clean:
yes | docker image prune
# generic install: install binary and man page
install: sslh $(MAN)
install: sslh-fork $(MAN)
mkdir -p $(DESTDIR)/$(BINDIR)
mkdir -p $(DESTDIR)/$(MANDIR)
install -p sslh-fork $(DESTDIR)/$(BINDIR)/sslh
@@ -148,7 +159,7 @@ distclean: clean
rm -f tags sslh-conf.[ch] echosrv-conf.[ch] cscope.*
clean:
rm -f sslh-fork sslh-select sslh-ev echosrv version.h $(MAN) systemd-sslh-generator *.o *.gcov *.gcno *.gcda *.png *.html *.css *.info
rm -f sslh-fork sslh-select $(CONDITIONAL_TARGETS) echosrv version.h $(MAN) systemd-sslh-generator *.o *.gcov *.gcno *.gcda *.png *.html *.css *.info
tags:
ctags --globals -T *.[ch]
+103 -12
View File
@@ -11,7 +11,7 @@ protocol that can be tested using a regular expression, can
be recognised. A typical use case is to allow serving
several services on port 443 (e.g. to connect to SSH from
inside a corporate firewall, which almost never block port
443) while still serving HTTPS on that port.
443) while still serving HTTPS on that port.
Hence `sslh` acts as a protocol demultiplexer, or a
switchboard. With the SNI and ALPN probe, it makes a good
@@ -20,9 +20,10 @@ address.
`sslh` has the bells and whistles expected from a mature
daemon: privilege and capabilities dropping, inetd support,
systemd support, transparent proxying, chroot, logging,
IPv4 and IPv6, TCP and UDP, a fork-based and a select-based
model, and more.
systemd support, transparent proxying, chroot, logging,
IPv4 and IPv6, TCP and UDP, a fork-based, a select-based
model, and yet another based on libev for larger
installations.
Install
=======
@@ -35,6 +36,18 @@ Configuration
Please refer to the [configuration guide](doc/config.md).
Transparent proxying allows the target server to see the
original client IP address, i.e. `sslh` becomes invisible.
This makes it easier to use the server's logs, and potential
IP-based banning ability.
Configuration of transparent proxying is highly dependent on
your network environment and infrastructure setup. There is
no known generic approach, and if you do not find directions
for your exact setup, you will probably need an extensive
knowledge of network management and iptables setup".
It is described in its own [document](doc/tproxy.md).
Docker image
@@ -44,16 +57,15 @@ How to use
---
Build docker image
make docker
```bash
docker run \
--cap-add CAP_NET_RAW \
--cap-add CAP_NET_BIND_SERVICE \
--rm \
-it \
sslh:latest \
ghcr.io/yrutschle/sslh:latest \
--foreground \
--listen=0.0.0.0:443 \
--ssh=hostname:22 \
--tls=hostname:443
@@ -61,16 +73,16 @@ docker run \
docker-compose example
```
```yaml
version: "3"
services:
sslh:
image: sslh:latest
image: ghcr.io/yrutschle/sslh:latest
hostname: sslh
ports:
- 443:443
command: --listen=0.0.0.0:443 --tls=nginx:443 --openvpn=openvpn:1194
command: --foreground --listen=0.0.0.0:443 --tls=nginx:443 --openvpn=openvpn:1194
depends_on:
- nginx
- openvpn
@@ -82,6 +94,85 @@ services:
image: openvpn
```
Transparent mode 1: using sslh container for networking
_Note: For transparent mode to work, the sslh container must be able to reach your services via **localhost**_
```yaml
version: "3"
services:
sslh:
build: https://github.com/yrutschle/sslh.git
container_name: sslh
environment:
- TZ=${TZ}
cap_add:
- NET_ADMIN
- NET_RAW
- NET_BIND_SERVICE
sysctls:
- net.ipv4.conf.default.route_localnet=1
- net.ipv4.conf.all.route_localnet=1
command: --transparent --foreground --listen=0.0.0.0:443 --tls=localhost:8443 --openvpn=localhost:1194
ports:
- 443:443 #sslh
- 80:80 #nginx
- 8443:8443 #nginx
- 1194:1194 #openvpn
extra_hosts:
- localbox:host-gateway
restart: unless-stopped
nginx:
image: nginx:latest
.....
network_mode: service:sslh #set nginx container to use sslh networking.
# ^^^ This is required. This makes nginx reachable by sslh via localhost
openvpn:
image: openvpn:latest
.....
network_mode: service:sslh #set openvpn container to use sslh networking
```
Transparent mode 2: using host networking
```yaml
version: "3"
services:
sslh:
build: https://github.com/yrutschle/sslh.git
container_name: sslh
environment:
- TZ=${TZ}
cap_add:
- NET_ADMIN
- NET_RAW
- NET_BIND_SERVICE
# must be set manually
#sysctls:
# - net.ipv4.conf.default.route_localnet=1
# - net.ipv4.conf.all.route_localnet=1
command: --transparent --foreground --listen=0.0.0.0:443 --tls=localhost:8443 --openvpn=localhost:1194
network_mode: host
restart: unless-stopped
nginx:
image: nginx:latest
.....
ports:
- 8443:8443 # bind to docker host on port 8443
openvpn:
image: openvpn:latest
.....
ports:
- 1194:1194 # bind to docker host on port 1194
```
Comments? Questions?
====================
+13 -13
View File
@@ -2876,9 +2876,9 @@ static void arg_file_resetfn(struct arg_file* parent) {
static const char* arg_basename(const char* filename) {
const char *result = NULL, *result1, *result2;
/* Find the last occurrence of eother file separator character. */
/* Two alternative file separator chars are supported as legal */
/* file separators but not both together in the same filename. */
/* Find the last occurrence of other file separator character. */
/* Two alternative file separator chars are supported as legal */
/* file separators but not both together in the same filename. */
result1 = (filename ? strrchr(filename, FILESEPARATOR1) : NULL);
result2 = (filename ? strrchr(filename, FILESEPARATOR2) : NULL);
@@ -2927,7 +2927,7 @@ static int arg_file_scanfn(struct arg_file* parent, const char* argval) {
} else if (!argval) {
/* a valid argument with no argument value was given. */
/* This happens when an optional argument value was invoked. */
/* leave parent arguiment value unaltered but still count the argument. */
/* leave parent argument value unaltered but still count the argument. */
parent->count++;
} else {
parent->filename[parent->count] = argval;
@@ -3173,7 +3173,7 @@ static int arg_int_scanfn(struct arg_int* parent, const char* argval) {
} else if (!argval) {
/* a valid argument with no argument value was given. */
/* This happens when an optional argument value was invoked. */
/* leave parent arguiment value unaltered but still count the argument. */
/* leave parent argument value unaltered but still count the argument. */
parent->count++;
} else {
long int val;
@@ -3813,8 +3813,8 @@ static const TRexChar* g_nnames[] = {_SC("NONE"), _SC("OP_GREEDY"), _SC("OP_O
#endif
#define OP_GREEDY (MAX_CHAR + 1) /* * + ? {n} */
#define OP_OR (MAX_CHAR + 2)
#define OP_EXPR (MAX_CHAR + 3) /* parentesis () */
#define OP_NOCAPEXPR (MAX_CHAR + 4) /* parentesis (?:) */
#define OP_EXPR (MAX_CHAR + 3) /* parenthesis () */
#define OP_NOCAPEXPR (MAX_CHAR + 4) /* parenthesis (?:) */
#define OP_DOT (MAX_CHAR + 5)
#define OP_CLASS (MAX_CHAR + 6)
#define OP_CCLASS (MAX_CHAR + 7)
@@ -5313,7 +5313,7 @@ static void arg_parse_untagged(int argc, char** argv, struct arg_hdr** table, st
}
}
/* if a tenative error still remains at this point then register it as a proper error */
/* if a tentative error still remains at this point then register it as a proper error */
if (errorlast) {
arg_register_error(endtable, parentlast, errorlast, optarglast);
optind++;
@@ -5384,7 +5384,7 @@ int arg_parse(int argc, char** argv, void** argtable) {
/*
Fill in the local copy of argv[]. We need a local copy
because getopt rearranges argv[] which adversely affects
susbsequent parsing attempts.
subsequent parsing attempts.
*/
for (i = 0; i < argc; i++)
argvcopy[i] = argv[i];
@@ -5451,7 +5451,7 @@ static void arg_cat_option(char* dest, size_t ndest, const char* shortopts, cons
if (shortopts) {
char option[3];
/* note: option array[] is initialiazed dynamically here to satisfy */
/* note: option array[] is initialized dynamically here to satisfy */
/* a deficiency in the watcom compiler wrt static array initializers. */
option[0] = '-';
option[1] = shortopts[0];
@@ -5509,7 +5509,7 @@ static void arg_cat_optionv(char* dest, size_t ndest, const char* shortopts, con
/* "-a|-b|-c" */
char shortopt[3];
/* note: shortopt array[] is initialiazed dynamically here to satisfy */
/* note: shortopt array[] is initialized dynamically here to satisfy */
/* a deficiency in the watcom compiler wrt static array initializers. */
shortopt[0] = '-';
shortopt[1] = *c;
@@ -5881,7 +5881,7 @@ static void arg_print_formatted_ds(arg_dstr_t ds, const unsigned lmargin, const
* Prints the glossary in strict GNU format.
* Differences to arg_print_glossary() are:
* - wraps lines after 80 chars
* - indents lines without shortops
* - indents lines without shortopts
* - does not accept formatstrings
*
* Contributed by Uli Fouquet
@@ -5956,7 +5956,7 @@ int arg_nullcheck(void** argtable) {
* that entry were still allocated ok. Those subsequent allocations will not be
* deallocated by arg_free().
* Despite the unlikeliness of the problem occurring, and the even unlikelier event
* that it has any deliterious effect, it is fixed regardless by replacing arg_free()
* that it has any deleterious effect, it is fixed regardless by replacing arg_free()
* with the newer arg_freetable() function.
* We still keep arg_free() for backwards compatibility.
*/
+2 -2
View File
@@ -87,7 +87,7 @@ typedef int(arg_comparefn)(const void* k1, const void* k2);
* that particular arg_xxx arguments, performing post-parse checks, and
* reporting errors.
* These functions are private to the individual arg_xxx source code
* and are the pointer to them are initiliased by that arg_xxx struct's
* and are the pointer to them are initialised by that arg_xxx struct's
* constructor function. The user could alter them after construction
* if desired, but the original intention is for them to be set by the
* constructor and left unaltered.
@@ -95,7 +95,7 @@ typedef int(arg_comparefn)(const void* k1, const void* k2);
typedef struct arg_hdr {
char flag; /* Modifier flags: ARG_TERMINATOR, ARG_HASVALUE. */
const char* shortopts; /* String defining the short options */
const char* longopts; /* String defiing the long options */
const char* longopts; /* String defining the long options */
const char* datatype; /* Description of the argument data type */
const char* glossary; /* Description of the option as shown by arg_print_glossary function */
int mincount; /* Minimum number of occurences of this option accepted */
+27 -6
View File
@@ -110,7 +110,7 @@ int make_listen_tfo(int s)
return setsockopt(s, SOL_SOCKET, TCP_FASTOPEN, (char*)&qlen, sizeof(qlen));
}
/* Starts listening on a single address
/* Starts listening on a single address
* Returns a socket filehandle, or dies with message in case of major error */
int listen_single_addr(struct addrinfo* addr, int keepalive, int udp)
{
@@ -262,7 +262,7 @@ int bind_peer(int fd, int fd_from)
/* if the destination is the same machine, there's no need to do bind */
if (is_same_machine(&from))
return 0;
#ifndef IP_BINDANY /* use IP_TRANSPARENT */
res = setsockopt(fd, IPPROTO_IP, IP_TRANSPARENT, &trans, sizeof(trans));
CHECK_RES_DIE(res, "setsockopt IP_TRANSPARENT");
@@ -278,7 +278,26 @@ int bind_peer(int fd, int fd_from)
}
#endif /* IP_TRANSPARENT / IP_BINDANY */
res = bind(fd, from.ai_addr, from.ai_addrlen);
CHECK_RES_RETURN(res, "bind", res);
if (res == -1) {
if (errno != EADDRINUSE) {
print_message(msg_system_error, "%s:%d:%s:%d:%s\n", __FILE__, __LINE__,
"bind", errno, strerror(errno));
return res;
}
/*
* If there is more than one transparent mode proxy going on, such as
* using sslh as the target of stunnel also in transparent mode, then
* the (ip,port) combination will already be bound for the previous application.
* In that case, the best we can do is bind with a different port.
* This does mean the local server can't use the ident protocol as the port will
* have changed, but most people won't care.
* Also note that stunnel uses the same logic for the same situation.
*/
((struct sockaddr_in *)from.ai_addr)->sin_port = 0;
res = bind(fd, from.ai_addr, from.ai_addrlen);
CHECK_RES_RETURN(res, "bind", res);
}
return 0;
}
@@ -348,6 +367,7 @@ int connect_addr(struct connection *cnx, int fd_from, connect_blocking blocking)
if (transparent) {
res = bind_peer(fd, fd_from);
if (res == -1) close(fd);
CHECK_RES_RETURN(res, "bind_peer", res);
}
res = connect(fd, a->ai_addr, a->ai_addrlen);
@@ -462,6 +482,7 @@ int fd2fd(struct queue *target_q, struct queue *from_q)
return FD_NODATA;
case ECONNRESET:
case ENOTSOCK:
case EPIPE:
return FD_CNXCLOSED;
}
@@ -529,7 +550,8 @@ char* sprintaddr(char* buf, size_t size, struct addrinfo *a)
}
/* Turns a hostname and port (or service) into a list of struct addrinfo
* returns 0 on success, -1 otherwise and logs error
* On success, returns 0
* On failure, returns -1 or one of getaddrinfo() codes
*/
int resolve_split_name(struct addrinfo **out, char* host, char* serv)
{
@@ -555,7 +577,7 @@ int resolve_split_name(struct addrinfo **out, char* host, char* serv)
res = getaddrinfo(host, serv, &hint, out);
if (res)
print_message(msg_system_error, "%s `%s:%s'\n", gai_strerror(res), host, serv);
print_message(msg_system_error, "resolve_split_name: %s `%s:%s'\n", gai_strerror(res), host, serv);
return res;
}
@@ -847,4 +869,3 @@ void write_pid_file(const char* pidfile)
exit(3);
}
}
+6 -1
View File
@@ -5,6 +5,7 @@
* enough for the macros to adapt (http://support.microsoft.com/kb/111855)
*/
#ifdef __CYGWIN__
#undef FD_SETSIZE
#define FD_SETSIZE 4096
#endif
@@ -112,7 +113,7 @@ struct connection {
struct queue q[2];
/* SOCK_DGRAM */
struct sockaddr client_addr; /* Contains the remote client address */
struct sockaddr_storage client_addr; /* Contains the remote client address */
socklen_t addrlen;
int local_endpoint; /* Contains the local address */
@@ -183,4 +184,8 @@ void start_shoveler(int);
void main_loop(struct listen_endpoint *listen_sockets, int num_addr_listen);
/* landlock.c */
void setup_landlock(void);
#endif
+9
View File
@@ -0,0 +1,9 @@
#ifndef CONFIG_H
/* Template for config.h, filled by `configure`. */
/* Landlock sandboxing Linux LSM */
#undef HAVE_LANDLOCK
#endif
Vendored Executable
+4469
View File
File diff suppressed because it is too large. Load diff
+9
View File
@@ -0,0 +1,9 @@
dnl Use autoconf to generate the `configure` script from this and Makefile.in
AC_INIT
AC_CONFIG_HEADERS(config.h)
AC_CONFIG_FILES([Makefile])
AC_CHECK_HEADERS(linux/landlock.h, AC_DEFINE(HAVE_LANDLOCK), [])
AC_OUTPUT
+97
View File
@@ -0,0 +1,97 @@
#!/bin/sh
# SPDX-License-Identifier: GPL2-or-later
#
# Copyright (C) 2023 Olliver Schinagl <oliver@schinagl.nl>
#
# A beginning user should be able to docker run image bash (or sh) without
# needing to learn about --entrypoint
# https://github.com/docker-library/official-images#consistency
set -eu
bin='sslh'
# run command if it is not starting with a "-" and is an executable in PATH
if [ "${#}" -le 0 ] || \
[ "${1#-}" != "${1}" ] || \
[ -d "${1}" ] || \
! command -v "${1}" > '/dev/null' 2>&1; then
entrypoint='true'
fi
unconfigure_iptables() {
echo "Received SIG TERM/INT/KILL. Removing iptables / routing changes"
set +e # Don't exit if got error
set -x
iptables -t raw -D PREROUTING ! -i lo -d 127.0.0.0/8 -j DROP
iptables -t mangle -D POSTROUTING ! -o lo -s 127.0.0.0/8 -j DROP
iptables -t nat -D OUTPUT -m owner --uid-owner sslh -p tcp --tcp-flags FIN,SYN,RST,ACK SYN -j CONNMARK --set-xmark 0x01/0x0f
iptables -t mangle -D OUTPUT ! -o lo -p tcp -m connmark --mark 0x01/0x0f -j CONNMARK --restore-mark --mask 0x0f
ip rule del fwmark 0x1 lookup 100
ip route del local 0.0.0.0/0 dev lo table 100
if [ $(cat /proc/sys/net/ipv6/conf/all/disable_ipv6) -eq 0 ]; then
ip6tables -t raw -D PREROUTING ! -i lo -d ::1/128 -j DROP
ip6tables -t mangle -D POSTROUTING ! -o lo -s ::1/128 -j DROP
ip6tables -t nat -D OUTPUT -m owner --uid-owner sslh -p tcp --tcp-flags FIN,SYN,RST,ACK SYN -j CONNMARK --set-xmark 0x01/0x0f
ip6tables -t mangle -D OUTPUT ! -o lo -p tcp -m connmark --mark 0x01/0x0f -j CONNMARK --restore-mark --mask 0x0f
ip -6 rule del fwmark 0x1 lookup 100
ip -6 route del local ::/0 dev lo table 100
fi
set -e
set +x
}
configure_iptables() {
echo "Configuring iptables and routing..."
set +e # Don't exit if got error
set -x
iptables -t raw -A PREROUTING ! -i lo -d 127.0.0.0/8 -j DROP
iptables -t mangle -A POSTROUTING ! -o lo -s 127.0.0.0/8 -j DROP
iptables -t nat -A OUTPUT -m owner --uid-owner sslh -p tcp --tcp-flags FIN,SYN,RST,ACK SYN -j CONNMARK --set-xmark 0x01/0x0f
iptables -t mangle -A OUTPUT ! -o lo -p tcp -m connmark --mark 0x01/0x0f -j CONNMARK --restore-mark --mask 0x0f
ip rule add fwmark 0x1 lookup 100
ip route add local 0.0.0.0/0 dev lo table 100
if [ $(cat /proc/sys/net/ipv6/conf/all/disable_ipv6) -eq 0 ]; then
ip6tables -t raw -A PREROUTING ! -i lo -d ::1/128 -j DROP
ip6tables -t mangle -A POSTROUTING ! -o lo -s ::1/128 -j DROP
ip6tables -t nat -A OUTPUT -m owner --uid-owner sslh -p tcp --tcp-flags FIN,SYN,RST,ACK SYN -j CONNMARK --set-xmark 0x01/0x0f
ip6tables -t mangle -A OUTPUT ! -o lo -p tcp -m connmark --mark 0x01/0x0f -j CONNMARK --restore-mark --mask 0x0f
ip -6 rule add fwmark 0x1 lookup 100
ip -6 route add local ::/0 dev lo table 100
fi
set -e
set +x
}
for _args in "${@}" ; do
if [ "${_args:-}" = '--transparent' ] ; then
echo '--transparent flag is set'
configure_iptables
trap unconfigure_iptables TERM INT KILL
break
fi
done
# Drop privileges and run as sslh user
sslh_cmd="${entrypoint:+${bin}} ${@}"
echo "Executing with user 'sslh': ${sslh_cmd}"
exec su - sslh -c "${sslh_cmd}" &
wait "${!}"
exit 0
+1 -1
View File
@@ -7,7 +7,7 @@ doesn't work, report how what was suggested here went.
It's also worth reading [how to ask
questions](http://www.catb.org/~esr/faqs/smart-questions.html)
before posting on the mailing list or opening an issue in
Github.
GitHub.
Getting more info
=================
+23
View File
@@ -1,3 +1,12 @@
Pre-built binaries
==================
Docker images of `master` and of the tagged versions are
available directly from [Github](https://github.com/yrutschle/sslh/pkgs/container/sslh).
Windows binaries for Cygwin are graciouly produced by
nono303 on his [repository](https://github.com/nono303/sslh).
Compile and install
===================
@@ -79,6 +88,20 @@ of the Makefile:
* `USELIBBSD` compiles support for updating the process name (as shown
by `ps`).
Generating the configuration parser
-----------------------------------
The configuration file and command line parser is generated
by `conf2struct`, from `sslhconf.cfg`, which generates
`sslh-conf.c` and `sslh-conf.h`. The resulting files are
included in the source so `sslh` can be built without
`conf2struct` installed.
Further, to prevent build issues, `sslh-conf.[ch]` has no
dependency to `sslhconf.cfg` in the Makefile. In the event
of adding configuration settings, they need to be
regenerated using `make c2s`.
Binaries
--------
+72
View File
@@ -0,0 +1,72 @@
It is possible to run `sslh` on Windows. The `fork` model
should be avoided as it is very inefficient on Windows, but
`sslh-select` and `sslh-ev` both work with good performance
(prefer the latter, however).
The following script downloads the latest cygwin, the latest version of sslh, and then compiles and copies the binaries with dependancies to an output folder.
It may be needed to correct it from time to time, but it works. I use it in a virtual machine.
Just retrieve WGET.EXE from [https://eternallybored.org/misc/wget/](url) or git binaries.
Copy the 3 files
GO.cmd
wget.exe
compile.sh
to C root folder, then execute **GO.cmd** with administrative rights.
with **GO.cmd**
@ECHO OFF
CD /D "%~dp0"
NET SESSION >NUL 2>&1
IF %ERRORLEVEL% NEQ 0 (
ECHO Permission denied. This script must be run as an Administrator.
ECHO:
GOTO FIN
) ELSE (
ECHO Running as Administrator.
TIMEOUT /T 2 >NUL
wget --no-check-certificate https://www.cygwin.com/setup-x86_64.exe
IF NOT EXIST setup-x86_64.exe GOTO FIN
MKDIR C:\Z
setup-x86_64.exe -l C:\Z -s ftp://ftp.funet.fi/pub/mirrors/sourceware.org/pub/cygwin/ -q -P make -P git -P gcc-g++ -P autoconf -P automake -P libtool -P libpcre-devel -P libpcre2-devel -P bison -P libev-devel
MKDIR C:\cygwin64\home\user
COPY COMPILE.SH C:\cygwin64\home\user
START C:\cygwin64\bin\mintty.exe /bin/bash --login -i ~/compile.sh
START EXPLORER C:\zzSORTIE
)
:FIN
PAUSE
EXIT
and **compile.sh**
# SAVE FILE TO UNIX FORMAT
# COPY IT IN C cygwin64 home user
git clone https://github.com/hyperrealm/libconfig.git
cd libconfig
autoreconf -fi
./configure
make
make install
cd ..
cp /usr/local/lib/libconfig.* /usr/lib
git clone https://github.com/yrutschle/sslh.git
cd sslh
make
cd ..
mkdir /cygdrive/c/zzSORTIE
cp ./sslh/sslh*.exe /cygdrive/c/zzSORTIE
cp /usr/local/bin/cygconfig-11.dll /cygdrive/c/zzSORTIE
cp /cygdrive/c/cygwin64/bin/cygwin1.dll /cygdrive/c/zzSORTIE
cp /cygdrive/c/cygwin64/bin/cygpcreposix-0.dll /cygdrive/c/zzSORTIE
cp /cygdrive/c/cygwin64/bin/cygpcre-1.dll /cygdrive/c/zzSORTIE
cp /cygdrive/c/cygwin64/bin/cygev-4.dll /cygdrive/c/zzSORTIE
cp /cygdrive/c/cygwin64/bin/cygpcre2-8-0.dll /cygdrive/c/zzSORTIE
This method was contributed by lerenardo on [github](https://github.com/yrutschle/sslh/issues/196#issuecomment-1692805639).
+7 -9
View File
@@ -92,19 +92,15 @@ to the executable:
sudo setcap cap_net_bind_service,cap_net_raw+pe sslh-select
Then you can run sslh-select as an unpriviledged user, e.g.:
Then you can run sslh-select as an unprivileged user, e.g.:
sslh-select -p myname:443 --ssh localhost:22 --tls localhost:443
Transparent proxy support
-------------------------
Transparent proxying allows the target server to see the
original client IP address, i.e. `sslh` becomes invisible.
This makes it easier to use the server's logs, and potential
IP-based banning ability.
Set up can get complicated, so it has its own [document](tproxy.md).
Transparent proxying is described in its own
[document](tproxy.md).
Systemd Socket Activation
-------------------------
@@ -167,7 +163,7 @@ This parses the /etc/sslh.cfg (or /etc/sslh/sslh.cfg file if that exists
instead) configuration file and dynamically generates a socket file to use.
This will also merge with any sslh.socket.d drop in configuration but will be
overriden by a /etc/systemd/system/sslh.socket file.
overridden by a /etc/systemd/system/sslh.socket file.
To use the generator place it in /usr/lib/systemd/system-generators and then
call systemctl daemon-reload after any changes to /etc/sslh.cfg to generate
@@ -196,6 +192,8 @@ will wait for incoming UDP packets, run the probes in the
usual fashion, and forward packets to the appropriate
target. `sslh` will then remember the association between
remote host to target server for 60 seconds by default,
which can be overriden with `udp_timeout`. This allows to
which can be overridden with `udp_timeout`. This allows to
process both single-datagram protocols such as DNS, and
connection-based protocols such as QUIC.
An example for supporting QUIC is shown in `example.cfg`.
+1 -1
View File
@@ -60,7 +60,7 @@ this scheme -- let me know if you manage that:
ip rule add fwmark 0x1 lookup 100
ip route add local 0.0.0.0/0 dev lo table 100
Tranparent proxying with IPv6 is similarly set up as follows:
Transparent proxying with IPv6 is similarly set up as follows:
# Set route_localnet = 1 on all interfaces so that ssl can use "localhost" as destination
# Not sure if this is needed for ipv6 though
+3 -3
View File
@@ -365,7 +365,7 @@ static int clcpy(config_type type, void* target, const void* cl_arg)
return 0;
}
/* Copy the value of a string argument to arbitary memory
/* Copy the value of a string argument to arbitrary memory
* location that must be large enough, converting on the way
* (i.e. CFG_INT gets atoi() and so on) */
/* 0: success
@@ -862,7 +862,7 @@ static int set_target_fields(void* target_addr, struct compound_cl_arg* arg, con
if (pmatch[pmatch_cnt].rm_so == -1) {
/* This should not happen as regexec() did
* match before, unless there is a
* discrepency between the regex and the
* discrepancy between the regex and the
* number of backreferences */
return 0;
}
@@ -1155,7 +1155,7 @@ static void scalar_to_string(char** strp, config_setting_t* s)
/* Typesets all the settings in a configuration as a
* newly-allocated string. The string management is caller's
* responsability.
* responsibility.
* Returns the number of scalars in the configuration */
static int cfg_as_string(config_setting_t* parent, const char* path, char** strp)
{
+1 -1
View File
@@ -1,6 +1,6 @@
/* echosrv: a simple line echo server with optional prefix adding.
*
* echsrv --listen localhost6:1234 --prefix "ssl: "
* echosrv --listen localhost6:1234 --prefix "ssl: "
*
* This will bind to 1234, and echo every line pre-pending "ssl: ". This is
* used for testing: we create several such servers with different prefixes,
+5 -2
View File
@@ -48,7 +48,10 @@ gap_array* gap_init(int len)
gap->len = gap_len_alloc(elem_size);
if (gap->len < len) gap->len = len;
gap->array = malloc(gap->len * elem_size);
if (!gap->array) return NULL;
if (!gap->array) {
free(gap);
return NULL;
}
for (int i = 0; i < gap->len; i++)
gap->array[i] = NULL;
@@ -99,7 +102,7 @@ int gap_remove_ptr(gap_array* gap, void* ptr, int len)
else
return -1;
for (i = start; i < len; i++) {
for (i = start; i < len - 1; i++) {
gap->array[i] = gap->array[i+1];
}
+5
View File
@@ -32,6 +32,11 @@ static inline int __attribute__((unused)) gap_set(gap_array* gap, int index, voi
static inline void* __attribute__((unused)) gap_get(gap_array* gap, int index)
{
/* sslh-ev routinely reads before it writes. It's not clear if it should be
* its job to check the length (and add a gap_getlen()), or if it should be
* gap_get()'s job. This will do for now */
if (index >= gap->len) return NULL;
return gap->array[index];
}
+5 -5
View File
@@ -10,14 +10,14 @@ fi
if [ ! -d .git ] || ! `(git status | grep -q "On branch") 2> /dev/null`; then
# If we don't have git, we can't work out what
# version this is. It must have been downloaded as a
# zip file.
# zip file.
# If downloaded from the release page, the directory
# has the version number.
release=`pwd | sed s/.*sslh-// | grep "[[:digit:]]"`
if [ "x$release" = "x" ]; then
# If downloaded from the head, Github creates the
# If downloaded from the head, GitHub creates the
# zip file with all files dated from the last
# change: use the Makefile's modification time as a
# release number
@@ -28,7 +28,7 @@ fi
if [ -d .git ] && head=`git rev-parse --verify HEAD 2>/dev/null`; then
# generate the version info based on the tag
release=`(git describe --tags || git --describe || git describe --all --long) \
2>/dev/null | tr -d '\n'`
2>/dev/null | tr -s '/' '-' | tr -d '\n'`
# Are there uncommitted changes?
git update-index --refresh --unmerged > /dev/null
+1 -1
View File
@@ -5,7 +5,7 @@
# Tests scripts are in *.tst files.
# Corresponding output is put in *.out.
# Reference output is put in *.ref.
# Any discrepency will be reported!
# Any discrepancy will be reported!
use strict;
+156
View File
@@ -0,0 +1,156 @@
/*
* Setup a sandbox using the Landlock LSM, if available.
# Copyright (C) 2023 Yves Rutschle
#
# This program is free software; you can redistribute it
# and/or modify it under the terms of the GNU General Public
# License as published by the Free Software Foundation; either
# version 2 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 General Public License for more
# details.
#
# The full text for the General Public License is here:
# http://www.gnu.org/licenses/gpl.html
#
*/
#include "config.h"
#include "log.h"
#ifdef HAVE_LANDLOCK
#define _GNU_SOURCE
#include <linux/landlock.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
/* Ubuntu 22.04 does not have this symbol */
#ifndef LANDLOCK_ACCESS_FS_REFER
#define LANDLOCK_ACCESS_FS_REFER (1ULL << 13)
#endif
#ifndef landlock_create_ruleset
static inline int
landlock_create_ruleset(const struct landlock_ruleset_attr *const attr,
const size_t size, const __u32 flags)
{
return syscall(__NR_landlock_create_ruleset, attr, size, flags);
}
#endif
#ifndef landlock_add_rule
static inline int landlock_add_rule(const int ruleset_fd,
const enum landlock_rule_type rule_type,
const void *const rule_attr,
const __u32 flags)
{
return syscall(__NR_landlock_add_rule, ruleset_fd, rule_type, rule_attr,
flags);
}
#endif
#ifndef landlock_restrict_self
static inline int landlock_restrict_self(const int ruleset_fd,
const __u32 flags)
{
return syscall(__NR_landlock_restrict_self, ruleset_fd, flags);
}
#endif
typedef enum {
LL_TREE,
LL_FILE
} ll_obj_type;
static int add_path_ro(int ruleset_fd, ll_obj_type otype, const char* path)
{
int fd = open(path, O_PATH | O_CLOEXEC);
if (fd < 0) {
print_message(msg_config_error, "Landlock: Failed to open %s: %s\n", path, strerror(errno));
return -1;
}
struct landlock_path_beneath_attr path_beneath = {
.allowed_access = (otype == LL_TREE ? LANDLOCK_ACCESS_FS_READ_DIR : 0 ) |
LANDLOCK_ACCESS_FS_READ_FILE,
.parent_fd = fd,
};
int res = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, &path_beneath, 0);
if (res) {
print_message(msg_config_error, "Landlock: Failed to update the ruleset with \"%s\": %s\n",
path, strerror(errno));
close(path_beneath.parent_fd);
return -1;
}
return 0;
}
void setup_landlock(void)
{
__u64 restrict_rules =
LANDLOCK_ACCESS_FS_EXECUTE |
LANDLOCK_ACCESS_FS_READ_FILE |
LANDLOCK_ACCESS_FS_READ_DIR |
LANDLOCK_ACCESS_FS_WRITE_FILE |
LANDLOCK_ACCESS_FS_REMOVE_DIR |
LANDLOCK_ACCESS_FS_REMOVE_FILE |
LANDLOCK_ACCESS_FS_MAKE_CHAR |
LANDLOCK_ACCESS_FS_MAKE_DIR |
LANDLOCK_ACCESS_FS_MAKE_REG |
LANDLOCK_ACCESS_FS_MAKE_SOCK |
LANDLOCK_ACCESS_FS_MAKE_FIFO |
LANDLOCK_ACCESS_FS_MAKE_BLOCK |
LANDLOCK_ACCESS_FS_MAKE_SYM |
LANDLOCK_ACCESS_FS_REFER;
struct landlock_ruleset_attr ruleset_attr = {
.handled_access_fs = restrict_rules
};
/* ruleset_addr.handled_access_fs contains all rights that will be restricted
* unless explicitly added */
int ruleset_fd = landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
if (ruleset_fd < 0) {
print_message(msg_config_error, "Landlock: Failed to create a ruleset");
return;
}
/* Access to libraries, to be able to fork */
add_path_ro(ruleset_fd, LL_TREE, "/lib");
add_path_ro(ruleset_fd, LL_TREE, "/usr/lib");
add_path_ro(ruleset_fd, LL_FILE, "/etc/ld.so.cache"); /* To avoid searching all libs... */
/* Files to resolve names (required when dynamic resolution is used) */
add_path_ro(ruleset_fd, LL_FILE, "/etc/hosts");
add_path_ro(ruleset_fd, LL_FILE, "/etc/resolv.conf");
add_path_ro(ruleset_fd, LL_FILE, "/etc/nsswitch.conf");
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
print_message(msg_config_error, "Landlock: Failed to restrict privileges");
return;
}
if (landlock_restrict_self(ruleset_fd, 0)) {
print_message(msg_config_error, "Landlock: Failed to enforce ruleset");
return;
}
close(ruleset_fd);
print_message(msg_config, "Landlock: all restricted\n");
}
#else /* HAVE_LANDLOCK */
void setup_landlock(void)
{
print_message(msg_config, "Landlock: not built in\n");
return;
}
#endif /* HAVE_LANDLOCK */
+9 -4
View File
@@ -107,10 +107,11 @@ void print_message(msg_info info, const char* str, ...)
{
va_list ap;
va_start(ap, str);
if ((*info.verbose & MSG_STDOUT) && ! cfg.inetd)
if ((*info.verbose & MSG_STDOUT) && ! cfg.inetd) {
va_start(ap, str);
vfprintf(stderr, str, ap);
va_end(ap);
}
if (*info.verbose & MSG_SYSLOG) {
va_start(ap, str);
@@ -194,7 +195,11 @@ void log_connection(struct connection_desc* desc, const struct connection *cnx)
if (!desc) {
desc = &d;
get_connection_desc(desc, cnx);
if (!get_connection_desc(desc, cnx)) {
print_message(msg_connections, "%s: lost incoming connection\n",
cnx->proto->name);
return;
}
}
print_message(msg_connections, "%s:connection from %s to %s forwarded from %s to %s\n",
+2 -2
View File
@@ -173,13 +173,13 @@ static int is_openvpn_protocol (const char*p,ssize_t len, struct sslhcfg_protoco
* whereas the packet id is increased with every transmitted datagram.
*/
if (len <= OVPN_HARD_RESET_PACKET_ID_OFFSET(OVPN_HMAC_128))
if (len <= OVPN_HARD_RESET_PACKET_ID_OFFSET(OVPN_HMAC_128) + sizeof(uint32_t))
return PROBE_NEXT;
if (ntohl(*(uint32_t*)(p + OVPN_HARD_RESET_PACKET_ID_OFFSET(OVPN_HMAC_128))) <= 5u)
return PROBE_MATCH;
if (len <= OVPN_HARD_RESET_PACKET_ID_OFFSET(OVPN_HMAC_160))
if (len <= OVPN_HARD_RESET_PACKET_ID_OFFSET(OVPN_HMAC_160) + sizeof(uint32_t))
return PROBE_NEXT;
if (ntohl(*(uint32_t*)(p + OVPN_HARD_RESET_PACKET_ID_OFFSET(OVPN_HMAC_160))) <= 5u)
+1 -1
View File
@@ -18,7 +18,7 @@
#CONFIG=/etc/sslh.cfg
#
# Extra option to pass on comand line
# Extra option to pass on command line
# Those can supersede configuration file settings
#
#OPTIONS=
+1 -1
View File
@@ -8,7 +8,7 @@
# but many connection attempts from the same
# origin is reason enough to block.
#
# Verion: 2014-03-28
# Version: 2014-03-28
[INCLUDES]
+24 -1
View File
@@ -1,5 +1,5 @@
/* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README)
* on Sun Sep 11 21:43:25 2022.
* on Tue Jan 2 21:25:47 2024.
# conf2struct: generate libconf parsers that read to structs
# Copyright (C) 2018-2021 Yves Rutschle
@@ -471,6 +471,7 @@ struct arg_file* sslhcfg_conffile;
struct arg_str* sslhcfg_listen;
struct arg_str* sslhcfg_ssh;
struct arg_str* sslhcfg_tls;
struct arg_str* sslhcfg_ssl;
struct arg_str* sslhcfg_openvpn;
struct arg_str* sslhcfg_tinc;
struct arg_str* sslhcfg_wireguard;
@@ -1333,6 +1334,15 @@ static struct compound_cl_target sslhcfg_openvpn_targets [] = {
{ 0 }
};
static struct compound_cl_target sslhcfg_ssl_targets [] = {
{ & table_sslhcfg_protocols[0], 0, .value.def_string = "tls" },
{ & table_sslhcfg_protocols[1], 1, .value.def_string = "0" },
{ & table_sslhcfg_protocols[2], 2, .value.def_string = "0" },
{ & table_sslhcfg_protocols[10], 0, .value.def_int = 1 },
{ & table_sslhcfg_protocols[7], 0, .value.def_bool = 1 },
{ 0 }
};
static struct compound_cl_target sslhcfg_tls_targets [] = {
{ & table_sslhcfg_protocols[0], 0, .value.def_string = "tls" },
{ & table_sslhcfg_protocols[1], 1, .value.def_string = "0" },
@@ -1395,6 +1405,18 @@ static struct compound_cl_arg compound_cl_args[] = {
.override_const = "tls",
},
{ /* arg: ssl */
.regex = "(.+):(\\w+)",
.arg_cl = & sslhcfg_ssl,
.base_entry = & table_sslhcfg [26],
.targets = sslhcfg_ssl_targets,
.override_desc = & table_sslhcfg_protocols [0],
.override_matchindex = 0,
.override_const = "tls",
},
{ /* arg: openvpn */
.regex = "(.+):(\\w+)",
.arg_cl = & sslhcfg_openvpn,
@@ -2199,6 +2221,7 @@ int sslhcfg_cl_parse(int argc, char* argv[], struct sslhcfg_item* cfg)
sslhcfg_listen = arg_strn("p", "listen", "<host:port>", 0, 10, "Listen on host:port"),
sslhcfg_ssh = arg_strn(NULL, "ssh", "<host:port>", 0, 10, "Set up ssh target"),
sslhcfg_tls = arg_strn(NULL, "tls", "<host:port>", 0, 10, "Set up TLS/SSL target"),
sslhcfg_ssl = arg_strn(NULL, "ssl", "<host:port>", 0, 10, "Set up TLS/SSL target"),
sslhcfg_openvpn = arg_strn(NULL, "openvpn", "<host:port>", 0, 10, "Set up OpenVPN target"),
sslhcfg_tinc = arg_strn(NULL, "tinc", "<host:port>", 0, 10, "Set up tinc target"),
sslhcfg_wireguard = arg_strn(NULL, "wireguard", "<host:port>", 0, 10, "Set up WireGuard target"),
+1 -1
View File
@@ -1,5 +1,5 @@
/* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README)
* on Sun Sep 11 21:43:25 2022.
* on Tue Jan 2 21:25:47 2024.
# conf2struct: generate libconf parsers that read to structs
# Copyright (C) 2018-2021 Yves Rutschle
-6
View File
@@ -39,8 +39,6 @@ struct watchers {
struct listen_endpoint* listen_sockets;
gap_array* fd2ls; /* Array indexed by file descriptor, pointing to listen_sockets */
int max_fd; /* legacy to be removed, still required for UDP */
};
static void cnx_read_cb(EV_P_ ev_io *w, int revents);
@@ -80,8 +78,6 @@ void watchers_add_read(watchers* w, int fd)
gap_set(w->ev_ior, fd, io);
}
ev_io_start(loop, io);
if (fd > w->max_fd) w->max_fd = fd + 1;
}
void watchers_del_read(watchers* w, int fd)
@@ -101,8 +97,6 @@ void watchers_add_write(watchers* w, int fd)
gap_set(w->ev_iow, fd, io);
}
ev_io_start(loop, io);
if (fd > w->max_fd) w->max_fd = fd + 1;
}
void watchers_del_write(watchers* w, int fd)
+29
View File
@@ -164,6 +164,13 @@ void set_listen_procname(struct listen_endpoint *listen_socket)
}
/* At least MacOS does not know these two options, so define them to something
* equivalent for our use case */
#ifndef ENONET
#define ENONET EWOULDBLOCK
#endif
/* /MacOS kludge */
/* TCP listener: connections, fork a child for each new connection
* IN:
* endpoint: array of listening endpoint objects
@@ -177,6 +184,26 @@ void tcp_listener(struct listen_endpoint* endpoint, int num_endpoints, int activ
while (1) {
in_socket = accept(endpoint[active_endpoint].socketfd, 0, 0);
CHECK_RES_RETURN(in_socket, "accept", /*void*/ );
if (in_socket == -1) {
print_message(msg_system_error, "%s:%d:%s:%d:%s\n",
__FILE__, __LINE__, "accept", errno, strerror(errno));
switch(in_socket) {
case ENETDOWN: /* accept(2) cites all these errnos as "you should retry" */
case EPROTO:
case ENOPROTOOPT:
case EHOSTDOWN:
case ENONET:
case EHOSTUNREACH:
case EOPNOTSUPP:
case ENETUNREACH:
case ECONNABORTED:
continue;
default: /* Otherwise, it's something wrong in our parameters, we fail */
return;
}
}
print_message(msg_fd, "accepted fd %d\n", in_socket);
switch(fork()) {
@@ -223,6 +250,8 @@ void main_loop(struct listen_endpoint listen_sockets[], int num_addr_listen)
print_message(msg_config_error, "UDP not (yet?) supported in sslh-fork\n");
else
tcp_listener(listen_sockets, num_addr_listen, i);
exit(0);
break;
/* We're in the parent, we don't need to do anything */
+12 -5
View File
@@ -60,20 +60,26 @@ static void printcaps(void) {
static void printsettings(void)
{
char buf[NI_MAXHOST];
char buf[NI_MAXHOST + 256]; /* 256 > " family %d %d" for reasonable ints */
int i;
struct sslhcfg_protocols_item *p;
for (i = 0; i < cfg.protocols_len; i++ ) {
p = &cfg.protocols[i];
strcpy(buf, "resolve on forward");
if (!p->resolve_on_forward) {
sprintaddr(buf, sizeof(buf), p->saddr);
int len = strlen(buf);
sprintf(buf+len, " family %d %d",
p->saddr->ai_family,
p->saddr->ai_addr->sa_family);
}
print_message(msg_config,
"%s addr: %s. libwrap service: %s log_level: %d family %d %d [%s] [%s] [%s]\n",
"%s addr: %s. libwrap service: %s log_level: %d [%s] [%s] [%s]\n",
p->name,
sprintaddr(buf, sizeof(buf), p->saddr),
buf,
p->service,
p->log_level,
p->saddr->ai_family,
p->saddr->ai_addr->sa_family,
p->keepalive ? "keepalive" : "",
p->fork ? "fork" : "",
p->transparent ? "transparent" : ""
@@ -277,6 +283,7 @@ int main(int argc, char *argv[], char* envp[])
if (cfg.user || cfg.chroot)
drop_privileges(cfg.user, cfg.chroot);
setup_landlock();
printcaps();
+2 -2
View File
@@ -180,9 +180,9 @@ void main_loop(struct listen_endpoint listen_sockets[], int num_addr_listen)
for (i = 0; i < fd_info.num_probing; i++) {
struct connection* cnx = gap_get(fd_info.probing_list, i);
if (!cnx || cnx->state != ST_PROBING) {
print_message(msg_int_error, "Inconsistent probing: cnx=%0xp\n", cnx);
print_message(msg_int_error, "Inconsistent probing: cnx=0x%p\n", cnx);
if (cnx)
print_message(msg_int_error, "Inconsistent probing: state=%d\n", cnx);
print_message(msg_int_error, "Inconsistent probing: state=%d\n", cnx->state);
exit(1);
}
if (cnx->probe_timeout < time(NULL)) {
+13
View File
@@ -186,6 +186,19 @@ cl_groups: (
{ path: "tfo_ok"; value: 1 }
);
},
# Redundant with the --tls setting before, for backwards compatibility
{ name: "ssl"; pattern: "(.+):(\w+)"; description: "Set up TLS/SSL target";
list: "protocols";
override: "name";
argdesc: "<host:port>";
targets: (
{ path: "name"; value: "tls" },
{ path: "host"; value: "$1" },
{ path: "port"; value: "$2" },
{ path: "log_level"; value: 1 },
{ path: "tfo_ok"; value: 1 }
);
},
{ name: "openvpn"; pattern: "(.+):(\w+)"; description: "Set up OpenVPN target";
list: "protocols";
override: "name";
+6 -2
View File
@@ -55,9 +55,12 @@ static int get_listen_from_conf(const char *filename, char **listen[]) {
config_setting_source_line(addr));
return -1;
} else {
(*listen)[i] = malloc(strlen(resolve_listen(hostname, port)));
char *resolved_listen = resolve_listen(hostname, port);
(*listen)[i] = malloc(strlen(resolved_listen));
CHECK_ALLOC((*listen)[i], "malloc");
strcpy((*listen)[i], resolve_listen(hostname, port));
strcpy((*listen)[i], resolved_listen);
free(resolved_listen);
}
}
}
@@ -125,6 +128,7 @@ static int gen_sslh_config(char *runtime_unit_dir) {
strcpy(runtime_conf, runtime_unit_dir);
strcat(runtime_conf, unit_file);
runtime_conf_fd = fopen(runtime_conf, "w");
free(runtime_conf);
}
+1 -1
View File
@@ -396,7 +396,7 @@ if ($RB_RESOLVE_ADDRESS) {
my $sslh_pid;
if (!($sslh_pid = fork)) {
my $user = (getpwuid $<)[0]; # Run under current username
exec "./sslh-select -v 3 -f -u $user --listen blahblah.dontexist:9000 --ssh $ssh_address --tls $ssl_address -P $pidfile";
exec "./sslh-select -v 3 -f -u $user --listen blahblah.nonexistent:9000 --ssh $ssh_address --tls $ssl_address -P $pidfile";
}
warn "spawned $sslh_pid\n";
waitpid $sslh_pid, 0;
+4 -4
View File
@@ -14,7 +14,7 @@
use strict;
use IO::Socket::INET6;
use Data::Dumper;
use Conf::Libconfig;
use Conf::Libconfig 1.0.3;
## BEGIN TEST CONFIG
@@ -44,7 +44,7 @@ $conf->read_file("test.cfg");
# Pick one address for TCP and one for UDP
my @listen = @{$conf->fetch_array("listen")};
my @listen = @{$conf->value("listen")};
my ($sslh_tcp_address, $sslh_udp_address);
foreach my $l (@listen) {
@@ -186,7 +186,7 @@ sub udp_client {
}
}
foreach my $p (@{$conf->fetch_array("protocols")}) {
foreach my $p (@{$conf->value("protocols")}) {
if (!fork) {
my $udp = $p->{is_udp} ? "--udp" : "";
my $cmd = "./echosrv $udp -p $p->{host}:$p->{port} --prefix '$p->{name}: ' 2> /dev/null";
@@ -203,7 +203,7 @@ sleep 2; # Let echosrv's and sslh start
my ($c_in, $c_out);
pipe $c_in, $c_out;
my @protocols = @{$conf->fetch_array("protocols")};
my @protocols = @{$conf->value("protocols")};
if (!fork) {
# Process that starts all the clients
+4 -6
View File
@@ -67,14 +67,16 @@ static void shovel(struct connection *cnx, int active_fd, struct loop_info* fd_i
}
/* Returns the queue index that contains the specified file descriptor */
/* Returns the queue index that contains the specified file descriptor.
* This is called by the *_process functions, which got cnx from the fd, so it
* is impossible for fd to not be cnx, hence we die if it happens */
static int active_queue(struct connection* cnx, int fd)
{
if (cnx->q[0].fd == fd) return 0;
if (cnx->q[1].fd == fd) return 1;
print_message(msg_int_error, "file descriptor %d not found in connection object\n", fd);
return -1;
exit(1);
}
/* Process a TCP read event on the specified file descriptor */
@@ -156,13 +158,9 @@ static int connect_queue(struct connection* cnx,
log_connection(NULL, cnx);
flush_deferred(q);
if (q->deferred_data) {
/*
FD_SET(q->fd, &fd_info->watchers->fds_w);
FD_CLR(cnx->q[0].fd, &fd_info->watchers->fds_r); */
watchers_add_write(fd_info->watchers, q->fd);
watchers_del_read(fd_info->watchers, cnx->q[0].fd);
}
/* FD_SET(q->fd, &fd_info->watchers->fds_r); */
watchers_add_read(fd_info->watchers, q->fd);
collection_add_fd(fd_info->collection, cnx, q->fd);
return q->fd;
+1
View File
@@ -65,6 +65,7 @@ static void tcp_protocol_list_init(void)
if (!p->is_udp) {
tcp_protocols_len++;
tcp_protocols = realloc(tcp_protocols, tcp_protocols_len * sizeof(*tcp_protocols));
CHECK_ALLOC(tcp_protocols, "realloc");
tcp_protocols[tcp_protocols_len-1] = p;
}
}
+2 -1
View File
@@ -42,7 +42,7 @@ listen:
protocols:
(
{ name: "ssh"; host: "localhost"; port: "9000"; fork: true; transparent: true; },
{ name: "ssh"; host: "localhost"; port: "9000"; fork: true; transparent: true; resolve_on_forward: true; },
{ name: "socks5"; host: "localhost"; port: "9001"; },
{ name: "http"; host: "localhost"; port: "9002"; },
{ name: "tinc"; host: "localhost"; port: "9003"; },
@@ -53,6 +53,7 @@ protocols:
{ name: "regex"; host: "ip4-localhost"; is_udp: true; port: "9020";
udp_timeout: 30;
regex_patterns: [ "^foo" ];
resolve_on_forward: true;
},
{ name: "regex"; host: "localhost"; port: "9011";
regex_patterns: [ "^foo", "^bar" ];
+37 -15
View File
@@ -36,10 +36,10 @@
static int cnx_cmp(struct connection* cnx1, struct connection* cnx2)
{
struct sockaddr* addr1 = &cnx1->client_addr;
struct sockaddr_storage* addr1 = &cnx1->client_addr;
socklen_t addrlen1 = cnx1->addrlen;
struct sockaddr* addr2 = &cnx2->client_addr;
struct sockaddr_storage* addr2 = &cnx2->client_addr;
socklen_t addrlen2 = cnx2->addrlen;
if (addrlen1 != addrlen2) return -1;
@@ -52,13 +52,13 @@ static int cnx_cmp(struct connection* cnx1, struct connection* cnx2)
* lowest bytes of remote port */
static int hash_make_key(hash_item new)
{
struct sockaddr* addr = &new->client_addr;
struct sockaddr_storage* addr = &new->client_addr;
//socklen_t addrlen = new->addrlen;
struct sockaddr_in* addr4;
struct sockaddr_in6* addr6;
int out;
switch (addr->sa_family) {
switch (((struct sockaddr*)addr)->sa_family) {
case AF_INET:
addr4 = (struct sockaddr_in*)addr;
out = addr4->sin_port;
@@ -86,13 +86,14 @@ static void udp_protocol_list_init(void)
if (p->is_udp) {
udp_protocols_len++;
udp_protocols = realloc(udp_protocols, udp_protocols_len * sizeof(*udp_protocols));
CHECK_ALLOC(udp_protocols, "realloc");
udp_protocols[udp_protocols_len-1] = p;
}
}
}
/* Configuration sanity check for UDP:
* - If there is a listening addres, there must be at least one target
* - If there is a listening address, there must be at least one target
*/
static void udp_sanity_check(void)
{
@@ -129,7 +130,7 @@ void udp_init(struct loop_info* fd_info)
* If yes, returns file descriptor of connection
* If not, returns -1
* */
static int known_source(hash* h, struct sockaddr* addr, socklen_t addrlen)
static int known_source(hash* h, struct sockaddr_storage* addr, socklen_t addrlen)
{
struct connection search;
search.client_addr = *addr;
@@ -221,6 +222,28 @@ static void mark_active(struct connection* cnx)
}
/* Creates a new non-blocking socket */
static int nonblocking_socket(struct sslhcfg_protocols_item* proto)
{
int res;
if (proto->resolve_on_forward) {
res = resolve_split_name(&(proto->saddr), proto->host,
proto->port);
if (res) return -1;
}
int out = socket(proto->saddr->ai_family, SOCK_DGRAM, 0);
res = set_nonblock(out);
if (res == -1) {
print_message(msg_system_error, "%s:%d:%s:%d:%s\n", __FILE__, __LINE__, "udp:socket:nonblock", errno, strerror(errno));
close(out);
return -1;
}
return out;
}
/* Process UDP coming from outside (client towards server)
* If it's a new source, probe; otherwise, forward to previous target
* Returns: newly allocate connections, for new connections
@@ -229,7 +252,7 @@ static void mark_active(struct connection* cnx)
struct connection* udp_c2s_forward(int sockfd, struct loop_info* fd_info)
{
char addr_str[NI_MAXHOST+1+NI_MAXSERV+1];
struct sockaddr src_addr;
struct sockaddr_storage src_addr;
struct addrinfo addrinfo;
struct sslhcfg_protocols_item* proto;
cnx_collection* collection = fd_info->collection;
@@ -237,7 +260,7 @@ struct connection* udp_c2s_forward(int sockfd, struct loop_info* fd_info)
ssize_t len;
socklen_t addrlen;
int res, target, out = -1;
char data[65536]; /* Theoritical max is 65507 (https://en.wikipedia.org/wiki/User_Datagram_Protocol).
char data[65536]; /* Theoretical max is 65507 (https://en.wikipedia.org/wiki/User_Datagram_Protocol).
This will do. Dynamic allocation is possible with the MSG_PEEK flag in recvfrom(2), but that'd imply
malloc/free overhead for each packet, when really 64K is not that much */
@@ -245,13 +268,13 @@ struct connection* udp_c2s_forward(int sockfd, struct loop_info* fd_info)
udp_timeouts(fd_info);
addrlen = sizeof(src_addr);
len = recvfrom(sockfd, data, sizeof(data), 0, &src_addr, &addrlen);
len = recvfrom(sockfd, data, sizeof(data), 0, (struct sockaddr*) &src_addr, &addrlen);
if (len < 0) {
perror("recvfrom");
return NULL;
}
target = known_source(fd_info->hash_sources, &src_addr, addrlen);
addrinfo.ai_addr = &src_addr;
addrinfo.ai_addr = (struct sockaddr*) &src_addr;
addrinfo.ai_addrlen = addrlen;
print_message(msg_probe_info, "received %ld UDP from %d:%s\n",
len, target, sprintaddr(addr_str, sizeof(addr_str), &addrinfo));
@@ -266,9 +289,8 @@ struct connection* udp_c2s_forward(int sockfd, struct loop_info* fd_info)
return NULL;
}
out = socket(proto->saddr->ai_family, SOCK_DGRAM, 0);
res = set_nonblock(out);
CHECK_RES_RETURN(res, "udp:socket:nonblock", NULL);
out = nonblocking_socket(proto);
if (out == -1) return NULL;
struct connection* cnx = collection_alloc_cnx_from_fd(collection, out);
if (!cnx) return NULL;
target = out;
@@ -281,7 +303,7 @@ struct connection* udp_c2s_forward(int sockfd, struct loop_info* fd_info)
res = new_source(fd_info->hash_sources, cnx);
if (res == -1) {
print_message(msg_connections_error, "Out of hash space for new incoming UDP connection -- increaѕe udp_max_connections");
print_message(msg_connections_error, "Out of hash space for new incoming UDP connection -- increase udp_max_connections");
collection_remove_cnx(collection, cnx);
return NULL;
}
@@ -308,7 +330,7 @@ void udp_s2c_forward(struct connection* cnx)
if ((res == -1) && ((errno == EAGAIN) || (errno == EWOULDBLOCK))) return;
CHECK_RES_DIE(res, "udp_listener/recvfrom");
res = sendto(cnx->local_endpoint, data, res, 0,
&cnx->client_addr, cnx->addrlen);
(struct sockaddr*)&cnx->client_addr, cnx->addrlen);
mark_active(cnx);
}