mirror of
https://github.com/yrutschle/sslh.git
synced 2025-04-24 12:30:54 +03:00
Compare commits
No commits in common. "master" and "v1.16" have entirely different histories.
63
.github/workflows/container-build.yaml
vendored
63
.github/workflows/container-build.yaml
vendored
@ -1,63 +0,0 @@
|
|||||||
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 }}
|
|
8
.gitignore
vendored
8
.gitignore
vendored
@ -3,14 +3,8 @@
|
|||||||
*.o
|
*.o
|
||||||
cscope.*
|
cscope.*
|
||||||
echosrv
|
echosrv
|
||||||
libsslh.a
|
|
||||||
sslh-fork
|
sslh-fork
|
||||||
sslh-select
|
sslh-select
|
||||||
sslh-ev
|
|
||||||
systemd-sslh-generator
|
|
||||||
sslh.8.gz
|
sslh.8.gz
|
||||||
tags
|
tags
|
||||||
/config.status
|
version.h
|
||||||
/config.log
|
|
||||||
/config.h
|
|
||||||
/Makefile
|
|
||||||
|
339
COPYING
339
COPYING
@ -1,339 +0,0 @@
|
|||||||
GNU GENERAL PUBLIC LICENSE
|
|
||||||
Version 2, June 1991
|
|
||||||
|
|
||||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
Everyone is permitted to copy and distribute verbatim copies
|
|
||||||
of this license document, but changing it is not allowed.
|
|
||||||
|
|
||||||
Preamble
|
|
||||||
|
|
||||||
The licenses for most software are designed to take away your
|
|
||||||
freedom to share and change it. By contrast, the GNU General Public
|
|
||||||
License is intended to guarantee your freedom to share and change free
|
|
||||||
software--to make sure the software is free for all its users. This
|
|
||||||
General Public License applies to most of the Free Software
|
|
||||||
Foundation's software and to any other program whose authors commit to
|
|
||||||
using it. (Some other Free Software Foundation software is covered by
|
|
||||||
the GNU Lesser General Public License instead.) You can apply it to
|
|
||||||
your programs, too.
|
|
||||||
|
|
||||||
When we speak of free software, we are referring to freedom, not
|
|
||||||
price. Our General Public Licenses are designed to make sure that you
|
|
||||||
have the freedom to distribute copies of free software (and charge for
|
|
||||||
this service if you wish), that you receive source code or can get it
|
|
||||||
if you want it, that you can change the software or use pieces of it
|
|
||||||
in new free programs; and that you know you can do these things.
|
|
||||||
|
|
||||||
To protect your rights, we need to make restrictions that forbid
|
|
||||||
anyone to deny you these rights or to ask you to surrender the rights.
|
|
||||||
These restrictions translate to certain responsibilities for you if you
|
|
||||||
distribute copies of the software, or if you modify it.
|
|
||||||
|
|
||||||
For example, if you distribute copies of such a program, whether
|
|
||||||
gratis or for a fee, you must give the recipients all the rights that
|
|
||||||
you have. You must make sure that they, too, receive or can get the
|
|
||||||
source code. And you must show them these terms so they know their
|
|
||||||
rights.
|
|
||||||
|
|
||||||
We protect your rights with two steps: (1) copyright the software, and
|
|
||||||
(2) offer you this license which gives you legal permission to copy,
|
|
||||||
distribute and/or modify the software.
|
|
||||||
|
|
||||||
Also, for each author's protection and ours, we want to make certain
|
|
||||||
that everyone understands that there is no warranty for this free
|
|
||||||
software. If the software is modified by someone else and passed on, we
|
|
||||||
want its recipients to know that what they have is not the original, so
|
|
||||||
that any problems introduced by others will not reflect on the original
|
|
||||||
authors' reputations.
|
|
||||||
|
|
||||||
Finally, any free program is threatened constantly by software
|
|
||||||
patents. We wish to avoid the danger that redistributors of a free
|
|
||||||
program will individually obtain patent licenses, in effect making the
|
|
||||||
program proprietary. To prevent this, we have made it clear that any
|
|
||||||
patent must be licensed for everyone's free use or not licensed at all.
|
|
||||||
|
|
||||||
The precise terms and conditions for copying, distribution and
|
|
||||||
modification follow.
|
|
||||||
|
|
||||||
GNU GENERAL PUBLIC LICENSE
|
|
||||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
|
||||||
|
|
||||||
0. This License applies to any program or other work which contains
|
|
||||||
a notice placed by the copyright holder saying it may be distributed
|
|
||||||
under the terms of this General Public License. The "Program", below,
|
|
||||||
refers to any such program or work, and a "work based on the Program"
|
|
||||||
means either the Program or any derivative work under copyright law:
|
|
||||||
that is to say, a work containing the Program or a portion of it,
|
|
||||||
either verbatim or with modifications and/or translated into another
|
|
||||||
language. (Hereinafter, translation is included without limitation in
|
|
||||||
the term "modification".) Each licensee is addressed as "you".
|
|
||||||
|
|
||||||
Activities other than copying, distribution and modification are not
|
|
||||||
covered by this License; they are outside its scope. The act of
|
|
||||||
running the Program is not restricted, and the output from the Program
|
|
||||||
is covered only if its contents constitute a work based on the
|
|
||||||
Program (independent of having been made by running the Program).
|
|
||||||
Whether that is true depends on what the Program does.
|
|
||||||
|
|
||||||
1. You may copy and distribute verbatim copies of the Program's
|
|
||||||
source code as you receive it, in any medium, provided that you
|
|
||||||
conspicuously and appropriately publish on each copy an appropriate
|
|
||||||
copyright notice and disclaimer of warranty; keep intact all the
|
|
||||||
notices that refer to this License and to the absence of any warranty;
|
|
||||||
and give any other recipients of the Program a copy of this License
|
|
||||||
along with the Program.
|
|
||||||
|
|
||||||
You may charge a fee for the physical act of transferring a copy, and
|
|
||||||
you may at your option offer warranty protection in exchange for a fee.
|
|
||||||
|
|
||||||
2. You may modify your copy or copies of the Program or any portion
|
|
||||||
of it, thus forming a work based on the Program, and copy and
|
|
||||||
distribute such modifications or work under the terms of Section 1
|
|
||||||
above, provided that you also meet all of these conditions:
|
|
||||||
|
|
||||||
a) You must cause the modified files to carry prominent notices
|
|
||||||
stating that you changed the files and the date of any change.
|
|
||||||
|
|
||||||
b) You must cause any work that you distribute or publish, that in
|
|
||||||
whole or in part contains or is derived from the Program or any
|
|
||||||
part thereof, to be licensed as a whole at no charge to all third
|
|
||||||
parties under the terms of this License.
|
|
||||||
|
|
||||||
c) If the modified program normally reads commands interactively
|
|
||||||
when run, you must cause it, when started running for such
|
|
||||||
interactive use in the most ordinary way, to print or display an
|
|
||||||
announcement including an appropriate copyright notice and a
|
|
||||||
notice that there is no warranty (or else, saying that you provide
|
|
||||||
a warranty) and that users may redistribute the program under
|
|
||||||
these conditions, and telling the user how to view a copy of this
|
|
||||||
License. (Exception: if the Program itself is interactive but
|
|
||||||
does not normally print such an announcement, your work based on
|
|
||||||
the Program is not required to print an announcement.)
|
|
||||||
|
|
||||||
These requirements apply to the modified work as a whole. If
|
|
||||||
identifiable sections of that work are not derived from the Program,
|
|
||||||
and can be reasonably considered independent and separate works in
|
|
||||||
themselves, then this License, and its terms, do not apply to those
|
|
||||||
sections when you distribute them as separate works. But when you
|
|
||||||
distribute the same sections as part of a whole which is a work based
|
|
||||||
on the Program, the distribution of the whole must be on the terms of
|
|
||||||
this License, whose permissions for other licensees extend to the
|
|
||||||
entire whole, and thus to each and every part regardless of who wrote it.
|
|
||||||
|
|
||||||
Thus, it is not the intent of this section to claim rights or contest
|
|
||||||
your rights to work written entirely by you; rather, the intent is to
|
|
||||||
exercise the right to control the distribution of derivative or
|
|
||||||
collective works based on the Program.
|
|
||||||
|
|
||||||
In addition, mere aggregation of another work not based on the Program
|
|
||||||
with the Program (or with a work based on the Program) on a volume of
|
|
||||||
a storage or distribution medium does not bring the other work under
|
|
||||||
the scope of this License.
|
|
||||||
|
|
||||||
3. You may copy and distribute the Program (or a work based on it,
|
|
||||||
under Section 2) in object code or executable form under the terms of
|
|
||||||
Sections 1 and 2 above provided that you also do one of the following:
|
|
||||||
|
|
||||||
a) Accompany it with the complete corresponding machine-readable
|
|
||||||
source code, which must be distributed under the terms of Sections
|
|
||||||
1 and 2 above on a medium customarily used for software interchange; or,
|
|
||||||
|
|
||||||
b) Accompany it with a written offer, valid for at least three
|
|
||||||
years, to give any third party, for a charge no more than your
|
|
||||||
cost of physically performing source distribution, a complete
|
|
||||||
machine-readable copy of the corresponding source code, to be
|
|
||||||
distributed under the terms of Sections 1 and 2 above on a medium
|
|
||||||
customarily used for software interchange; or,
|
|
||||||
|
|
||||||
c) Accompany it with the information you received as to the offer
|
|
||||||
to distribute corresponding source code. (This alternative is
|
|
||||||
allowed only for noncommercial distribution and only if you
|
|
||||||
received the program in object code or executable form with such
|
|
||||||
an offer, in accord with Subsection b above.)
|
|
||||||
|
|
||||||
The source code for a work means the preferred form of the work for
|
|
||||||
making modifications to it. For an executable work, complete source
|
|
||||||
code means all the source code for all modules it contains, plus any
|
|
||||||
associated interface definition files, plus the scripts used to
|
|
||||||
control compilation and installation of the executable. However, as a
|
|
||||||
special exception, the source code distributed need not include
|
|
||||||
anything that is normally distributed (in either source or binary
|
|
||||||
form) with the major components (compiler, kernel, and so on) of the
|
|
||||||
operating system on which the executable runs, unless that component
|
|
||||||
itself accompanies the executable.
|
|
||||||
|
|
||||||
If distribution of executable or object code is made by offering
|
|
||||||
access to copy from a designated place, then offering equivalent
|
|
||||||
access to copy the source code from the same place counts as
|
|
||||||
distribution of the source code, even though third parties are not
|
|
||||||
compelled to copy the source along with the object code.
|
|
||||||
|
|
||||||
4. You may not copy, modify, sublicense, or distribute the Program
|
|
||||||
except as expressly provided under this License. Any attempt
|
|
||||||
otherwise to copy, modify, sublicense or distribute the Program is
|
|
||||||
void, and will automatically terminate your rights under this License.
|
|
||||||
However, parties who have received copies, or rights, from you under
|
|
||||||
this License will not have their licenses terminated so long as such
|
|
||||||
parties remain in full compliance.
|
|
||||||
|
|
||||||
5. You are not required to accept this License, since you have not
|
|
||||||
signed it. However, nothing else grants you permission to modify or
|
|
||||||
distribute the Program or its derivative works. These actions are
|
|
||||||
prohibited by law if you do not accept this License. Therefore, by
|
|
||||||
modifying or distributing the Program (or any work based on the
|
|
||||||
Program), you indicate your acceptance of this License to do so, and
|
|
||||||
all its terms and conditions for copying, distributing or modifying
|
|
||||||
the Program or works based on it.
|
|
||||||
|
|
||||||
6. Each time you redistribute the Program (or any work based on the
|
|
||||||
Program), the recipient automatically receives a license from the
|
|
||||||
original licensor to copy, distribute or modify the Program subject to
|
|
||||||
these terms and conditions. You may not impose any further
|
|
||||||
restrictions on the recipients' exercise of the rights granted herein.
|
|
||||||
You are not responsible for enforcing compliance by third parties to
|
|
||||||
this License.
|
|
||||||
|
|
||||||
7. If, as a consequence of a court judgment or allegation of patent
|
|
||||||
infringement or for any other reason (not limited to patent issues),
|
|
||||||
conditions are imposed on you (whether by court order, agreement or
|
|
||||||
otherwise) that contradict the conditions of this License, they do not
|
|
||||||
excuse you from the conditions of this License. If you cannot
|
|
||||||
distribute so as to satisfy simultaneously your obligations under this
|
|
||||||
License and any other pertinent obligations, then as a consequence you
|
|
||||||
may not distribute the Program at all. For example, if a patent
|
|
||||||
license would not permit royalty-free redistribution of the Program by
|
|
||||||
all those who receive copies directly or indirectly through you, then
|
|
||||||
the only way you could satisfy both it and this License would be to
|
|
||||||
refrain entirely from distribution of the Program.
|
|
||||||
|
|
||||||
If any portion of this section is held invalid or unenforceable under
|
|
||||||
any particular circumstance, the balance of the section is intended to
|
|
||||||
apply and the section as a whole is intended to apply in other
|
|
||||||
circumstances.
|
|
||||||
|
|
||||||
It is not the purpose of this section to induce you to infringe any
|
|
||||||
patents or other property right claims or to contest validity of any
|
|
||||||
such claims; this section has the sole purpose of protecting the
|
|
||||||
integrity of the free software distribution system, which is
|
|
||||||
implemented by public license practices. Many people have made
|
|
||||||
generous contributions to the wide range of software distributed
|
|
||||||
through that system in reliance on consistent application of that
|
|
||||||
system; it is up to the author/donor to decide if he or she is willing
|
|
||||||
to distribute software through any other system and a licensee cannot
|
|
||||||
impose that choice.
|
|
||||||
|
|
||||||
This section is intended to make thoroughly clear what is believed to
|
|
||||||
be a consequence of the rest of this License.
|
|
||||||
|
|
||||||
8. If the distribution and/or use of the Program is restricted in
|
|
||||||
certain countries either by patents or by copyrighted interfaces, the
|
|
||||||
original copyright holder who places the Program under this License
|
|
||||||
may add an explicit geographical distribution limitation excluding
|
|
||||||
those countries, so that distribution is permitted only in or among
|
|
||||||
countries not thus excluded. In such case, this License incorporates
|
|
||||||
the limitation as if written in the body of this License.
|
|
||||||
|
|
||||||
9. The Free Software Foundation may publish revised and/or new versions
|
|
||||||
of the General Public License from time to time. Such new versions will
|
|
||||||
be similar in spirit to the present version, but may differ in detail to
|
|
||||||
address new problems or concerns.
|
|
||||||
|
|
||||||
Each version is given a distinguishing version number. If the Program
|
|
||||||
specifies a version number of this License which applies to it and "any
|
|
||||||
later version", you have the option of following the terms and conditions
|
|
||||||
either of that version or of any later version published by the Free
|
|
||||||
Software Foundation. If the Program does not specify a version number of
|
|
||||||
this License, you may choose any version ever published by the Free Software
|
|
||||||
Foundation.
|
|
||||||
|
|
||||||
10. If you wish to incorporate parts of the Program into other free
|
|
||||||
programs whose distribution conditions are different, write to the author
|
|
||||||
to ask for permission. For software which is copyrighted by the Free
|
|
||||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
|
||||||
make exceptions for this. Our decision will be guided by the two goals
|
|
||||||
of preserving the free status of all derivatives of our free software and
|
|
||||||
of promoting the sharing and reuse of software generally.
|
|
||||||
|
|
||||||
NO WARRANTY
|
|
||||||
|
|
||||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
|
||||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
|
||||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
|
||||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
|
||||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
||||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
|
||||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
|
||||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
|
||||||
REPAIR OR CORRECTION.
|
|
||||||
|
|
||||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
|
||||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
|
||||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
|
||||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
|
||||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
|
||||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
|
||||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
|
||||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
|
||||||
POSSIBILITY OF SUCH DAMAGES.
|
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
How to Apply These Terms to Your New Programs
|
|
||||||
|
|
||||||
If you develop a new program, and you want it to be of the greatest
|
|
||||||
possible use to the public, the best way to achieve this is to make it
|
|
||||||
free software which everyone can redistribute and change under these terms.
|
|
||||||
|
|
||||||
To do so, attach the following notices to the program. It is safest
|
|
||||||
to attach them to the start of each source file to most effectively
|
|
||||||
convey the exclusion of warranty; and each file should have at least
|
|
||||||
the "copyright" line and a pointer to where the full notice is found.
|
|
||||||
|
|
||||||
<one line to give the program's name and a brief idea of what it does.>
|
|
||||||
Copyright (C) <year> <name of author>
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
|
||||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
|
|
||||||
Also add information on how to contact you by electronic and paper mail.
|
|
||||||
|
|
||||||
If the program is interactive, make it output a short notice like this
|
|
||||||
when it starts in an interactive mode:
|
|
||||||
|
|
||||||
Gnomovision version 69, Copyright (C) year name of author
|
|
||||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
|
||||||
This is free software, and you are welcome to redistribute it
|
|
||||||
under certain conditions; type `show c' for details.
|
|
||||||
|
|
||||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
|
||||||
parts of the General Public License. Of course, the commands you use may
|
|
||||||
be called something other than `show w' and `show c'; they could even be
|
|
||||||
mouse-clicks or menu items--whatever suits your program.
|
|
||||||
|
|
||||||
You should also get your employer (if you work as a programmer) or your
|
|
||||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
|
||||||
necessary. Here is a sample; alter the names:
|
|
||||||
|
|
||||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
|
||||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
|
||||||
|
|
||||||
<signature of Ty Coon>, 1 April 1989
|
|
||||||
Ty Coon, President of Vice
|
|
||||||
|
|
||||||
This General Public License does not permit incorporating your program into
|
|
||||||
proprietary programs. If your program is a subroutine library, you may
|
|
||||||
consider it more useful to permit linking proprietary applications with the
|
|
||||||
library. If this is what you want to do, use the GNU Lesser General
|
|
||||||
Public License instead of this License.
|
|
243
ChangeLog
243
ChangeLog
@ -1,238 +1,3 @@
|
|||||||
v2.2.1:
|
|
||||||
Added a boolean setting "is_unix" for listen and
|
|
||||||
protocol entries. This will use the 'host' setting
|
|
||||||
as a path name to a socket file, and connections
|
|
||||||
(listening or connecting) will be performed on Unix
|
|
||||||
socket instead of Internet sockets.
|
|
||||||
|
|
||||||
Support HAProxy's proxyprotocol on the backend
|
|
||||||
server side.
|
|
||||||
|
|
||||||
Lots of documentation about a new, simpler way to
|
|
||||||
perform transparent proxying.
|
|
||||||
|
|
||||||
New "verbose" option that overrides all other
|
|
||||||
verbose settings.
|
|
||||||
|
|
||||||
v2.1.3:
|
|
||||||
Fix Landlock access to /etc/hosts.deny and
|
|
||||||
/etc/hosts.allow.
|
|
||||||
|
|
||||||
v2.1.2:
|
|
||||||
Fix inetd mode.
|
|
||||||
|
|
||||||
v2.1.1:
|
|
||||||
Various minor fixes.
|
|
||||||
|
|
||||||
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 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.
|
|
||||||
|
|
||||||
New log system: instead of --verbose with arbitrary
|
|
||||||
levels, there are now several message classes. Each
|
|
||||||
message class can be set to go to stderr, syslog, or
|
|
||||||
both. Classes are documented in example.cfg.
|
|
||||||
|
|
||||||
UDP connections are now managed in a hash to avoid
|
|
||||||
linear searches. The downside is that the number of
|
|
||||||
UDP connections is a hard limit, configurable with
|
|
||||||
the 'udp_max_connections', which defaults to 1024.
|
|
||||||
Timeouts are managed with lists.
|
|
||||||
|
|
||||||
inetd merges stderr output to what is sent to the
|
|
||||||
client, which is a security issue as it might give
|
|
||||||
information to an attacker. When inetd is activated,
|
|
||||||
stderr is forcibly closed.
|
|
||||||
|
|
||||||
New protocol-level option `resolve_on_forward`,
|
|
||||||
requests that target names are resolved at each
|
|
||||||
connection instead of at startup. Useful for dynamic
|
|
||||||
DNS situations. (Paul Schroeder/milkpirate)
|
|
||||||
|
|
||||||
New probe for MSRDP (akappner).
|
|
||||||
|
|
||||||
v1.22: 17AUG2021
|
|
||||||
sslh-select now supports UDP protocols.
|
|
||||||
Probes specified in the `protocols`
|
|
||||||
configuration entry are tried on incoming packets,
|
|
||||||
TCP or UDP, and forwarded based on the input
|
|
||||||
protocol (an incoming TCP connection will be
|
|
||||||
forwarded as TCP, and same with UDP).
|
|
||||||
This has been tested with DNS as shown in udp.cfg:
|
|
||||||
incoming packets that contain my domain name are
|
|
||||||
assumed to be a DNS request and forwarded
|
|
||||||
accordingly. Note this could cause problems if
|
|
||||||
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 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.
|
|
||||||
|
|
||||||
Added probes for UDP protocols QUICK and Teamspeak.
|
|
||||||
|
|
||||||
Added probes for syslog protocol.
|
|
||||||
|
|
||||||
sslh-select refactored to change linear searches
|
|
||||||
through connections to linear searches through
|
|
||||||
fd_set.
|
|
||||||
|
|
||||||
Fixed a libconfig call to support libconfig 1.7.3.
|
|
||||||
|
|
||||||
Added symbol to support libconfig 1.4.9, still in
|
|
||||||
use in CentOS7.
|
|
||||||
|
|
||||||
Warn about unknown settings in the configuration
|
|
||||||
file.
|
|
||||||
|
|
||||||
Added per-protocol `transparent` option. sslh-fork
|
|
||||||
drops the capability after creating the server-side
|
|
||||||
transparent socket. Transparent now uses CAP_NET_RAW
|
|
||||||
instead of CAP_NET_ADMIN.
|
|
||||||
|
|
||||||
Removed compile-time option to use POSIX regex. Now
|
|
||||||
regex must be PCRE2 (Perl-Compatible). This was in
|
|
||||||
fact the case since v1.21, as PCRE are used to parse
|
|
||||||
the config file.
|
|
||||||
|
|
||||||
v1.21: 11JUL2020
|
|
||||||
WARNING:
|
|
||||||
Moved configuration and command-line management to
|
|
||||||
use conf2struct. Changes are:
|
|
||||||
* `--ssl` and using `name: 'ssl'` in config file is no longer supported, use `tls` instead.
|
|
||||||
* command line option <-F|--config> no longer defaults to /etc/sslh.cfg, so you have to
|
|
||||||
specify it explicitly.
|
|
||||||
* command line option <-v|--verbose> takes a mandatory integer parameter
|
|
||||||
|
|
||||||
Added TCP_FASTOPEN support for client sockets (if
|
|
||||||
tfo_ok is specified in their configuration) and for
|
|
||||||
listening socket, if all client protocols support it.
|
|
||||||
(Craig Andrews)
|
|
||||||
|
|
||||||
Added 'minlength' option to skip a probe if less
|
|
||||||
than that many bytes have been received (mostly for
|
|
||||||
regex)
|
|
||||||
|
|
||||||
Update Let's Encrypt entry in example.cfg for tls-alpn-01
|
|
||||||
challenges; tls-sni-* challenges are now deprecated.
|
|
||||||
|
|
||||||
Log to syslog even if in foreground (for people who
|
|
||||||
use fail2ban)
|
|
||||||
|
|
||||||
Use syslog_facility: "none" to disable syslog
|
|
||||||
output.
|
|
||||||
|
|
||||||
Changed exit code for illegal command line parameter
|
|
||||||
from 1 to 6 (for testing purposes)
|
|
||||||
|
|
||||||
v1.20: 20NOV2018
|
|
||||||
Added support for socks5 protocol (Eugene Protozanov)
|
|
||||||
|
|
||||||
New probing method:
|
|
||||||
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. return PROBE_AGAIN for
|
|
||||||
a long time) could prevent successful matches from
|
|
||||||
subsequent probes. The configuration file needed to
|
|
||||||
take that into account.
|
|
||||||
|
|
||||||
Now, all probes are tried each time new data is
|
|
||||||
found. If any probe matches, use it. If at least one
|
|
||||||
probe requires more data, wait for more. If all
|
|
||||||
probes failed, connect to the last one. So the only
|
|
||||||
thing to know when writing the configuration file is
|
|
||||||
that 'anyprot' needs to be last.
|
|
||||||
|
|
||||||
Test suite heavily refactored; `t` uses `test.cfg`
|
|
||||||
to decide which probes to test and all setup is
|
|
||||||
automatic; probes get tested with 'fast' (entire
|
|
||||||
first message in one packet) and 'slow' (one byte at
|
|
||||||
a time); when SNI/ALPN are defined, all combinations
|
|
||||||
are tested.
|
|
||||||
|
|
||||||
Old 'tls' probe removed, 'sni_alpn' probe renamed as 'tls'.
|
|
||||||
You'll need to change 'sni_alpn' to 'tls' in
|
|
||||||
your configuration file, if ever you used it.
|
|
||||||
|
|
||||||
v1.19: 20JAN2018
|
|
||||||
Added 'syslog_facility' configuration option to
|
|
||||||
specify where to log.
|
|
||||||
|
|
||||||
TLS now supports SNI and ALPN (Travis Burtrum),
|
|
||||||
including support for Let's Encrypt challenges
|
|
||||||
(Jonathan McCrohan)
|
|
||||||
|
|
||||||
ADB probe. (Mike Frysinger)
|
|
||||||
|
|
||||||
Added per-protocol 'fork' option. (Oleg Oshmyan)
|
|
||||||
|
|
||||||
Added chroot option. (Mike Frysinger)
|
|
||||||
|
|
||||||
A truckload of bug fixes and documentation
|
|
||||||
improvements (Various contributors)
|
|
||||||
|
|
||||||
v1.18: 29MAR2016
|
|
||||||
Added USELIBPCRE to make use of regex engine
|
|
||||||
optional.
|
|
||||||
|
|
||||||
Added support for RFC4366 SNI and RFC7301 ALPN
|
|
||||||
(Travis Burtrum)
|
|
||||||
|
|
||||||
Changed connection log to include the name of the probe that
|
|
||||||
triggered.
|
|
||||||
|
|
||||||
Changed configuration file format: 'probe' field is
|
|
||||||
no longer required, 'name' field can now contain
|
|
||||||
'tls' or 'regex', with corresponding options (see
|
|
||||||
example.cfg)
|
|
||||||
Added 'log_level' option to each protocol, which
|
|
||||||
allows to turn off generation of log at each
|
|
||||||
connection.
|
|
||||||
Added 'keepalive' option.
|
|
||||||
|
|
||||||
v1.17: 09MAR2015
|
|
||||||
Support RFC5952-style IPv6 addresses, e.g. [::]:443.
|
|
||||||
|
|
||||||
Transparent proxy support for FreeBSD.
|
|
||||||
(Ruben van Staveren)
|
|
||||||
|
|
||||||
Using -F with no argument will try
|
|
||||||
/etc/sslh/sslh.cfg and then /etc/sslh.cfg as
|
|
||||||
configuration files. (argument to -F can no longer
|
|
||||||
be separated from the option by a space, e.g. must
|
|
||||||
be -Ffoo.cfg)
|
|
||||||
|
|
||||||
Call setgroups() before setgid() (fixes potential
|
|
||||||
privilege escalation).
|
|
||||||
(Lars Vogdt)
|
|
||||||
|
|
||||||
Use portable way of getting modified time for OSX
|
|
||||||
support.
|
|
||||||
(Aaron Madlon-Kay)
|
|
||||||
|
|
||||||
Example configuration for fail2ban.
|
|
||||||
(Every Mouw)
|
|
||||||
|
|
||||||
v1.16: 11FEB2014
|
v1.16: 11FEB2014
|
||||||
Probes made more resilient, to incoming data
|
Probes made more resilient, to incoming data
|
||||||
containing NULLs. Also made them behave properly
|
containing NULLs. Also made them behave properly
|
||||||
@ -242,7 +7,7 @@ v1.16: 11FEB2014
|
|||||||
|
|
||||||
Libcap support: Keep only CAP_NET_ADMIN if started
|
Libcap support: Keep only CAP_NET_ADMIN if started
|
||||||
as root with transparent proxying and dropping
|
as root with transparent proxying and dropping
|
||||||
privileges (enable USELIBCAP in Makefile). This
|
priviledges (enable USELIBCAP in Makefile). This
|
||||||
avoids having to mess with filesystem capabilities.
|
avoids having to mess with filesystem capabilities.
|
||||||
(Sebastian Schmidt/yath)
|
(Sebastian Schmidt/yath)
|
||||||
|
|
||||||
@ -251,7 +16,7 @@ v1.16: 11FEB2014
|
|||||||
actual errors if connections are dropped before
|
actual errors if connections are dropped before
|
||||||
getting to getpeername).
|
getting to getpeername).
|
||||||
|
|
||||||
Set IP_FREEBIND if available to bind to addresses
|
Set IP_FREEDBIND if available to bind to addresses
|
||||||
that don't yet exist.
|
that don't yet exist.
|
||||||
|
|
||||||
v1.15: 27JUL2013
|
v1.15: 27JUL2013
|
||||||
@ -336,7 +101,7 @@ v1.11: 21APR2012
|
|||||||
--user isn't specified, just run as current user.
|
--user isn't specified, just run as current user.
|
||||||
|
|
||||||
No longer create PID file by default, it should be
|
No longer create PID file by default, it should be
|
||||||
explicitly set with --pidfile.
|
explicitely set with --pidfile.
|
||||||
|
|
||||||
No longer log to syslog if in foreground. Logs are
|
No longer log to syslog if in foreground. Logs are
|
||||||
instead output to stderr.
|
instead output to stderr.
|
||||||
@ -427,7 +192,7 @@ v1.8: 15JUL2011
|
|||||||
v1.7: 01FEB2010
|
v1.7: 01FEB2010
|
||||||
Added CentOS init.d script (Andre Krajnik).
|
Added CentOS init.d script (Andre Krajnik).
|
||||||
|
|
||||||
Fixed default ssl address inconsistency, now
|
Fixed default ssl address inconsistancy, now
|
||||||
defaults to "localhost:443" and fixed documentation
|
defaults to "localhost:443" and fixed documentation
|
||||||
accordingly (pointed by Markus Schalke).
|
accordingly (pointed by Markus Schalke).
|
||||||
|
|
||||||
|
38
Dockerfile
38
Dockerfile
@ -1,38 +0,0 @@
|
|||||||
ARG ALPINE_VERSION="latest"
|
|
||||||
ARG TARGET_ARCH="library"
|
|
||||||
|
|
||||||
FROM docker.io/${TARGET_ARCH}/alpine:${ALPINE_VERSION} AS build
|
|
||||||
|
|
||||||
WORKDIR /sslh
|
|
||||||
|
|
||||||
RUN apk add --no-cache \
|
|
||||||
'gcc' \
|
|
||||||
'libconfig-dev' \
|
|
||||||
'make' \
|
|
||||||
'musl-dev' \
|
|
||||||
'pcre2-dev' \
|
|
||||||
'perl' \
|
|
||||||
;
|
|
||||||
|
|
||||||
COPY . /sslh
|
|
||||||
|
|
||||||
RUN ./configure && make sslh-select && strip sslh-select
|
|
||||||
|
|
||||||
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
|
|
100
Makefile
Normal file
100
Makefile
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
# Configuration
|
||||||
|
|
||||||
|
VERSION=$(shell ./genver.sh -r)
|
||||||
|
USELIBCONFIG=1 # Use libconfig? (necessary to use configuration files)
|
||||||
|
USELIBWRAP= # Use libwrap?
|
||||||
|
USELIBCAP= # Use libcap?
|
||||||
|
COV_TEST= # Perform test coverage?
|
||||||
|
PREFIX=/usr/local
|
||||||
|
|
||||||
|
MAN=sslh.8.gz # man page name
|
||||||
|
|
||||||
|
# End of configuration -- the rest should take care of
|
||||||
|
# itself
|
||||||
|
|
||||||
|
ifneq ($(strip $(COV_TEST)),)
|
||||||
|
CFLAGS_COV=-fprofile-arcs -ftest-coverage
|
||||||
|
endif
|
||||||
|
|
||||||
|
CC ?= gcc
|
||||||
|
CFLAGS ?=-Wall -g $(CFLAGS_COV)
|
||||||
|
|
||||||
|
LIBS=
|
||||||
|
OBJS=common.o sslh-main.o probe.o
|
||||||
|
|
||||||
|
ifneq ($(strip $(USELIBWRAP)),)
|
||||||
|
LIBS:=$(LIBS) -lwrap
|
||||||
|
CPPFLAGS+=-DLIBWRAP
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifneq ($(strip $(USELIBCONFIG)),)
|
||||||
|
LIBS:=$(LIBS) -lconfig
|
||||||
|
CPPFLAGS+=-DLIBCONFIG
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifneq ($(strip $(USELIBCAP)),)
|
||||||
|
LIBS:=$(LIBS) -lcap
|
||||||
|
CPPFLAGS+=-DLIBCAP
|
||||||
|
endif
|
||||||
|
|
||||||
|
all: sslh $(MAN) echosrv
|
||||||
|
|
||||||
|
.c.o: *.h
|
||||||
|
$(CC) $(CFLAGS) $(CPPFLAGS) -c $<
|
||||||
|
|
||||||
|
version.h:
|
||||||
|
./genver.sh >version.h
|
||||||
|
|
||||||
|
sslh: sslh-fork sslh-select
|
||||||
|
|
||||||
|
sslh-fork: version.h $(OBJS) sslh-fork.o Makefile common.h
|
||||||
|
$(CC) $(CFLAGS) $(LDFLAGS) -o sslh-fork sslh-fork.o $(OBJS) $(LIBS)
|
||||||
|
#strip sslh-fork
|
||||||
|
|
||||||
|
sslh-select: version.h $(OBJS) sslh-select.o Makefile common.h
|
||||||
|
$(CC) $(CFLAGS) $(LDFLAGS) -o sslh-select sslh-select.o $(OBJS) $(LIBS)
|
||||||
|
#strip sslh-select
|
||||||
|
|
||||||
|
echosrv: $(OBJS) echosrv.o
|
||||||
|
$(CC) $(CFLAGS) $(LDFLAGS) -o echosrv echosrv.o probe.o common.o $(LIBS)
|
||||||
|
|
||||||
|
$(MAN): sslh.pod Makefile
|
||||||
|
pod2man --section=8 --release=$(VERSION) --center=" " sslh.pod | gzip -9 - > $(MAN)
|
||||||
|
|
||||||
|
# Create release: export clean tree and tag current
|
||||||
|
# configuration
|
||||||
|
release:
|
||||||
|
git archive master --prefix="sslh-$(VERSION)/" | gzip > /tmp/sslh-$(VERSION).tar.gz
|
||||||
|
|
||||||
|
# generic install: install binary and man page
|
||||||
|
install: sslh $(MAN)
|
||||||
|
install -pD sslh-fork $(DESTDIR)$(PREFIX)/sbin/sslh
|
||||||
|
install -pD -m 0644 $(MAN) $(DESTDIR)$(PREFIX)/share/man/man8/$(MAN)
|
||||||
|
|
||||||
|
# "extended" install for Debian: install startup script
|
||||||
|
install-debian: install sslh $(MAN)
|
||||||
|
sed -e "s+^PREFIX=+PREFIX=$(PREFIX)+" scripts/etc.init.d.sslh > /etc/init.d/sslh
|
||||||
|
chmod 755 /etc/init.d/sslh
|
||||||
|
cp scripts/etc.default.sslh /etc/default/sslh
|
||||||
|
update-rc.d sslh defaults
|
||||||
|
|
||||||
|
uninstall:
|
||||||
|
rm -f $(DESTDIR)$(PREFIX)/sbin/sslh $(DESTDIR)$(PREFIX)/share/man/man8/$(MAN) $(DESTDIR)/etc/init.d/sslh $(DESTDIR)/etc/default/sslh
|
||||||
|
update-rc.d sslh remove
|
||||||
|
|
||||||
|
distclean: clean
|
||||||
|
rm -f tags cscope.*
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f sslh-fork sslh-select echosrv version.h $(MAN) *.o *.gcov *.gcno *.gcda *.png *.html *.css *.info
|
||||||
|
|
||||||
|
tags:
|
||||||
|
ctags --globals -T *.[ch]
|
||||||
|
|
||||||
|
cscope:
|
||||||
|
-find . -name "*.[chS]" >cscope.files
|
||||||
|
-cscope -b -R
|
||||||
|
|
||||||
|
test:
|
||||||
|
./t
|
||||||
|
|
158
Makefile.in
158
Makefile.in
@ -1,158 +0,0 @@
|
|||||||
|
|
||||||
VERSION=$(shell ./genver.sh -r)
|
|
||||||
|
|
||||||
# Configuration -- you probably need to `make clean` if you
|
|
||||||
# change any of these
|
|
||||||
|
|
||||||
# uncomment the following line to disable landlock
|
|
||||||
# override undefine HAVE_LANDLOCK
|
|
||||||
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?
|
|
||||||
USESYSTEMD= # Make use of systemd socket activation
|
|
||||||
COV_TEST= # Perform test coverage?
|
|
||||||
PREFIX?=/usr
|
|
||||||
BINDIR?=$(PREFIX)/sbin
|
|
||||||
MANDIR?=$(PREFIX)/share/man/man8
|
|
||||||
|
|
||||||
MAN=sslh.8.gz # man page name
|
|
||||||
|
|
||||||
# End of configuration -- the rest should take care of
|
|
||||||
# itself
|
|
||||||
|
|
||||||
ifneq ($(strip $(ENABLE_SANITIZER)),)
|
|
||||||
CFLAGS_SAN=-fsanitize=address -fsanitize=leak -fsanitize=undefined
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifneq ($(strip $(COV_TEST)),)
|
|
||||||
CFLAGS_COV=-fprofile-arcs -ftest-coverage
|
|
||||||
endif
|
|
||||||
|
|
||||||
CC ?= gcc
|
|
||||||
AR ?= ar
|
|
||||||
CFLAGS +=-Wall -O2 -DLIBPCRE -g $(CFLAGS_COV) $(CFLAGS_SAN)
|
|
||||||
|
|
||||||
|
|
||||||
LIBS=-lm -lpcre2-8 @LIBS@
|
|
||||||
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 proxyprotocol.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)
|
|
||||||
EV_OBJS=processes.o udp-listener.o sslh-ev.o hash.o tcp-listener.o $(OBJS_A)
|
|
||||||
|
|
||||||
CONDITIONAL_TARGETS=
|
|
||||||
|
|
||||||
ifneq ($(strip $(ENABLE_REGEX)),)
|
|
||||||
CPPFLAGS+=-DENABLE_REGEX
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifneq ($(strip $(USELIBCONFIG)),)
|
|
||||||
LIBS:=$(LIBS) -lconfig
|
|
||||||
CPPFLAGS+=-DLIBCONFIG
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifneq ($(strip $(USESYSTEMD)),)
|
|
||||||
LIBS:=$(LIBS) -lsystemd
|
|
||||||
CPPFLAGS+=-DSYSTEMD
|
|
||||||
CONDITIONAL_TARGETS+=systemd-sslh-generator
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifneq ($(strip $(USELIBEV)),)
|
|
||||||
CONDITIONAL_TARGETS+=sslh-ev
|
|
||||||
endif
|
|
||||||
|
|
||||||
all: sslh-fork sslh-select $(MAN) echosrv $(CONDITIONAL_TARGETS)
|
|
||||||
|
|
||||||
%.o: %.c %.h version.h
|
|
||||||
$(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@
|
|
||||||
|
|
||||||
$(OBJS_A): $(OBJS)
|
|
||||||
$(AR) rcs $(OBJS_A) $(OBJS)
|
|
||||||
|
|
||||||
version.h: .FORCE
|
|
||||||
./genver.sh >version.h
|
|
||||||
.FORCE:
|
|
||||||
|
|
||||||
$(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
|
|
||||||
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
sslh-select: version.h $(SELECT_OBJS) Makefile
|
|
||||||
$(CC) $(CFLAGS) $(LDFLAGS) -o sslh-select $(SELECT_OBJS) $(LIBS)
|
|
||||||
|
|
||||||
sslh-ev: version.h $(EV_OBJS) Makefile
|
|
||||||
$(CC) $(CFLAGS) $(LDFLAGS) -o sslh-ev $(EV_OBJS) $(LIBS) -lev
|
|
||||||
|
|
||||||
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
|
|
||||||
$(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)
|
|
||||||
|
|
||||||
# Create release: export clean tree and tag current
|
|
||||||
# configuration
|
|
||||||
release:
|
|
||||||
git archive $(VERSION) --prefix="sslh-$(VERSION)/" | gzip > /tmp/sslh-$(VERSION).tar.gz
|
|
||||||
gpg --detach-sign --armor /tmp/sslh-$(VERSION).tar.gz
|
|
||||||
|
|
||||||
# Build docker image
|
|
||||||
docker:
|
|
||||||
docker image build -t "sslh:${VERSION}" .
|
|
||||||
docker image tag "sslh:${VERSION}" sslh:latest
|
|
||||||
|
|
||||||
docker-clean:
|
|
||||||
yes | docker image rm "sslh:${VERSION}" sslh:latest
|
|
||||||
yes | docker image prune
|
|
||||||
|
|
||||||
# generic install: install binary and man page
|
|
||||||
install: sslh-fork $(MAN)
|
|
||||||
mkdir -p $(DESTDIR)/$(BINDIR)
|
|
||||||
mkdir -p $(DESTDIR)/$(MANDIR)
|
|
||||||
install -p sslh-fork $(DESTDIR)/$(BINDIR)/sslh
|
|
||||||
install -p -m 0644 $(MAN) $(DESTDIR)/$(MANDIR)/$(MAN)
|
|
||||||
|
|
||||||
# "extended" install for Debian: install startup script
|
|
||||||
install-debian: install sslh $(MAN)
|
|
||||||
sed -e "s+^PREFIX=+PREFIX=$(PREFIX)+" scripts/etc.init.d.sslh > /etc/init.d/sslh
|
|
||||||
chmod 755 /etc/init.d/sslh
|
|
||||||
update-rc.d sslh defaults
|
|
||||||
|
|
||||||
uninstall:
|
|
||||||
rm -f $(DESTDIR)$(BINDIR)/sslh $(DESTDIR)$(MANDIR)/$(MAN) $(DESTDIR)/etc/init.d/sslh $(DESTDIR)/etc/default/sslh
|
|
||||||
update-rc.d sslh remove
|
|
||||||
|
|
||||||
distclean: clean
|
|
||||||
rm -f tags sslh-conf.[ch] echosrv-conf.[ch] cscope.*
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -f sslh-fork sslh-select $(CONDITIONAL_TARGETS) echosrv version.h $(MAN) systemd-sslh-generator *.o *.gcov *.gcno *.gcda *.png *.html *.css *.info
|
|
||||||
echo "// this is a placeholder for version.h, to make code-checking editors happy" > version.h
|
|
||||||
|
|
||||||
tags:
|
|
||||||
ctags --globals -T *.[ch]
|
|
||||||
|
|
||||||
cscope:
|
|
||||||
-find . -name "*.[chS]" >cscope.files
|
|
||||||
-cscope -b -R
|
|
||||||
|
|
||||||
test:
|
|
||||||
./t
|
|
@ -33,7 +33,7 @@ with launchctl or simply reboot.
|
|||||||
<string>0.0.0.0:443</string>
|
<string>0.0.0.0:443</string>
|
||||||
<string>--ssh</string>
|
<string>--ssh</string>
|
||||||
<string>localhost:22</string>
|
<string>localhost:22</string>
|
||||||
<string>--tls</string>
|
<string>--ssl</string>
|
||||||
<string>localhost:443</string>
|
<string>localhost:443</string>
|
||||||
</array>
|
</array>
|
||||||
<key>QueueDirectories</key>
|
<key>QueueDirectories</key>
|
383
README.md
383
README.md
@ -5,204 +5,257 @@ sslh -- A ssl/ssh multiplexer
|
|||||||
them further based on tests performed on the first data
|
them further based on tests performed on the first data
|
||||||
packet sent by the remote client.
|
packet sent by the remote client.
|
||||||
|
|
||||||
Probes for HTTP, TLS/SSL (including SNI and ALPN), SSH,
|
Probes for HTTP, SSL, SSH, OpenVPN, tinc, XMPP are
|
||||||
OpenVPN, tinc, XMPP, SOCKS5, are implemented, and any other
|
implemented, and any other protocol that can be tested using
|
||||||
protocol that can be tested using a regular expression, can
|
a regular expression, can be recognised. A typical use case
|
||||||
be recognised. A typical use case is to allow serving
|
is to allow serving several services on port 443 (e.g. to
|
||||||
several services on port 443 (e.g. to connect to SSH from
|
connect to SSH from inside a corporate firewall, which
|
||||||
inside a corporate firewall, which almost never block port
|
almost never block port 443) while still serving HTTPS on
|
||||||
443) while still serving HTTPS on that port.
|
that port.
|
||||||
|
|
||||||
Hence `sslh` acts as a protocol demultiplexer, or a
|
Hence `sslh` acts as a protocol demultiplexer, or a
|
||||||
switchboard. With the SNI and ALPN probe, it makes a good
|
switchboard. Its name comes from its original function to
|
||||||
front-end to a virtual host farm hosted behind a single IP
|
serve SSH and HTTPS on the same port.
|
||||||
address.
|
|
||||||
|
|
||||||
`sslh` has the bells and whistles expected from a mature
|
Compile and install
|
||||||
daemon: privilege and capabilities dropping, inetd support,
|
===================
|
||||||
systemd support, transparent proxying, support for HAProxy's
|
|
||||||
proxyprotocol, 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
|
Dependencies
|
||||||
=======
|
------------
|
||||||
|
|
||||||
Please refer to the [install guide](doc/INSTALL.md).
|
`sslh` uses [libconfig](http://www.hyperrealm.com/libconfig/)
|
||||||
|
and [libwrap](http://packages.debian.org/source/unstable/tcp-wrappers).
|
||||||
|
|
||||||
|
For Debian, these are contained in packages `libwrap0-dev` and
|
||||||
|
`libconfig8-dev`.
|
||||||
|
|
||||||
|
For OpenSUSE, these are contained in packages libconfig9 and
|
||||||
|
libconfig-dev in repository
|
||||||
|
<http://download.opensuse.org/repositories/multimedia:/libs/openSUSE_12.1/>
|
||||||
|
|
||||||
|
For Fedora, you'll need packages `libconfig` and
|
||||||
|
`libconfig-devel`:
|
||||||
|
|
||||||
|
yum install libconfig libconfig-devel
|
||||||
|
|
||||||
|
If you can't find `libconfig`, or just don't want a
|
||||||
|
configuration file, set `USELIBCONFIG=` in the Makefile.
|
||||||
|
|
||||||
|
Compilation
|
||||||
|
-----------
|
||||||
|
|
||||||
|
After this, the Makefile should work:
|
||||||
|
|
||||||
|
make install
|
||||||
|
|
||||||
|
There are a couple of configuration options at the beginning
|
||||||
|
of the Makefile:
|
||||||
|
|
||||||
|
* `USELIBWRAP` compiles support for host access control (see
|
||||||
|
`hosts_access(3)`), you will need `libwrap` headers and
|
||||||
|
library to compile (`libwrap0-dev` in Debian).
|
||||||
|
|
||||||
|
* `USELIBCONFIG` compiles support for the configuration
|
||||||
|
file. You will need `libconfig` headers to compile
|
||||||
|
(`libconfig8-dev` in Debian).
|
||||||
|
|
||||||
|
|
||||||
|
Binaries
|
||||||
|
--------
|
||||||
|
|
||||||
|
The Makefile produces two different executables: `sslh-fork`
|
||||||
|
and `sslh-select`:
|
||||||
|
|
||||||
|
* `sslh-fork` forks a new process for each incoming connection.
|
||||||
|
It is well-tested and very reliable, but incurs the overhead
|
||||||
|
of many processes.
|
||||||
|
If you are going to use `sslh` for a "small" setup (less than
|
||||||
|
a dozen ssh connections and a low-traffic https server) then
|
||||||
|
`sslh-fork` is probably more suited for you.
|
||||||
|
|
||||||
|
* `sslh-select` uses only one thread, which monitors all connections
|
||||||
|
at once. It is more recent and less tested, but only incurs a 16
|
||||||
|
byte overhead per connection. Also, if it stops, you'll lose all
|
||||||
|
connections, which means you can't upgrade it remotely.
|
||||||
|
If you are going to use `sslh` on a "medium" setup (a few thousand ssh
|
||||||
|
connections, and another few thousand ssl connections),
|
||||||
|
`sslh-select` will be better.
|
||||||
|
|
||||||
|
If you have a very large site (tens of thousands of connections),
|
||||||
|
you'll need a vapourware version that would use libevent or
|
||||||
|
something like that.
|
||||||
|
|
||||||
|
|
||||||
|
Installation
|
||||||
|
------------
|
||||||
|
|
||||||
|
* In general:
|
||||||
|
|
||||||
|
make
|
||||||
|
cp sslh-fork /usr/local/sbin/sslh
|
||||||
|
cp scripts/etc.default.sslh /etc/default/sslh
|
||||||
|
|
||||||
|
* For Debian:
|
||||||
|
|
||||||
|
cp scripts/etc.init.d.sslh /etc/init.d/sslh
|
||||||
|
|
||||||
|
* For CentOS:
|
||||||
|
|
||||||
|
cp scripts/etc.rc.d.init.d.sslh /etc/rc.d/init.d/sslh
|
||||||
|
|
||||||
|
|
||||||
|
You might need to create links in /etc/rc<x>.d so that the server
|
||||||
|
start automatically at boot-up, e.g. under Debian:
|
||||||
|
|
||||||
|
update-rc.d sslh defaults
|
||||||
|
|
||||||
|
|
||||||
Configuration
|
Configuration
|
||||||
=============
|
=============
|
||||||
|
|
||||||
Please refer to the [configuration guide](doc/config.md).
|
You can edit settings in /etc/default/sslh:
|
||||||
|
|
||||||
Transparent proxying
|
LISTEN=ifname:443
|
||||||
|
SSH=localhost:22
|
||||||
|
SSL=localhost:443
|
||||||
|
|
||||||
|
A good scheme is to use the external name of the machine in
|
||||||
|
`$LISTEN`, and bind `httpd` to `localhost:443` (instead of all
|
||||||
|
binding to all interfaces): that way, HTTPS connections
|
||||||
|
coming from inside your network don't need to go through
|
||||||
|
`sslh`, and `sslh` is only there as a frontal for connections
|
||||||
|
coming from the internet.
|
||||||
|
|
||||||
|
Note that 'external name' in this context refers to the
|
||||||
|
actual IP address of the machine as seen from your network,
|
||||||
|
i.e. that that is not `127.0.0.1` in the output of
|
||||||
|
`ifconfig(8)`.
|
||||||
|
|
||||||
|
Libwrap support
|
||||||
|
---------------
|
||||||
|
|
||||||
|
Sslh can optionnaly perform `libwrap` checks for the sshd
|
||||||
|
service: because the connection to `sshd` will be coming
|
||||||
|
locally from `sslh`, `sshd` cannot determine the IP of the
|
||||||
|
client.
|
||||||
|
|
||||||
|
OpenVPN support
|
||||||
|
---------------
|
||||||
|
|
||||||
|
OpenVPN clients connecting to OpenVPN running with
|
||||||
|
`-port-share` reportedly take more than one second between
|
||||||
|
the time the TCP connexion is established and the time they
|
||||||
|
send the first data packet. This results in `sslh` with
|
||||||
|
default settings timing out and assuming an SSH connexion.
|
||||||
|
To support OpenVPN connexions reliably, it is necessary to
|
||||||
|
increase `sslh`'s timeout to 5 seconds.
|
||||||
|
|
||||||
|
Instead of using OpenVPN's port sharing, it is more reliable
|
||||||
|
to use `sslh`'s `-o` option to get `sslh` to do the port sharing.
|
||||||
|
|
||||||
|
Using proxytunnel with sslh
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
If you are connecting through a proxy that checks that the
|
||||||
|
outgoing connection really is SSL and rejects SSH, you can
|
||||||
|
encapsulate all your traffic in SSL using `proxytunnel` (this
|
||||||
|
should work with `corkscrew` as well). On the server side you
|
||||||
|
receive the traffic with `stunnel` to decapsulate SSL, then
|
||||||
|
pipe through `sslh` to switch HTTP on one side and SSL on the
|
||||||
|
other.
|
||||||
|
|
||||||
|
In that case, you end up with something like this:
|
||||||
|
|
||||||
|
ssh -> proxytunnel -e ----[ssh/ssl]---> stunnel ---[ssh]---> sslh --> sshd
|
||||||
|
Web browser -------------[http/ssl]---> stunnel ---[http]--> sslh --> httpd
|
||||||
|
|
||||||
|
Configuration goes like this on the server side, using `stunnel3`:
|
||||||
|
|
||||||
|
stunnel -f -p mycert.pem -d thelonious:443 -l /usr/local/sbin/sslh -- \
|
||||||
|
sslh -i --http localhost:80 --ssh localhost:22
|
||||||
|
|
||||||
|
* stunnel options:
|
||||||
|
* `-f` for foreground/debugging
|
||||||
|
* `-p` for specifying the key and certificate
|
||||||
|
* `-d` for specifying which interface and port
|
||||||
|
we're listening to for incoming connexions
|
||||||
|
* `-l` summons `sslh` in inetd mode.
|
||||||
|
|
||||||
|
* sslh options:
|
||||||
|
* `-i` for inetd mode
|
||||||
|
* `--http` to forward HTTP connexions to port 80,
|
||||||
|
and SSH connexions to port 22.
|
||||||
|
|
||||||
|
Capabilities support
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
Transparent proxying allows the target server to see the
|
On Linux (only?), you can compile sslh with `USELIBCAP=1` to
|
||||||
original client IP address, i.e. `sslh` becomes invisible.
|
make use of POSIX capabilities; this will save the required
|
||||||
|
capabilities needed for transparent proxying for unprivileged
|
||||||
|
processes.
|
||||||
|
|
||||||
The same result can be achieved more easily by using
|
Alternatively, you may use filesystem capabilities instead
|
||||||
`proxyprotocol` if the backend server supports it. This is a
|
of starting sslh as root and asking it to drop privileges.
|
||||||
simple setting to add to the `sslh` protocol configuration,
|
You will need `CAP_NET_BIND_SERVICE` for listening on port 443
|
||||||
usually with an equivalently simple setting to add in
|
and `CAP_NET_ADMIN` for transparent proxying (see
|
||||||
the backend server configuration, so try that first.
|
`capabilities(7)`).
|
||||||
|
|
||||||
This means services behind `sslh` (Apache, `sshd` and so on)
|
You can use the `setcap(8)` utility to give these capabilities
|
||||||
will see the external IP and ports as if the external world
|
to the executable:
|
||||||
connected directly to them. This simplifies IP-based access
|
|
||||||
control (or makes it possible at all), and makes it possible
|
|
||||||
to use IP-based banning tools such as `fail2ban`.
|
|
||||||
|
|
||||||
There are two methods. One uses additional virtual network
|
# setcap cap_net_bind_service,cap_net_admin+pe sslh-select
|
||||||
interfaces. The principle and basic setup is described
|
|
||||||
[here](doc/simple_transparent_proxy.md), with further
|
|
||||||
scenarios described [there](doc/scenarios-for-simple-transparent-proxy.md).
|
|
||||||
|
|
||||||
|
Then you can run sslh-select as an unpriviledged user, e.g.:
|
||||||
|
|
||||||
Another method uses iptable packet marking features, and is
|
$ sslh-select -p myname:443 --ssh localhost:22 --ssl localhost:443
|
||||||
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).
|
Caveat: `CAP_NET_ADMIN` does give sslh too many rights, e.g.
|
||||||
In most cases, you will be better off following the first
|
configuring the interface. If you're not going to use
|
||||||
method.
|
transparent proxying, just don't use it (or use the libcap method).
|
||||||
|
|
||||||
|
Transparent proxy support
|
||||||
|
-------------------------
|
||||||
|
|
||||||
Docker image
|
On Linux (only?) you can use the `--transparent` option to
|
||||||
------------
|
request transparent proying. This means services behind `sslh`
|
||||||
|
(Apache, `sshd` and so on) will see the external IP and ports
|
||||||
|
as if the external world connected directly to them. This
|
||||||
|
simplifies IP-based access control (or makes it possible at
|
||||||
|
all).
|
||||||
|
|
||||||
How to use
|
`sslh` needs extended rights to perform this: you'll need to
|
||||||
|
give it `CAP_NET_ADMIN` capabilities (see appropriate chapter)
|
||||||
|
or run it as root (but don't do that).
|
||||||
|
|
||||||
---
|
The firewalling tables also need to be adjusted as follow.
|
||||||
|
The example connects to HTTPS on 4443 -- adapt to your needs ;
|
||||||
|
I don't think it is possible to have `httpd` listen to 443 in
|
||||||
|
this scheme -- let me know if you manage that:
|
||||||
|
|
||||||
|
# iptables -t mangle -N SSLH
|
||||||
|
# iptables -t mangle -A OUTPUT --protocol tcp --out-interface eth0 --sport 22 --jump SSLH
|
||||||
|
# iptables -t mangle -A OUTPUT --protocol tcp --out-interface eth0 --sport 4443 --jump SSLH
|
||||||
|
# iptables -t mangle -A SSLH --jump MARK --set-mark 0x1
|
||||||
|
# iptables -t mangle -A SSLH --jump ACCEPT
|
||||||
|
# ip rule add fwmark 0x1 lookup 100
|
||||||
|
# ip route add local 0.0.0.0/0 dev lo table 100
|
||||||
|
|
||||||
```bash
|
This will only work if `sslh` does not use any loopback
|
||||||
docker run \
|
addresses (no `127.0.0.1` or `localhost`), you'll need to use
|
||||||
--cap-add CAP_NET_RAW \
|
explicit IP addresses (or names):
|
||||||
--cap-add CAP_NET_BIND_SERVICE \
|
|
||||||
--rm \
|
|
||||||
-it \
|
|
||||||
ghcr.io/yrutschle/sslh:latest \
|
|
||||||
--foreground \
|
|
||||||
--listen=0.0.0.0:443 \
|
|
||||||
--ssh=hostname:22 \
|
|
||||||
--tls=hostname:443
|
|
||||||
```
|
|
||||||
|
|
||||||
docker-compose example
|
sslh --listen 192.168.0.1:443 --ssh 192.168.0.1:22 --ssl 192.168.0.1:4443
|
||||||
|
|
||||||
```yaml
|
This will not work:
|
||||||
version: "3"
|
|
||||||
|
|
||||||
services:
|
sslh --listen 192.168.0.1:443 --ssh 127.0.0.1:22 --ssl 127.0.0.1:4443
|
||||||
sslh:
|
|
||||||
image: ghcr.io/yrutschle/sslh:latest
|
|
||||||
hostname: sslh
|
|
||||||
ports:
|
|
||||||
- 443:443
|
|
||||||
command: --foreground --listen=0.0.0.0:443 --tls=nginx:443 --openvpn=openvpn:1194
|
|
||||||
depends_on:
|
|
||||||
- nginx
|
|
||||||
- openvpn
|
|
||||||
|
|
||||||
nginx:
|
|
||||||
image: nginx
|
|
||||||
|
|
||||||
openvpn:
|
|
||||||
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?
|
Comments? Questions?
|
||||||
====================
|
====================
|
||||||
|
|
||||||
You can subscribe to the `sslh` mailing list here:
|
You can subscribe to the `sslh` mailing list here:
|
||||||
<https://lists.rutschle.net/mailman/listinfo/sslh>
|
<http://rutschle.net/cgi-bin/mailman/listinfo/sslh>
|
||||||
|
|
||||||
This mailing list should be used for discussion, feature
|
This mailing list should be used for discussion, feature
|
||||||
requests, and will be the preferred channel for announcements.
|
requests, and will be the prefered channel for announcements.
|
||||||
|
|
||||||
Of course, check the [FAQ](doc/FAQ.md) first!
|
|
||||||
|
|
||||||
|
6006
argtable3.c
6006
argtable3.c
File diff suppressed because it is too large
Load Diff
273
argtable3.h
273
argtable3.h
@ -1,273 +0,0 @@
|
|||||||
/*******************************************************************************
|
|
||||||
* argtable3: Declares the main interfaces of the library
|
|
||||||
*
|
|
||||||
* This file is part of the argtable3 library.
|
|
||||||
*
|
|
||||||
* Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann
|
|
||||||
* <sheitmann@users.sourceforge.net>
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* * Neither the name of STEWART HEITMANN nor the names of its contributors
|
|
||||||
* may be used to endorse or promote products derived from this software
|
|
||||||
* without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT,
|
|
||||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
******************************************************************************/
|
|
||||||
|
|
||||||
#ifndef ARGTABLE3
|
|
||||||
#define ARGTABLE3
|
|
||||||
|
|
||||||
#include <stdio.h> /* FILE */
|
|
||||||
#include <time.h> /* struct tm */
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define ARG_REX_ICASE 1
|
|
||||||
#define ARG_DSTR_SIZE 200
|
|
||||||
#define ARG_CMD_NAME_LEN 100
|
|
||||||
#define ARG_CMD_DESCRIPTION_LEN 256
|
|
||||||
|
|
||||||
#ifndef ARG_REPLACE_GETOPT
|
|
||||||
#define ARG_REPLACE_GETOPT 1 /* use the embedded getopt as the system getopt(3) */
|
|
||||||
#endif /* ARG_REPLACE_GETOPT */
|
|
||||||
|
|
||||||
/* bit masks for arg_hdr.flag */
|
|
||||||
enum { ARG_TERMINATOR = 0x1, ARG_HASVALUE = 0x2, ARG_HASOPTVALUE = 0x4 };
|
|
||||||
|
|
||||||
#if defined(_WIN32)
|
|
||||||
#if defined(argtable3_EXPORTS)
|
|
||||||
#define ARG_EXTERN __declspec(dllexport)
|
|
||||||
#elif defined(argtable3_IMPORTS)
|
|
||||||
#define ARG_EXTERN __declspec(dllimport)
|
|
||||||
#else
|
|
||||||
#define ARG_EXTERN
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
#define ARG_EXTERN
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef struct _internal_arg_dstr* arg_dstr_t;
|
|
||||||
typedef void* arg_cmd_itr_t;
|
|
||||||
|
|
||||||
typedef void(arg_resetfn)(void* parent);
|
|
||||||
typedef int(arg_scanfn)(void* parent, const char* argval);
|
|
||||||
typedef int(arg_checkfn)(void* parent);
|
|
||||||
typedef void(arg_errorfn)(void* parent, arg_dstr_t ds, int error, const char* argval, const char* progname);
|
|
||||||
typedef void(arg_dstr_freefn)(char* buf);
|
|
||||||
typedef int(arg_cmdfn)(int argc, char* argv[], arg_dstr_t res);
|
|
||||||
typedef int(arg_comparefn)(const void* k1, const void* k2);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The arg_hdr struct defines properties that are common to all arg_xxx structs.
|
|
||||||
* The argtable library requires each arg_xxx struct to have an arg_hdr
|
|
||||||
* struct as its first data member.
|
|
||||||
* The argtable library functions then use this data to identify the
|
|
||||||
* properties of the command line option, such as its option tags,
|
|
||||||
* datatype string, and glossary strings, and so on.
|
|
||||||
* Moreover, the arg_hdr struct contains pointers to custom functions that
|
|
||||||
* are provided by each arg_xxx struct which perform the tasks of parsing
|
|
||||||
* 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 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.
|
|
||||||
*/
|
|
||||||
typedef struct arg_hdr {
|
|
||||||
char flag; /* Modifier flags: ARG_TERMINATOR, ARG_HASVALUE. */
|
|
||||||
const char* shortopts; /* String defining the short 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 */
|
|
||||||
int maxcount; /* Maximum number of occurences if this option accepted */
|
|
||||||
void* parent; /* Pointer to parent arg_xxx struct */
|
|
||||||
arg_resetfn* resetfn; /* Pointer to parent arg_xxx reset function */
|
|
||||||
arg_scanfn* scanfn; /* Pointer to parent arg_xxx scan function */
|
|
||||||
arg_checkfn* checkfn; /* Pointer to parent arg_xxx check function */
|
|
||||||
arg_errorfn* errorfn; /* Pointer to parent arg_xxx error function */
|
|
||||||
void* priv; /* Pointer to private header data for use by arg_xxx functions */
|
|
||||||
} arg_hdr_t;
|
|
||||||
|
|
||||||
typedef struct arg_rem {
|
|
||||||
struct arg_hdr hdr; /* The mandatory argtable header struct */
|
|
||||||
} arg_rem_t;
|
|
||||||
|
|
||||||
typedef struct arg_lit {
|
|
||||||
struct arg_hdr hdr; /* The mandatory argtable header struct */
|
|
||||||
int count; /* Number of matching command line args */
|
|
||||||
} arg_lit_t;
|
|
||||||
|
|
||||||
typedef struct arg_int {
|
|
||||||
struct arg_hdr hdr; /* The mandatory argtable header struct */
|
|
||||||
int count; /* Number of matching command line args */
|
|
||||||
int* ival; /* Array of parsed argument values */
|
|
||||||
} arg_int_t;
|
|
||||||
|
|
||||||
typedef struct arg_dbl {
|
|
||||||
struct arg_hdr hdr; /* The mandatory argtable header struct */
|
|
||||||
int count; /* Number of matching command line args */
|
|
||||||
double* dval; /* Array of parsed argument values */
|
|
||||||
} arg_dbl_t;
|
|
||||||
|
|
||||||
typedef struct arg_str {
|
|
||||||
struct arg_hdr hdr; /* The mandatory argtable header struct */
|
|
||||||
int count; /* Number of matching command line args */
|
|
||||||
const char** sval; /* Array of parsed argument values */
|
|
||||||
} arg_str_t;
|
|
||||||
|
|
||||||
typedef struct arg_rex {
|
|
||||||
struct arg_hdr hdr; /* The mandatory argtable header struct */
|
|
||||||
int count; /* Number of matching command line args */
|
|
||||||
const char** sval; /* Array of parsed argument values */
|
|
||||||
} arg_rex_t;
|
|
||||||
|
|
||||||
typedef struct arg_file {
|
|
||||||
struct arg_hdr hdr; /* The mandatory argtable header struct */
|
|
||||||
int count; /* Number of matching command line args*/
|
|
||||||
const char** filename; /* Array of parsed filenames (eg: /home/foo.bar) */
|
|
||||||
const char** basename; /* Array of parsed basenames (eg: foo.bar) */
|
|
||||||
const char** extension; /* Array of parsed extensions (eg: .bar) */
|
|
||||||
} arg_file_t;
|
|
||||||
|
|
||||||
typedef struct arg_date {
|
|
||||||
struct arg_hdr hdr; /* The mandatory argtable header struct */
|
|
||||||
const char* format; /* strptime format string used to parse the date */
|
|
||||||
int count; /* Number of matching command line args */
|
|
||||||
struct tm* tmval; /* Array of parsed time values */
|
|
||||||
} arg_date_t;
|
|
||||||
|
|
||||||
enum { ARG_ELIMIT = 1, ARG_EMALLOC, ARG_ENOMATCH, ARG_ELONGOPT, ARG_EMISSARG };
|
|
||||||
typedef struct arg_end {
|
|
||||||
struct arg_hdr hdr; /* The mandatory argtable header struct */
|
|
||||||
int count; /* Number of errors encountered */
|
|
||||||
int* error; /* Array of error codes */
|
|
||||||
void** parent; /* Array of pointers to offending arg_xxx struct */
|
|
||||||
const char** argval; /* Array of pointers to offending argv[] string */
|
|
||||||
} arg_end_t;
|
|
||||||
|
|
||||||
typedef struct arg_cmd_info {
|
|
||||||
char name[ARG_CMD_NAME_LEN];
|
|
||||||
char description[ARG_CMD_DESCRIPTION_LEN];
|
|
||||||
arg_cmdfn* proc;
|
|
||||||
} arg_cmd_info_t;
|
|
||||||
|
|
||||||
/**** arg_xxx constructor functions *********************************/
|
|
||||||
|
|
||||||
ARG_EXTERN struct arg_rem* arg_rem(const char* datatype, const char* glossary);
|
|
||||||
|
|
||||||
ARG_EXTERN struct arg_lit* arg_lit0(const char* shortopts, const char* longopts, const char* glossary);
|
|
||||||
ARG_EXTERN struct arg_lit* arg_lit1(const char* shortopts, const char* longopts, const char* glossary);
|
|
||||||
ARG_EXTERN struct arg_lit* arg_litn(const char* shortopts, const char* longopts, int mincount, int maxcount, const char* glossary);
|
|
||||||
|
|
||||||
ARG_EXTERN struct arg_int* arg_int0(const char* shortopts, const char* longopts, const char* datatype, const char* glossary);
|
|
||||||
ARG_EXTERN struct arg_int* arg_int1(const char* shortopts, const char* longopts, const char* datatype, const char* glossary);
|
|
||||||
ARG_EXTERN struct arg_int* arg_intn(const char* shortopts, const char* longopts, const char* datatype, int mincount, int maxcount, const char* glossary);
|
|
||||||
|
|
||||||
ARG_EXTERN struct arg_dbl* arg_dbl0(const char* shortopts, const char* longopts, const char* datatype, const char* glossary);
|
|
||||||
ARG_EXTERN struct arg_dbl* arg_dbl1(const char* shortopts, const char* longopts, const char* datatype, const char* glossary);
|
|
||||||
ARG_EXTERN struct arg_dbl* arg_dbln(const char* shortopts, const char* longopts, const char* datatype, int mincount, int maxcount, const char* glossary);
|
|
||||||
|
|
||||||
ARG_EXTERN struct arg_str* arg_str0(const char* shortopts, const char* longopts, const char* datatype, const char* glossary);
|
|
||||||
ARG_EXTERN struct arg_str* arg_str1(const char* shortopts, const char* longopts, const char* datatype, const char* glossary);
|
|
||||||
ARG_EXTERN struct arg_str* arg_strn(const char* shortopts, const char* longopts, const char* datatype, int mincount, int maxcount, const char* glossary);
|
|
||||||
|
|
||||||
ARG_EXTERN struct arg_rex* arg_rex0(const char* shortopts, const char* longopts, const char* pattern, const char* datatype, int flags, const char* glossary);
|
|
||||||
ARG_EXTERN struct arg_rex* arg_rex1(const char* shortopts, const char* longopts, const char* pattern, const char* datatype, int flags, const char* glossary);
|
|
||||||
ARG_EXTERN struct arg_rex* arg_rexn(const char* shortopts,
|
|
||||||
const char* longopts,
|
|
||||||
const char* pattern,
|
|
||||||
const char* datatype,
|
|
||||||
int mincount,
|
|
||||||
int maxcount,
|
|
||||||
int flags,
|
|
||||||
const char* glossary);
|
|
||||||
|
|
||||||
ARG_EXTERN struct arg_file* arg_file0(const char* shortopts, const char* longopts, const char* datatype, const char* glossary);
|
|
||||||
ARG_EXTERN struct arg_file* arg_file1(const char* shortopts, const char* longopts, const char* datatype, const char* glossary);
|
|
||||||
ARG_EXTERN struct arg_file* arg_filen(const char* shortopts, const char* longopts, const char* datatype, int mincount, int maxcount, const char* glossary);
|
|
||||||
|
|
||||||
ARG_EXTERN struct arg_date* arg_date0(const char* shortopts, const char* longopts, const char* format, const char* datatype, const char* glossary);
|
|
||||||
ARG_EXTERN struct arg_date* arg_date1(const char* shortopts, const char* longopts, const char* format, const char* datatype, const char* glossary);
|
|
||||||
ARG_EXTERN struct arg_date* arg_daten(const char* shortopts, const char* longopts, const char* format, const char* datatype, int mincount, int maxcount, const char* glossary);
|
|
||||||
|
|
||||||
ARG_EXTERN struct arg_end* arg_end(int maxerrors);
|
|
||||||
|
|
||||||
#define ARG_DSTR_STATIC ((arg_dstr_freefn*)0)
|
|
||||||
#define ARG_DSTR_VOLATILE ((arg_dstr_freefn*)1)
|
|
||||||
#define ARG_DSTR_DYNAMIC ((arg_dstr_freefn*)3)
|
|
||||||
|
|
||||||
/**** other functions *******************************************/
|
|
||||||
ARG_EXTERN int arg_nullcheck(void** argtable);
|
|
||||||
ARG_EXTERN int arg_parse(int argc, char** argv, void** argtable);
|
|
||||||
ARG_EXTERN void arg_print_option(FILE* fp, const char* shortopts, const char* longopts, const char* datatype, const char* suffix);
|
|
||||||
ARG_EXTERN void arg_print_syntax(FILE* fp, void** argtable, const char* suffix);
|
|
||||||
ARG_EXTERN void arg_print_syntaxv(FILE* fp, void** argtable, const char* suffix);
|
|
||||||
ARG_EXTERN void arg_print_glossary(FILE* fp, void** argtable, const char* format);
|
|
||||||
ARG_EXTERN void arg_print_glossary_gnu(FILE* fp, void** argtable);
|
|
||||||
ARG_EXTERN void arg_print_errors(FILE* fp, struct arg_end* end, const char* progname);
|
|
||||||
ARG_EXTERN void arg_print_option_ds(arg_dstr_t ds, const char* shortopts, const char* longopts, const char* datatype, const char* suffix);
|
|
||||||
ARG_EXTERN void arg_print_syntax_ds(arg_dstr_t ds, void** argtable, const char* suffix);
|
|
||||||
ARG_EXTERN void arg_print_syntaxv_ds(arg_dstr_t ds, void** argtable, const char* suffix);
|
|
||||||
ARG_EXTERN void arg_print_glossary_ds(arg_dstr_t ds, void** argtable, const char* format);
|
|
||||||
ARG_EXTERN void arg_print_glossary_gnu_ds(arg_dstr_t ds, void** argtable);
|
|
||||||
ARG_EXTERN void arg_print_errors_ds(arg_dstr_t ds, struct arg_end* end, const char* progname);
|
|
||||||
ARG_EXTERN void arg_freetable(void** argtable, size_t n);
|
|
||||||
|
|
||||||
ARG_EXTERN arg_dstr_t arg_dstr_create(void);
|
|
||||||
ARG_EXTERN void arg_dstr_destroy(arg_dstr_t ds);
|
|
||||||
ARG_EXTERN void arg_dstr_reset(arg_dstr_t ds);
|
|
||||||
ARG_EXTERN void arg_dstr_free(arg_dstr_t ds);
|
|
||||||
ARG_EXTERN void arg_dstr_set(arg_dstr_t ds, char* str, arg_dstr_freefn* free_proc);
|
|
||||||
ARG_EXTERN void arg_dstr_cat(arg_dstr_t ds, const char* str);
|
|
||||||
ARG_EXTERN void arg_dstr_catc(arg_dstr_t ds, char c);
|
|
||||||
ARG_EXTERN void arg_dstr_catf(arg_dstr_t ds, const char* fmt, ...);
|
|
||||||
ARG_EXTERN char* arg_dstr_cstr(arg_dstr_t ds);
|
|
||||||
|
|
||||||
ARG_EXTERN void arg_cmd_init(void);
|
|
||||||
ARG_EXTERN void arg_cmd_uninit(void);
|
|
||||||
ARG_EXTERN void arg_cmd_register(const char* name, arg_cmdfn* proc, const char* description);
|
|
||||||
ARG_EXTERN void arg_cmd_unregister(const char* name);
|
|
||||||
ARG_EXTERN int arg_cmd_dispatch(const char* name, int argc, char* argv[], arg_dstr_t res);
|
|
||||||
ARG_EXTERN unsigned int arg_cmd_count(void);
|
|
||||||
ARG_EXTERN arg_cmd_info_t* arg_cmd_info(const char* name);
|
|
||||||
ARG_EXTERN arg_cmd_itr_t arg_cmd_itr_create(void);
|
|
||||||
ARG_EXTERN void arg_cmd_itr_destroy(arg_cmd_itr_t itr);
|
|
||||||
ARG_EXTERN int arg_cmd_itr_advance(arg_cmd_itr_t itr);
|
|
||||||
ARG_EXTERN char* arg_cmd_itr_key(arg_cmd_itr_t itr);
|
|
||||||
ARG_EXTERN arg_cmd_info_t* arg_cmd_itr_value(arg_cmd_itr_t itr);
|
|
||||||
ARG_EXTERN int arg_cmd_itr_search(arg_cmd_itr_t itr, void* k);
|
|
||||||
ARG_EXTERN void arg_mgsort(void* data, int size, int esize, int i, int k, arg_comparefn* comparefn);
|
|
||||||
ARG_EXTERN void arg_make_get_help_msg(arg_dstr_t res);
|
|
||||||
ARG_EXTERN void arg_make_help_msg(arg_dstr_t ds, char* cmd_name, void** argtable);
|
|
||||||
ARG_EXTERN void arg_make_syntax_err_msg(arg_dstr_t ds, void** argtable, struct arg_end* end);
|
|
||||||
ARG_EXTERN int arg_make_syntax_err_help_msg(arg_dstr_t ds, char* name, int help, int nerrors, void** argtable, struct arg_end* end, int* exitcode);
|
|
||||||
ARG_EXTERN void arg_set_module_name(const char* name);
|
|
||||||
ARG_EXTERN void arg_set_module_version(int major, int minor, int patch, const char* tag);
|
|
||||||
|
|
||||||
/**** deprecated functions, for back-compatibility only ********/
|
|
||||||
ARG_EXTERN void arg_free(void** argtable);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#endif
|
|
31
basic.cfg
31
basic.cfg
@ -1,34 +1,29 @@
|
|||||||
# This is a basic configuration file that should provide
|
# This is a basic configuration file that should provide
|
||||||
# sensible values for "standard" setup.
|
# sensible values for "standard" setup.
|
||||||
|
|
||||||
# You will find extensive examples with explanations in
|
verbose: false;
|
||||||
# example.cfg
|
foreground: false;
|
||||||
|
inetd: false;
|
||||||
|
numeric: false;
|
||||||
|
transparent: false;
|
||||||
timeout: 2;
|
timeout: 2;
|
||||||
user: "nobody";
|
user: "nobody";
|
||||||
pidfile: "/var/run/sslh.pid";
|
pidfile: "/var/run/sslh.pid";
|
||||||
|
|
||||||
|
|
||||||
# Change hostname with your external address name, or the IP
|
# Change hostname with your external address name.
|
||||||
# of the interface that receives connections
|
|
||||||
# Default is to bind all interfaces. httpd can be started
|
|
||||||
# first to bind on localhost, in which case sslh will bind
|
|
||||||
# only other interfaces.
|
|
||||||
listen:
|
listen:
|
||||||
(
|
(
|
||||||
{ host: "0.0.0.0"; port: "443"; },
|
{ host: "thelonious"; port: "443"; }
|
||||||
{ host: "[::]"; port: "443"; }
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
# Change to the protocols you want to forward to. The
|
|
||||||
# defaults here are sensible for services running on
|
|
||||||
# localhost
|
|
||||||
protocols:
|
protocols:
|
||||||
(
|
(
|
||||||
{ name: "ssh"; service: "ssh"; host: "localhost"; port: "22"; fork: true; },
|
{ name: "ssh"; service: "ssh"; host: "localhost"; port: "22"; probe: "builtin"; },
|
||||||
{ name: "openvpn"; host: "localhost"; port: "1194"; },
|
{ name: "openvpn"; host: "localhost"; port: "1194"; probe: "builtin"; },
|
||||||
{ name: "tls"; host: "localhost"; port: "443"; log_level: 0; },
|
{ name: "xmpp"; host: "localhost"; port: "5222"; probe: "builtin"; },
|
||||||
{ name: "anyprot"; host: "localhost"; port: "443"; }
|
{ name: "http"; host: "localhost"; port: "80"; probe: "builtin"; },
|
||||||
|
{ name: "ssl"; host: "localhost"; port: "443"; probe: "builtin"; },
|
||||||
|
{ name: "anyprot"; host: "localhost"; port: "443"; probe: "builtin"; }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
98
collection.c
98
collection.c
@ -1,98 +0,0 @@
|
|||||||
/*
|
|
||||||
collection.c: management of a collection of connections, for sslh-select
|
|
||||||
|
|
||||||
# Copyright (C) 2021 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 "common.h"
|
|
||||||
#include "collection.h"
|
|
||||||
#include "sslh-conf.h"
|
|
||||||
#include "gap.h"
|
|
||||||
|
|
||||||
/* Info to keep track of all connections */
|
|
||||||
struct cnx_collection {
|
|
||||||
gap_array* fd2cnx; /* Array indexed by file descriptor to things in cnx[] */
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Allocates and initialises a new collection of connections with at least
|
|
||||||
* `len` elements. */
|
|
||||||
cnx_collection* collection_init(int len)
|
|
||||||
{
|
|
||||||
cnx_collection* collection;
|
|
||||||
|
|
||||||
collection = malloc(sizeof(*collection));
|
|
||||||
CHECK_ALLOC(collection, "collection_init(collection)");
|
|
||||||
|
|
||||||
memset(collection, 0, sizeof(*collection));
|
|
||||||
|
|
||||||
collection->fd2cnx = gap_init(len);
|
|
||||||
|
|
||||||
return collection;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Caveat: might not work, as has never been used */
|
|
||||||
void collection_destroy(cnx_collection* collection)
|
|
||||||
{
|
|
||||||
/* Caveat 2: no code to free connections yet */
|
|
||||||
gap_destroy(collection->fd2cnx);
|
|
||||||
free(collection);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Points the file descriptor to the specified connection index */
|
|
||||||
int collection_add_fd(cnx_collection* collection, struct connection* cnx, int fd)
|
|
||||||
{
|
|
||||||
gap_set(collection->fd2cnx, fd, cnx);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Allocates a connection and inits it with specified file descriptor */
|
|
||||||
struct connection* collection_alloc_cnx_from_fd(struct cnx_collection* collection, int fd)
|
|
||||||
{
|
|
||||||
struct connection* cnx = malloc(sizeof(*cnx));
|
|
||||||
|
|
||||||
if (!cnx) return NULL;
|
|
||||||
|
|
||||||
init_cnx(cnx);
|
|
||||||
cnx->type = SOCK_STREAM;
|
|
||||||
cnx->q[0].fd = fd;
|
|
||||||
cnx->state = ST_PROBING;
|
|
||||||
cnx->probe_timeout = time(NULL) + cfg.timeout;
|
|
||||||
|
|
||||||
gap_set(collection->fd2cnx, fd, cnx);
|
|
||||||
|
|
||||||
return cnx;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Remove a connection from the collection */
|
|
||||||
int collection_remove_cnx(cnx_collection* collection, struct connection *cnx)
|
|
||||||
{
|
|
||||||
if (cnx->q[0].fd != -1)
|
|
||||||
gap_set(collection->fd2cnx, cnx->q[0].fd, NULL);
|
|
||||||
if (cnx->q[1].fd != -1)
|
|
||||||
gap_set(collection->fd2cnx, cnx->q[1].fd, NULL);
|
|
||||||
free(cnx);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns the connection that contains the file descriptor */
|
|
||||||
struct connection* collection_get_cnx_from_fd(struct cnx_collection* collection, int fd)
|
|
||||||
{
|
|
||||||
return gap_get(collection->fd2cnx, fd);
|
|
||||||
}
|
|
||||||
|
|
18
collection.h
18
collection.h
@ -1,18 +0,0 @@
|
|||||||
#ifndef COLLECTION_H
|
|
||||||
#define COLLECTION_H
|
|
||||||
|
|
||||||
typedef struct cnx_collection cnx_collection;
|
|
||||||
|
|
||||||
|
|
||||||
cnx_collection* collection_init(int len);
|
|
||||||
void collection_destroy(cnx_collection* collection);
|
|
||||||
|
|
||||||
struct connection* collection_alloc_cnx_from_fd(cnx_collection* collection, int fd);
|
|
||||||
int collection_add_fd(cnx_collection* collection, struct connection* cnx, int fd);
|
|
||||||
|
|
||||||
/* Remove a connection from the collection */
|
|
||||||
int collection_remove_cnx(cnx_collection* collection, struct connection *cnx);
|
|
||||||
|
|
||||||
struct connection* collection_get_cnx_from_fd(struct cnx_collection* collection, int fd);
|
|
||||||
|
|
||||||
#endif
|
|
117
common.h
117
common.h
@ -1,11 +1,10 @@
|
|||||||
#ifndef COMMON_H
|
#ifndef __COMMON_H_
|
||||||
#define COMMON_H
|
#define __COMMON_H_
|
||||||
|
|
||||||
/* FD_SETSIZE is 64 on Cygwin, which is really low. Just redefining it is
|
/* FD_SETSIZE is 64 on Cygwin, which is really low. Just redefining it is
|
||||||
* enough for the macros to adapt (http://support.microsoft.com/kb/111855)
|
* enough for the macros to adapt (http://support.microsoft.com/kb/111855)
|
||||||
*/
|
*/
|
||||||
#ifdef __CYGWIN__
|
#ifdef __CYGWIN__
|
||||||
#undef FD_SETSIZE
|
|
||||||
#define FD_SETSIZE 4096
|
#define FD_SETSIZE 4096
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -34,36 +33,20 @@
|
|||||||
#include <sys/capability.h>
|
#include <sys/capability.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __APPLE__
|
|
||||||
#include <AvailabilityMacros.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
|
||||||
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
|
|
||||||
|
|
||||||
|
|
||||||
#define CHECK_RES_DIE(res, str) \
|
#define CHECK_RES_DIE(res, str) \
|
||||||
if (res == -1) { \
|
if (res == -1) { \
|
||||||
print_message(msg_system_error, "%s:%d:", __FILE__, __LINE__); \
|
|
||||||
perror(str); \
|
perror(str); \
|
||||||
exit(1); \
|
exit(1); \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define CHECK_RES_RETURN(res, str, ret) \
|
#define CHECK_RES_RETURN(res, str) \
|
||||||
if (res == -1) { \
|
if (res == -1) { \
|
||||||
print_message(msg_system_error, "%s:%d:%s:%d:%s\n", __FILE__, __LINE__, str, errno, strerror(errno)); \
|
log_message(LOG_CRIT, "%s:%d:%s\n", str, errno, strerror(errno)); \
|
||||||
return ret; \
|
return res; \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define CHECK_ALLOC(a, str) \
|
|
||||||
if (!a) { \
|
|
||||||
print_message(msg_system_error, "%s:%d:", __FILE__, __LINE__); \
|
|
||||||
perror(str); \
|
|
||||||
exit(1); \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
|
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
|
||||||
|
|
||||||
#if 1
|
#if 1
|
||||||
@ -76,19 +59,14 @@
|
|||||||
#define IP_FREEBIND 0
|
#define IP_FREEBIND 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef TCP_FASTOPEN
|
|
||||||
#define TCP_FASTOPEN 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef TCP_FASTOPEN_CONNECT
|
|
||||||
#define TCP_FASTOPEN_CONNECT 30 /* Attempt FastOpen with connect. */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
enum connection_state {
|
enum connection_state {
|
||||||
ST_PROBING=1, /* Waiting for timeout to find where to forward */
|
ST_PROBING=1, /* Waiting for timeout to find where to forward */
|
||||||
ST_SHOVELING /* Connexion is established */
|
ST_SHOVELING /* Connexion is established */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* this is used to pass protocols through the command-line parameter parsing */
|
||||||
|
#define PROT_SHIFT 1000 /* protocol options will be 1000, 1001, etc */
|
||||||
|
|
||||||
/* A 'queue' is composed of a file descriptor (which can be read from or
|
/* A 'queue' is composed of a file descriptor (which can be read from or
|
||||||
* written to), and a queue for deferred write data */
|
* written to), and a queue for deferred write data */
|
||||||
struct queue {
|
struct queue {
|
||||||
@ -98,105 +76,54 @@ struct queue {
|
|||||||
int deferred_data_size;
|
int deferred_data_size;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Double linked list for timeout management */
|
|
||||||
typedef struct {
|
|
||||||
struct connection* head;
|
|
||||||
struct connection* tail;
|
|
||||||
} dl_list;
|
|
||||||
|
|
||||||
struct connection {
|
struct connection {
|
||||||
int type; /* SOCK_DGRAM | SOCK_STREAM */
|
|
||||||
struct sslhcfg_protocols_item* proto; /* Where to connect to */
|
|
||||||
|
|
||||||
/* SOCK_STREAM */
|
|
||||||
enum connection_state state;
|
enum connection_state state;
|
||||||
time_t probe_timeout;
|
time_t probe_timeout;
|
||||||
|
struct proto *proto;
|
||||||
|
|
||||||
/* q[0]: queue for external connection (client);
|
/* q[0]: queue for external connection (client);
|
||||||
* q[1]: queue for internal connection (httpd or sshd);
|
* q[1]: queue for internal connection (httpd or sshd);
|
||||||
* */
|
* */
|
||||||
struct queue q[2];
|
struct queue q[2];
|
||||||
|
|
||||||
/* SOCK_DGRAM */
|
|
||||||
struct sockaddr_storage client_addr; /* Contains the remote client address */
|
|
||||||
socklen_t addrlen;
|
|
||||||
|
|
||||||
int local_endpoint; /* Contains the local address */
|
|
||||||
|
|
||||||
time_t last_active;
|
|
||||||
|
|
||||||
/* double linked list of timeouts */
|
|
||||||
struct connection *timeout_prev, *timeout_next;
|
|
||||||
|
|
||||||
/* We need one local socket for each target server, so we know where to
|
|
||||||
* forward server responses */
|
|
||||||
int target_sock;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
struct listen_endpoint {
|
|
||||||
int socketfd; /* file descriptor of listening socket */
|
|
||||||
int type; /* SOCK_DGRAM | SOCK_STREAM */
|
|
||||||
int family; /* AF_INET | AF_UNIX */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#define FD_CNXCLOSED 0
|
#define FD_CNXCLOSED 0
|
||||||
#define FD_NODATA -1
|
#define FD_NODATA -1
|
||||||
#define FD_STALLED -2
|
#define FD_STALLED -2
|
||||||
|
|
||||||
/* String description of a connection */
|
|
||||||
#define MAX_NAMELENGTH (NI_MAXHOST + NI_MAXSERV + 1)
|
|
||||||
struct connection_desc {
|
|
||||||
char peer[MAX_NAMELENGTH], service[MAX_NAMELENGTH],
|
|
||||||
local[MAX_NAMELENGTH], target[MAX_NAMELENGTH];
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
NON_BLOCKING = 0,
|
|
||||||
BLOCKING = 1
|
|
||||||
} connect_blocking;
|
|
||||||
|
|
||||||
|
|
||||||
/* common.c */
|
/* common.c */
|
||||||
void init_cnx(struct connection *cnx);
|
void init_cnx(struct connection *cnx);
|
||||||
int set_nonblock(int fd);
|
int connect_addr(struct connection *cnx, int fd_from);
|
||||||
void connect_addr(struct connection *cnx, int fd_from, connect_blocking blocking);
|
|
||||||
int fd2fd(struct queue *target, struct queue *from);
|
int fd2fd(struct queue *target, struct queue *from);
|
||||||
char* sprintaddr(char* buf, size_t size, struct addrinfo *a);
|
char* sprintaddr(char* buf, size_t size, struct addrinfo *a);
|
||||||
void resolve_name(struct addrinfo **out, char* fullname);
|
void resolve_name(struct addrinfo **out, char* fullname);
|
||||||
int get_connection_desc(struct connection_desc* desc, const struct connection *cnx);
|
void log_connection(struct connection *cnx);
|
||||||
void log_connection(struct connection_desc* desc, const struct connection *cnx);
|
|
||||||
void set_proctitle_shovel(struct connection_desc* desc, const struct connection *cnx);
|
|
||||||
int check_access_rights(int in_socket, const char* service);
|
int check_access_rights(int in_socket, const char* service);
|
||||||
void setup_signals(void);
|
void setup_signals(void);
|
||||||
void setup_syslog(const char* bin_name);
|
void setup_syslog(const char* bin_name);
|
||||||
void drop_privileges(const char* user_name, const char* chroot_path);
|
void drop_privileges(const char* user_name);
|
||||||
void set_capabilities(int cap_net_admin);
|
|
||||||
void write_pid_file(const char* pidfile);
|
void write_pid_file(const char* pidfile);
|
||||||
|
void log_message(int type, char* msg, ...);
|
||||||
void dump_connection(struct connection *cnx);
|
void dump_connection(struct connection *cnx);
|
||||||
int resolve_split_name(struct addrinfo **out, char* hostname, char* port);
|
int resolve_split_name(struct addrinfo **out, const char* hostname, const char* port);
|
||||||
|
|
||||||
int start_listen_sockets(struct listen_endpoint *sockfd[]);
|
int start_listen_sockets(int *sockfd[], struct addrinfo *addr_list);
|
||||||
|
|
||||||
int defer_write(struct queue *q, void* data, ssize_t data_size);
|
int defer_write(struct queue *q, void* data, int data_size);
|
||||||
int defer_write_before(struct queue *q, void* data, ssize_t data_size);
|
|
||||||
int flush_deferred(struct queue *q);
|
int flush_deferred(struct queue *q);
|
||||||
|
|
||||||
extern struct sslhcfg_item cfg;
|
extern int probing_timeout, verbose, inetd, foreground,
|
||||||
|
background, transparent, numeric;
|
||||||
|
extern struct sockaddr_storage addr_ssl, addr_ssh, addr_openvpn;
|
||||||
extern struct addrinfo *addr_listen;
|
extern struct addrinfo *addr_listen;
|
||||||
|
extern const char* USAGE_STRING;
|
||||||
|
extern const char* user_name, *pid_file;
|
||||||
extern const char* server_type;
|
extern const char* server_type;
|
||||||
|
|
||||||
#if defined(__APPLE__) && (MAC_OS_X_VERSION_MIN_REQUIRED < 1080)
|
|
||||||
extern int hosts_ctl();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* sslh-fork.c */
|
/* sslh-fork.c */
|
||||||
void start_shoveler(int);
|
void start_shoveler(int);
|
||||||
|
|
||||||
void main_loop(struct listen_endpoint *listen_sockets, int num_addr_listen);
|
void main_loop(int *listen_sockets, int num_addr_listen);
|
||||||
|
|
||||||
/* landlock.c */
|
|
||||||
void setup_landlock(void);
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
21
config.h.in
21
config.h.in
@ -1,21 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
#ifndef CONFIG_H
|
|
||||||
/* Template for config.h, filled by `configure`. */
|
|
||||||
|
|
||||||
/* Libwrap, to support host_ctl, /etc/allow and /etc/deny */
|
|
||||||
#undef HAVE_LIBWRAP
|
|
||||||
|
|
||||||
/* Landlock sandboxing Linux LSM */
|
|
||||||
#undef HAVE_LANDLOCK
|
|
||||||
|
|
||||||
/* Support for Proxy-protocol using libproxyprotocol */
|
|
||||||
#undef HAVE_PROXYPROTOCOL
|
|
||||||
|
|
||||||
/* libcap support, to use Linux capabilities */
|
|
||||||
#undef HAVE_LIBCAP
|
|
||||||
|
|
||||||
/* libbsd, to change process name */
|
|
||||||
#undef HAVE_LIBBSD
|
|
||||||
|
|
||||||
#endif
|
|
20
configure.ac
20
configure.ac
@ -1,20 +0,0 @@
|
|||||||
|
|
||||||
dnl Use autoconf to generate the `configure` script from this and Makefile.in
|
|
||||||
dnl This is awkardly adapted from https://github.com/edrosten/autoconf_tutorial
|
|
||||||
dnl (all mistakes mine)
|
|
||||||
|
|
||||||
AC_INIT
|
|
||||||
AC_CONFIG_HEADERS(config.h)
|
|
||||||
AC_CONFIG_FILES([Makefile])
|
|
||||||
|
|
||||||
AC_CHECK_LIB([wrap], [hosts_ctl], [AC_DEFINE(HAVE_LIBWRAP) LIBS="$LIBS -lwrap" ], [])
|
|
||||||
AC_CHECK_LIB([cap], [cap_get_proc], [AC_DEFINE(HAVE_LIBCAP) LIBS="$LIBS -lcap" ], [])
|
|
||||||
AC_CHECK_LIB([bsd], [setproctitle], [AC_DEFINE(HAVE_LIBBSD) LIBS="$LIBS -lbsd" ], [])
|
|
||||||
|
|
||||||
AC_CHECK_HEADERS(linux/landlock.h, AC_DEFINE(HAVE_LANDLOCK), [])
|
|
||||||
AC_CHECK_HEADERS(proxy_protocol.h, [AC_DEFINE(HAVE_PROXYPROTOCOL) LIBS="$LIBS -lproxyprotocol" ], [])
|
|
||||||
|
|
||||||
LIBS="$LIBS"
|
|
||||||
AC_SUBST([LIBS])
|
|
||||||
|
|
||||||
AC_OUTPUT
|
|
@ -1,97 +0,0 @@
|
|||||||
#!/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,283 +0,0 @@
|
|||||||
# Daisy-Chaining-Transparency #
|
|
||||||
This documentation goes a level deeper, what happens in the operating system with IP-addresses, and why some combinations of programs are failing, when they use the same transparency method.
|
|
||||||
There are situations, where you need to combine two applications, both working as ip-transparent proxies, to reach your goal. One example is, having nginx or stunnel as an proxytunnel-endpoint for tls tunneled ssh connections through https-proxies. An example for such a combination will be desribed at the end of this article.<br>
|
|
||||||
Unfortunately you will see a lot of errors popping out: **Address already in use**<br>
|
|
||||||
[This article from Cloudflare blog](https://blog.cloudflare.com/how-to-stop-running-out-of-ephemeral-ports-and-start-to-love-long-lived-connections) explains why this is happening, while it is describing the solution to another problem. However this is a close relative to our problem.
|
|
||||||
|
|
||||||
Let us look to the following example: We have sslh (S) accepting connections from a client (C) and forwarding one of those connections to a man-in-the-middle (M), which finally forwards this connection to sshd. If everything works perfectly, we would like to see those connections.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
But unfortunately we are receiving in many constellations errors, when M tries to open its connection to our final target sshd, here called T.
|
|
||||||
Let us look more close, why that is happening. We need for this two terminal windows on the same server.<br>
|
|
||||||
### First example, uncooperative applications ###
|
|
||||||
As the problem has nothing to do with transparency itself, but only of reuse of same IP-addresses and ports, we avoid the overhead of the additional capablities, to keep the example easy and clear.
|
|
||||||
In the first terminal we are starting python3 and entering the following three lines:
|
|
||||||
```
|
|
||||||
user@host:~$ python3
|
|
||||||
Python 3.11.2 (main, May 2 2024, 11:59:08) [GCC 12.2.0] on linux
|
|
||||||
Type "help", "copyright", "credits" or "license" for more information.
|
|
||||||
>>> import socket
|
|
||||||
>>> sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
||||||
>>> sock.bind(("192.168.255.254", 12345))
|
|
||||||
>>>
|
|
||||||
```
|
|
||||||
|
|
||||||
Now we are going to the second terminal window, and trying just the same:
|
|
||||||
```
|
|
||||||
Python 3.11.2 (main, May 2 2024, 11:59:08) [GCC 12.2.0] on linux
|
|
||||||
Type "help", "copyright", "credits" or "license" for more information.
|
|
||||||
>>> import socket
|
|
||||||
>>> sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
||||||
>>> sock.bind(("192.168.255.254", 12345))
|
|
||||||
Traceback (most recent call last):
|
|
||||||
File "<stdin>", line 1, in <module>
|
|
||||||
OSError: [Errno 98] Address already in use
|
|
||||||
sock.bind(("192.168.255.254", 12346)) ##this however works!!
|
|
||||||
```
|
|
||||||
Here we are getting the error, which caused many of us hours of research.
|
|
||||||
The problem is caused by the fact, that the kernel does not know at the moment of the bind()-call, how we want to use the socket. If we use this as a server socket, and will do a listen()-call as next, this will not work, as for server sockets, the two-value tuple ADDRESS:PORT needs to be unique only to the process, tied to this socket.
|
|
||||||
That is the reason, that there are port ranges, reserved for servers, where the administrator is responsible not to assign the same port to two applications.
|
|
||||||
But as server ports are coming from a range, which will not be used for client connections, a server can be sure, that if it is started at any time in the future, no outgoing client has used its port for an outbound connection.
|
|
||||||
Clients are usually using ports from a so called [ephemeral port range](https://en.wikipedia.org/wiki/Ephemeral_port).
|
|
||||||
However, for clients each connection is valid, as long as one value in the four-tuples describing the connection is different. In our example above, the two values from the destination are different, so this connection could be established (in theory) without conflicts.
|
|
||||||
To make that happen, you need to deploy a special socket option to this socket, to explain, that we "know the risks" and we will reuse the ip-port combination.
|
|
||||||
And, as we see from the second bind() in the second example, the error message: _**Address already in use**_ really means: _**Address:Port already in use**_
|
|
||||||
|
|
||||||
### Taking Care, part I ###
|
|
||||||
|
|
||||||
Ok, now we are entering our two terminals again, pressing Ctrl-D to finish python, and start a new session like this in both terminals:
|
|
||||||
```
|
|
||||||
user@host:~$ python3
|
|
||||||
Python 3.11.2 (main, May 2 2024, 11:59:08) [GCC 12.2.0] on linux
|
|
||||||
Type "help", "copyright", "credits" or "license" for more information.
|
|
||||||
>>> import socket
|
|
||||||
>>> sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
||||||
>>> sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
||||||
>>> sock.bind(("192.168.255.254", 12345))
|
|
||||||
>>>
|
|
||||||
```
|
|
||||||
And we will see, that the problem is solved, when both applications are taking care.
|
|
||||||
|
|
||||||
### Taking Care, part II ###
|
|
||||||
|
|
||||||
Ok, now we are going back to our terminals, pressing again Ctrl-D to finish python, and start new sessions like this:
|
|
||||||
In the first terminal we repeat the input from our first example, without the setsockopt() call:
|
|
||||||
```
|
|
||||||
user@host:~$ python3
|
|
||||||
Python 3.11.2 (main, May 2 2024, 11:59:08) [GCC 12.2.0] on linux
|
|
||||||
Type "help", "copyright", "credits" or "license" for more information.
|
|
||||||
>>> import socket
|
|
||||||
>>> sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
||||||
>>> sock.bind(("192.168.255.254", 12345))
|
|
||||||
>>>
|
|
||||||
```
|
|
||||||
in the second terminal, we enter our modified cooperative example:
|
|
||||||
|
|
||||||
```
|
|
||||||
user@host:~$ python3
|
|
||||||
Python 3.11.2 (main, May 2 2024, 11:59:08) [GCC 12.2.0] on linux
|
|
||||||
Type "help", "copyright", "credits" or "license" for more information.
|
|
||||||
>>> import socket
|
|
||||||
>>> sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
||||||
>>> sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
||||||
>>> sock.bind(("192.168.255.254", 12345))
|
|
||||||
Traceback (most recent call last):
|
|
||||||
File "<stdin>", line 1, in <module>
|
|
||||||
OSError: [Errno 98] Address already in use
|
|
||||||
>>>
|
|
||||||
```
|
|
||||||
Oops, here is our error again. And this is the reason, why I called this method "cooperative". As the first application has bound to that IP:PORT combination, without telling, that "it knows the risk", the kernel denies us, to use this combinations, as we may break the already active application. The first application is not "cooperative" :-(<br>
|
|
||||||
Ok, but the kernel gives as some more possibilities: As we now get connections from a uncoperative application, we can no longer use the Client-IP-Port combination. We need to use a conflict free port for the client IP, to succeed. So lets get back to terminal 2 and continue after the error with the following commands:
|
|
||||||
```
|
|
||||||
user@host:~$ python3
|
|
||||||
Python 3.11.2 (main, May 2 2024, 11:59:08) [GCC 12.2.0] on linux
|
|
||||||
Type "help", "copyright", "credits" or "license" for more information.
|
|
||||||
>>> import socket
|
|
||||||
>>> sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
||||||
>>> sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
||||||
>>> sock.bind(("192.168.255.254", 12345))
|
|
||||||
Traceback (most recent call last):
|
|
||||||
File "<stdin>", line 1, in <module>
|
|
||||||
OSError: [Errno 98] Address already in use
|
|
||||||
>>> sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 0)
|
|
||||||
>>> sock.setsockopt(socket.SOL_IP, socket.IP_BIND_ADDRESS_NO_PORT, 1)
|
|
||||||
>>> sock.bind(("192.168.255.254", 0))
|
|
||||||
>>>
|
|
||||||
```
|
|
||||||
This last example makes it neccessary, that you use a recent version of the python3 socket library, as older versions, have the option _IP_BIND_ADDRESS_NO_PORT_ not yet defined.
|
|
||||||
With his behaviour, we are telling the kernel, that the kernel should assign us a conflict free port address at latest possible moment, while calling connect().
|
|
||||||
From `man ip`:
|
|
||||||
```
|
|
||||||
Inform the kernel to not reserve an ephemeral port when using bind(2)
|
|
||||||
with a port number of 0. The port will later be automatically chosen
|
|
||||||
at connect(2) time, in a way that allows sharing a source port as
|
|
||||||
long as the 4-tuple is unique.
|
|
||||||
```
|
|
||||||
|
|
||||||
Ok, now with those two actions an application is really ready for cooperative working in ip-transparent chains.
|
|
||||||
If you are running in problems, with any kind of application, where you are redirecting transparent traffic to from sslh, check the following:
|
|
||||||
- Are you using a recent version of sslh, having the described feature enabled
|
|
||||||
- are you using the most recent version of the other application
|
|
||||||
If you can confirm both checks, tell the maintainers of the other application, about possible fixes, and send them a link to this article.
|
|
||||||
|
|
||||||
## Practical Use Of Daisy-Chaining: Proxytunnel Endpoint ##
|
|
||||||
|
|
||||||
One reasons, why we want to combine two programs is related to the core functionality of sslh. You wish to hide your ssh connection behind a https port.
|
|
||||||
But now you would like, to reach this port via the [proxy-tunnel application](https://github.com/proxytunnel/proxytunnel), though an restrictive http(s) proxy. This is in many cases, one of the few methods, to escape from restricted private networks, like in companies, schools and universities. Unfortunately, many of those proxy-servers will check, that the protocol leaving the proxy is really tls. Therefore you need and endpoint in your system, which will terminate the tls-connection and forward the encapsulated ssh stream to the sshd. Sslh can't do tls termination, as this is not a core job of tls. One of the solutions tried here is stunnel. Stunnel can do transparency like sslh, but unfortunately belongs to the uncooperative ip-transparent programs. At the time of writing this article, you can use stunnel as the first-in-chain, and a very recent sslh as second in chain. But nginx (or openresty) is capable of this, however it prefers (at least in the tested versions), mostly the second way just selecting a new random port and not (always) preserving the original source port, what makes debugging of events much easier.
|
|
||||||
|
|
||||||
|
|
||||||
```
|
|
||||||
stream {
|
|
||||||
ssl_preread on;
|
|
||||||
|
|
||||||
map $ssl_preread_server_name $name {
|
|
||||||
default master.yourdomain.top;
|
|
||||||
t1.yourdomain.top t1.yourdomain.top;
|
|
||||||
t2.yourdomain.top t2.yourdomain.top;
|
|
||||||
cryptic.foo.bar location.selfsigned.cert;
|
|
||||||
}
|
|
||||||
|
|
||||||
## $destination port :443 is assumed, beeing as real
|
|
||||||
## webserver. Either anothe nginx http-server or apache
|
|
||||||
## or anything other ...
|
|
||||||
map $ssl_preread_server_name $dest {
|
|
||||||
default 192.168.255.254:443;
|
|
||||||
t1.yourdomain.top 192.168.255.254:443;
|
|
||||||
t2.yourdomain.top 192.168.255.254:444;
|
|
||||||
cryptic.foo.notexist 192.168.255.254:445;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
## this is the server, to handle incoming tcp streams
|
|
||||||
## and dispatching them transparent to $dest
|
|
||||||
## 192.168.255.254:1443 is the address, where
|
|
||||||
## the front facing sslh sends traffic to
|
|
||||||
server {
|
|
||||||
listen 192.168.255.254:1443;
|
|
||||||
proxy_connect_timeout 5s;
|
|
||||||
proxy_timeout 3m;
|
|
||||||
proxy_bind $remote_addr transparent;
|
|
||||||
proxy_pass $dest;
|
|
||||||
ssl_preread on;
|
|
||||||
}
|
|
||||||
|
|
||||||
## this is a basic endpoint for proxy-tunnel connections
|
|
||||||
server {
|
|
||||||
listen 192.168.255.254:444 ssl;
|
|
||||||
ssl_protocols TLSv1.2 TLSv1.3;
|
|
||||||
ssl_session_cache shared:SSL:10m;
|
|
||||||
ssl_session_timeout 10m;
|
|
||||||
ssl_prefer_server_ciphers on;
|
|
||||||
ssl_ciphers "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256";
|
|
||||||
## give facl to the nginx user, see later in article
|
|
||||||
ssl_certificate /etc/letsencrypt/live/$name/fullchain.pem;
|
|
||||||
ssl_certificate_key /etc/letsencrypt/live/$name/privkey.pem;
|
|
||||||
ssl_dhparam /etc/nginx/dhparam-nginx.pem;
|
|
||||||
ssl_session_cache shared:MySSL:10m;
|
|
||||||
ssl_session_tickets off;
|
|
||||||
proxy_connect_timeout 5s;
|
|
||||||
proxy_timeout 3m;
|
|
||||||
proxy_bind $remote_addr transparent;
|
|
||||||
proxy_pass 192.168.255.254:22 ;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
## this is a fancy destination, using some tricks, to fool
|
|
||||||
## some proxies.
|
|
||||||
server {
|
|
||||||
listen 192.168.255.254:445 ssl;
|
|
||||||
ssl_protocols TLSv1.2 TLSv1.3;
|
|
||||||
ssl_session_cache shared:SSL:10m;
|
|
||||||
ssl_session_timeout 10m;
|
|
||||||
ssl_prefer_server_ciphers on;
|
|
||||||
ssl_ciphers "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256";
|
|
||||||
## this points to a directory, containg a self signed certificate
|
|
||||||
## created with CN and SAN to an not officially existing
|
|
||||||
## domain, e.g. cryptic.foo.notexists
|
|
||||||
ssl_certificate /etc/nginx/$name/fullchain.pem;
|
|
||||||
ssl_certificate_key /etc/nginx/$name/privkey.pem;
|
|
||||||
ssl_dhparam /etc/nginx/dhparam-nginx.pem;
|
|
||||||
ssl_session_cache shared:MySSL:10m;
|
|
||||||
ssl_session_tickets off;
|
|
||||||
## additional trick: requiring client certificate authentication
|
|
||||||
ssl_client_certificate /etc/nginx/public.cert ;
|
|
||||||
ssl_verify_client on ;
|
|
||||||
proxy_connect_timeout 5s;
|
|
||||||
proxy_timeout 3m;
|
|
||||||
proxy_bind $remote_addr transparent;
|
|
||||||
proxy_pass 192.168.255.254:22;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
This nginx stream server shows a combination of different possibilities, how to establish an endpoint for proxytunnel or similar programs, to hide your ssh connection. Remember: Hiding ssh tunneled in tls does not neccessarly raise the security of your connection. In theory a combination of two methods may also lower the security. In addition there are more drawbacks: Such a connection needs more cpu on both ends for doing double crypto, and the throughput through he connection drops, as the ratio payload/header increases. the single ssh-packets are smaller, than usual.
|
|
||||||
So it is not recommended for doing scp or sftp through such connections.
|
|
||||||
|
|
||||||
### Nginx Configration Explained ###
|
|
||||||
|
|
||||||
However, there are situations, where this is the only way, to reach your server.
|
|
||||||
So we have now this nginx instance helping us out.
|
|
||||||
The server stanza listening on _192.168.255.254:1443_ is the main process, accepting incoming connections forwarded from sslh. It prereads tls SNI names, to get destination information. When in the nginx-configuration a variable is used, which is set by an map action, the mapping takes place just in that moment, when the variable needs to be expanded.
|
|
||||||
So we have either default values, in case there is no SNI, or values for individual SNI.
|
|
||||||
|
|
||||||
The connection stream is than forwarded ip-transparent to a given destination, which is itself another nginx instance, defined in the configuration file.
|
|
||||||
|
|
||||||
#### The Standard TLS-Termination ####
|
|
||||||
|
|
||||||
The server stanza listening on _192.168.255.254:444_ is a very basic TLS terminating endpoint. In most cases, a proxy is happy with a destination like this. Here nginx just terminates the TLS connection and shovels the incoming packets over to sshd in ip-transparent way. The proxytunnel configuration for this target will look like:
|
|
||||||
```
|
|
||||||
host t2.yourdomain.top
|
|
||||||
ProxyCommand /path/to/proxytunnel -e -p PROXY-IP:3128 -d t2.yourdomain.top:443
|
|
||||||
ServerAliveInterval 30
|
|
||||||
```
|
|
||||||
If you have no proxytunnel on your system, you can also use openssl. In this case the configuration looks like:
|
|
||||||
```
|
|
||||||
host t2-alias
|
|
||||||
ProxyCommand openssl s_client -proxy PROXY-IP:3128 -connect t2.yourdomain.top:443
|
|
||||||
ServerAliveInterval 30
|
|
||||||
```
|
|
||||||
`ssh ts-alias` will use this configuration.
|
|
||||||
|
|
||||||
One point from the above configuration needs further explanation, as we just refer to our common letsencrypt store, for certificates.
|
|
||||||
|
|
||||||
##### Excurse To File ACLs #####
|
|
||||||
|
|
||||||
We are using here our common letsencrypt certificate store, as copying certificates after renewal is a pain in the as and prone to errors.
|
|
||||||
A small script makes sure, that all applications can read the certificates:
|
|
||||||
```
|
|
||||||
for i in Debian-exim dovecot mail www-data nginx ; do
|
|
||||||
setfacl -Rdm u:$i:rX ./archive/
|
|
||||||
setfacl -Rdm u:$i:rX ./live/
|
|
||||||
setfacl -Rm u:$i:rX ./archive/
|
|
||||||
setfacl -Rm u:$i:rX ./live/
|
|
||||||
for file in $( find ./live ./archive -type l,f ) ; do
|
|
||||||
echo -e "$i $file set"
|
|
||||||
setfacl -m u:$i:r $file
|
|
||||||
done
|
|
||||||
done
|
|
||||||
```
|
|
||||||
This script needs only to be run, when a new application user needs access.
|
|
||||||
The first two lines are making sure (watch the **d**), that the given options
|
|
||||||
on those directories will be the default for newly created files below.
|
|
||||||
The uppercase **X** means, that **x**-access will only be granted to directories or files, having already **x** set for others.
|
|
||||||
|
|
||||||
#### The Tricky TLS Termination ####
|
|
||||||
|
|
||||||
The server stanza listening on _192.168.255.254:445_ is a more tricky TLS terminating endpoint. Some proxies are trying to inspect the destination, before letting you go. Some of them you can fool, others not.
|
|
||||||
One trick can be to use an phantasy domain name with a self signed certificate, no dns server will ever resolve. As you are using this name as SNI name in your proxy-tunnel connection, it will work. This can be also a way, to hide your sshd, if someone, who knows you are using sslh tries to find your sshd. As long, as this person does not get access to proxies you are using, this may help in some situations.
|
|
||||||
Another trick is, requiring a client certificate for authentication. This is in each case the much better approach, as you can combine this also with official connected domain names. Without the client certificate no sshd!
|
|
||||||
The client certificate does not increase the above mentioned ratio between payload and headers, as this is only used while establishing the TLS connection.
|
|
||||||
It prevents however a proxy, doing a parallel sneak to your destination, to figure out what is behind. I have seen proxies letting you connect with this method, others are denying access.
|
|
||||||
|
|
||||||
To access this endpoint, the proxytunnel configuration inside `~/.ssh/config`
|
|
||||||
will look like:
|
|
||||||
```
|
|
||||||
host cryptic.foo.notexists
|
|
||||||
ProxyCommand /path/to/proxytunnel -e -c ~/public.cert -k ~/private.pem -C ~/myOwnCA.cert -p PROXY-IP:3128 -o cryptic.foo.notexists -d t1.yourdomain.top:443
|
|
||||||
ServerAliveInterval 30
|
|
||||||
```
|
|
||||||
-C gives the certfile to your certificate used for selfsigning the server certificate. You can also set -z for not verifying the certificate, but that is not recommended! `t1.yourdomain.top` represents a valid domain name, where the listening sslh can be reached. This name is not taken for SNI, because **-o** sets our hidden SNI name. Whenever you enter `ssh cryptic.foo.notexists` you will get connected to your server!
|
|
||||||
|
|
||||||
The best reommendation however is: Avoid self signed certs, if you are not really sure, what you are doing.
|
|
||||||
|
|
170
doc/FAQ.md
170
doc/FAQ.md
@ -1,170 +0,0 @@
|
|||||||
Frequently Asked Questions
|
|
||||||
==========================
|
|
||||||
|
|
||||||
When something doesn't work, look up here... and if it still
|
|
||||||
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.
|
|
||||||
|
|
||||||
Getting more info
|
|
||||||
=================
|
|
||||||
|
|
||||||
There are several `verbose` options that each enable a set
|
|
||||||
of messages, each related to some event type. See
|
|
||||||
`example.cfg` for a list of them.
|
|
||||||
|
|
||||||
If something doesn't work, you'll want to run `sslh` with
|
|
||||||
lots of logging, and the logging directly in the terminal
|
|
||||||
(Otherwise, logs are sent to `syslog`, and usually end up in
|
|
||||||
`/var/log/auth.log`). There is a general `--verbose` option
|
|
||||||
that will allow you to enable all messages:
|
|
||||||
|
|
||||||
```
|
|
||||||
sslh -v 3 -f -F myconfig.cfg
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
forward to [PROBE] failed:connect: Connection refused
|
|
||||||
=====================================================
|
|
||||||
|
|
||||||
Usually this means `sslh` is configured to forward a
|
|
||||||
protocol somewhere, but no service is listening on the
|
|
||||||
target address. Check your `sslh` configuration, check the
|
|
||||||
corresponding server really is listening and running.
|
|
||||||
Finally, check the server is listening where you expect it
|
|
||||||
to:
|
|
||||||
|
|
||||||
```
|
|
||||||
netstat -lpt
|
|
||||||
```
|
|
||||||
|
|
||||||
I get a segmentation fault!
|
|
||||||
===========================
|
|
||||||
|
|
||||||
Well, it's not yours (fault): a segfault is always a bug in
|
|
||||||
the programme. Usually standard use cases are well tested,
|
|
||||||
so it may be related to something unusual in your
|
|
||||||
configuration, or even something wrong, but it should still
|
|
||||||
never result in a segfault.
|
|
||||||
|
|
||||||
Thankfully, when they are deterministic, segfaults are
|
|
||||||
usually fairly easy to fix if you're willing to run a few
|
|
||||||
diagnostics to help the developer.
|
|
||||||
|
|
||||||
First, make sure you have debug symbols:
|
|
||||||
```
|
|
||||||
$ file sslh-select
|
|
||||||
sslh-select: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=a758ac75ff11f1ace577705b4d6627e301940b59, with debug_info, not stripped
|
|
||||||
```
|
|
||||||
|
|
||||||
Note `with debug_info, not stripped` at the end. If you
|
|
||||||
don't have that, your distribution stripped the binary: you
|
|
||||||
will need to get the source code and compile it yourself
|
|
||||||
(that way, you will also get the latest version).
|
|
||||||
|
|
||||||
Install `valgrind` and run `sslh` under it:
|
|
||||||
|
|
||||||
```
|
|
||||||
valgrind --leak-check=full ./sslh-fork -v 2 -f -F yourconfig.cfg
|
|
||||||
```
|
|
||||||
|
|
||||||
Report the full output to the mailing list or github.
|
|
||||||
Valgrind is very powerful and gives precise hints of what is
|
|
||||||
wrong and why. For example on `sslh` issue
|
|
||||||
(#273)[https://github.com/yrutschle/sslh/issues/273]:
|
|
||||||
|
|
||||||
```
|
|
||||||
sudo valgrind --leak-check=full ./sslh-fork -v 2 -f -F /etc/sslh.cfg
|
|
||||||
==20037== Memcheck, a memory error detector
|
|
||||||
==20037== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
|
|
||||||
==20037== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
|
|
||||||
==20037== Command: ./sslh-fork -v 2 -f -F /etc/sslh.cfg
|
|
||||||
==20037==
|
|
||||||
sslh-fork v1.21b-1-g2c93a01-dirty started
|
|
||||||
--20037-- WARNING: unhandled arm-linux syscall: 403
|
|
||||||
--20037-- You may be able to write your own handler.
|
|
||||||
--20037-- Read the file README_MISSING_SYSCALL_OR_IOCTL.
|
|
||||||
--20037-- Nevertheless we consider this a bug. Please report
|
|
||||||
--20037-- it at http://valgrind.org/support/bug_reports.html.
|
|
||||||
==20040== Conditional jump or move depends on uninitialised value(s)
|
|
||||||
==20040== at 0x112A3C: parse_tls_header (tls.c:162)
|
|
||||||
==20040== by 0x111CEF: is_tls_protocol (probe.c:214)
|
|
||||||
==20040== by 0x11239F: probe_client_protocol (probe.c:366)
|
|
||||||
==20040== by 0x10A8F7: start_shoveler (sslh-fork.c:98)
|
|
||||||
==20040== by 0x10AE9B: main_loop (sslh-fork.c:200)
|
|
||||||
==20040== by 0x1114FB: main (sslh-main.c:322)
|
|
||||||
==20040==
|
|
||||||
```
|
|
||||||
|
|
||||||
Here we see that something wrong is happening at `tls.c`
|
|
||||||
line 162, and it's linked to an uninitialised value.
|
|
||||||
|
|
||||||
Using sslh for virtual hosting
|
|
||||||
==============================
|
|
||||||
|
|
||||||
Virtual hosting refers to having several domain names behind
|
|
||||||
a single IP address. All Web servers handle this, but
|
|
||||||
sometimes it can be useful to do it with `sslh`.
|
|
||||||
|
|
||||||
TLS virtual hosting with SNI
|
|
||||||
----------------------------
|
|
||||||
|
|
||||||
For TLS, this is done very simply using Server Name
|
|
||||||
Indication, SNI for short, which is a TLS extension whereby
|
|
||||||
the client indicates the name of the server it wishes to
|
|
||||||
connect to. This can be a very powerful way to separate
|
|
||||||
several TLS-based services hosted behind the same port:
|
|
||||||
simply name each service with its own hostname. For example,
|
|
||||||
we could define `mail.rutschle.net`, `im.rutschle.net`,
|
|
||||||
`www.rutschle.net`, all of which point to the same IP
|
|
||||||
address. `sslh` uses the `sni_hostnames` setting of the
|
|
||||||
TLS probe to do this, e.g.:
|
|
||||||
|
|
||||||
```
|
|
||||||
protocols: (
|
|
||||||
{ name: "tls";
|
|
||||||
host: "localhost";
|
|
||||||
port: "993";
|
|
||||||
sni_hostnames: [ "mail.rutschle.net" ];
|
|
||||||
},
|
|
||||||
{ name: "tls";
|
|
||||||
host: "localhost";
|
|
||||||
port: "xmpp-client";
|
|
||||||
sni_hostnames: [ "im.rutschle.net" ];
|
|
||||||
},
|
|
||||||
{ name: "tls";
|
|
||||||
host: "localhost";
|
|
||||||
port: "4443";
|
|
||||||
sni_hostnames: [ "www.rutschle.net" ];
|
|
||||||
}
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
HTTP virtual hosting with regex
|
|
||||||
-------------------------------
|
|
||||||
|
|
||||||
If you wish to serve several Web domains over HTTP through
|
|
||||||
`sslh`, you can do this simply by using regular expressions
|
|
||||||
on the Host specification part of the HTTP query.
|
|
||||||
|
|
||||||
The following example forwards connections to `host_A.acme`
|
|
||||||
to 192.168.0.2, and connections to `host_B.acme` to
|
|
||||||
192.168.0.3.
|
|
||||||
|
|
||||||
```
|
|
||||||
protocols: (
|
|
||||||
{ name: "regex";
|
|
||||||
host: "192.168.0.2";
|
|
||||||
port: "80";
|
|
||||||
regex_patterns:
|
|
||||||
["^(GET|POST|PUT|OPTIONS|DELETE|HEADER) [^ ]* HTTP/[0-9.]*[\r\n]*Host: host_A.acme"] },
|
|
||||||
{ name: "regex";
|
|
||||||
host: "192.168.0.3";
|
|
||||||
port: "80";
|
|
||||||
regex_patterns:
|
|
||||||
["^(GET|POST|PUT|OPTIONS|DELETE|HEADER) [^ ]* HTTP/[0-9.]*[\r\n]*Host: host_B.acme"] }
|
|
||||||
);
|
|
||||||
```
|
|
171
doc/INSTALL.md
171
doc/INSTALL.md
@ -1,171 +0,0 @@
|
|||||||
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 graciously produced by
|
|
||||||
nono303 on his [repository](https://github.com/nono303/sslh).
|
|
||||||
|
|
||||||
|
|
||||||
Compile and install
|
|
||||||
===================
|
|
||||||
|
|
||||||
Dependencies
|
|
||||||
------------
|
|
||||||
|
|
||||||
`sslh` uses:
|
|
||||||
|
|
||||||
* [libconfig](http://www.hyperrealm.com/libconfig/).
|
|
||||||
For Debian this is contained in package `libconfig-dev`.
|
|
||||||
You can compile with or without it using USELIBCONFIG in the Makefile.
|
|
||||||
|
|
||||||
* [libwrap](http://packages.debian.org/source/unstable/tcp-wrappers).
|
|
||||||
For Debian, this is contained in packages `libwrap0-dev`.
|
|
||||||
Presence of libwrap is checked by the configure script.
|
|
||||||
|
|
||||||
* [libsystemd](http://packages.debian.org/source/unstable/libsystemd-dev), in package `libsystemd-dev`.
|
|
||||||
You can compile with or without it using USESYSTEMD in the Makefile.
|
|
||||||
|
|
||||||
* [libcap](http://packages.debian.org/source/unstable/libcap-dev), in package `libcap-dev`.
|
|
||||||
Presence of libcap is checked by the configure script.
|
|
||||||
|
|
||||||
* [libconfig++-dev](https://packages.debian.org/bookworm/libconfig++-dev), in package `lìbconfig++-dev`
|
|
||||||
|
|
||||||
* libbsd, to enable to change the process name (as shown in `ps`,
|
|
||||||
so each forked process shows what protocol and what connection it is serving),
|
|
||||||
which requires `libbsd` at runtime, and `libbsd-dev` at compile-time.
|
|
||||||
Presence of libbsd is checked by the configure script.
|
|
||||||
|
|
||||||
* libpcre2, in package `libpcre2-dev`.
|
|
||||||
You can compile with or without it using ENABLE_REGEX in the Makefile.
|
|
||||||
|
|
||||||
* libev-dev, in package `libev-dev`.
|
|
||||||
If you build a binary specifically and do not build `sslh-ev`, you don't need this.
|
|
||||||
|
|
||||||
* [libproxyprotocol](https://github.com/kosmas-valianos/libproxyprotocol.git)
|
|
||||||
to support HAProxy's [ProxyProtocol](https://www.haproxy.org/download/2.3/doc/proxy-protocol.txt).
|
|
||||||
As this is not part of the distribution packages, set
|
|
||||||
C_INCLUDE_PATH, LD_LIBRARY_PATH, and LIBRARY_PATH to the appropriate
|
|
||||||
values:
|
|
||||||
```
|
|
||||||
export C_INCLUDE_PATH=/home/user/src/libproxyprotocol/src
|
|
||||||
export LD_LIBRARY_PATH=/home/user/src/libproxyprotocol/libs
|
|
||||||
export LIBRARY_PATH=/home/user/src/libproxyprotocol/libs
|
|
||||||
```
|
|
||||||
|
|
||||||
For OpenSUSE, these are contained in packages libconfig9 and
|
|
||||||
libconfig-dev in repository
|
|
||||||
<http://download.opensuse.org/repositories/multimedia:/libs/openSUSE_12.1/>
|
|
||||||
|
|
||||||
For Fedora, you'll need packages `libconfig` and `libconfig-devel`:
|
|
||||||
|
|
||||||
yum install libconfig libconfig-devel
|
|
||||||
|
|
||||||
If you want to rebuild `sslh-conf.c` (after a `make distclean` for example),
|
|
||||||
you will also need to add [conf2struct](https://www.rutschle.net/tech/conf2struct/README.html)
|
|
||||||
(v1.5) to your path.
|
|
||||||
|
|
||||||
The test scripts are written in Perl, and will require
|
|
||||||
`IO::Socket::INET6` (`libio-socket-inet6-perl` in Debian).
|
|
||||||
|
|
||||||
|
|
||||||
Compilation
|
|
||||||
-----------
|
|
||||||
First you have to run `./configure` in the _**./sslh**_ directory. After this,
|
|
||||||
the Makefile is created, and you can do your configuration changes in the Makefile.
|
|
||||||
After each run of ./configure, those changes are gone and the Makefile is recreated.
|
|
||||||
|
|
||||||
There are a couple of configuration options at the beginning of the Makefile:
|
|
||||||
|
|
||||||
* `# override undefine HAVE_LANDLOCK` if you uncomment this line, sslh will be compiled
|
|
||||||
without landlock. This works with gcc versions < 12. Otherwise, if your system has
|
|
||||||
linux/landlock.h in the include path, the configure script creates a _**config.h**_ file,
|
|
||||||
which defines HAVE_LANDLOCK. It is not enough, to set this to 0, you must delete it,
|
|
||||||
when you don't wish to have landlock in your binary.
|
|
||||||
|
|
||||||
* `USELIBWRAP` compiles support for host access control (see `hosts_access(3)`),
|
|
||||||
you will need `libwrap` headers and library to compile (`libwrap0-dev` in Debian).
|
|
||||||
|
|
||||||
* `USELIBCONFIG` compiles support for the configuration file.
|
|
||||||
You will need `libconfig` headers to compile (`libconfig8-dev` in Debian).
|
|
||||||
|
|
||||||
* `USESYSTEMD` compiles support for using systemd socket activation.
|
|
||||||
You will need `systemd` headers to compile (`systemd-devel` in Fedora).
|
|
||||||
|
|
||||||
* `USELIBBSD` compiles support for updating the process name (as shown by `ps`).
|
|
||||||
|
|
||||||
* `USELIBCAP` compiles support for libcap, which allows to inherit capabilities to
|
|
||||||
daughter-processes, which run as restricted users. You need this, when you wish to
|
|
||||||
make sure, that the --user= parameter can be used, without setting capabilities etc.
|
|
||||||
to your binaries, to make this work.
|
|
||||||
|
|
||||||
Now you can do either a plain `make` to create the binaries, or you can do an
|
|
||||||
`make install` to create the binaries and install them.
|
|
||||||
|
|
||||||
|
|
||||||
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
|
|
||||||
--------
|
|
||||||
|
|
||||||
The Makefile produces three different executables:
|
|
||||||
`sslh-fork`, `sslh-select` and `sslh-ev`:
|
|
||||||
|
|
||||||
* `sslh-fork` forks a new process for each incoming connection.
|
|
||||||
It is well-tested and very reliable, but incurs the overhead of many processes.
|
|
||||||
If you are going to use `sslh` for a "small" setup
|
|
||||||
(less than a dozen ssh connections and a low-traffic https server)
|
|
||||||
then `sslh-fork` is probably more suited for you.
|
|
||||||
|
|
||||||
* `sslh-select` uses only one thread, which monitors all connections at once.
|
|
||||||
It only incurs a 16 byte overhead per connection.
|
|
||||||
Also, if it stops, you'll lose all connections,
|
|
||||||
which means you can't upgrade it remotely.
|
|
||||||
If you are going to use `sslh` on a "medium" setup (a few hundreds of connections),
|
|
||||||
or if you are on a system where forking is expensive (e.g. Windows),
|
|
||||||
`sslh-select` will be better.
|
|
||||||
|
|
||||||
* `sslh-ev` is similar to `sslh-select`, but uses `libev` as a backend.
|
|
||||||
This allows using specific kernel APIs that
|
|
||||||
allow to manage thousands of connections concurrently.
|
|
||||||
|
|
||||||
|
|
||||||
Installation
|
|
||||||
------------
|
|
||||||
|
|
||||||
* In general:
|
|
||||||
```sh
|
|
||||||
./configure
|
|
||||||
make
|
|
||||||
cp sslh-fork /usr/local/sbin/sslh
|
|
||||||
cp basic.cfg /etc/sslh.cfg
|
|
||||||
vi /etc/sslh.cfg
|
|
||||||
```
|
|
||||||
* For Debian:
|
|
||||||
```sh
|
|
||||||
cp scripts/etc.init.d.sslh /etc/init.d/sslh
|
|
||||||
```
|
|
||||||
* For CentOS:
|
|
||||||
```sh
|
|
||||||
cp scripts/etc.rc.d.init.d.sslh.centos /etc/rc.d/init.d/sslh
|
|
||||||
```
|
|
||||||
|
|
||||||
You might need to create links in /etc/rc<x>.d so that the server
|
|
||||||
start automatically at boot-up, e.g. under Debian:
|
|
||||||
```sh
|
|
||||||
update-rc.d sslh defaults
|
|
||||||
```
|
|
||||||
|
|
@ -1,72 +0,0 @@
|
|||||||
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/ 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).
|
|
202
doc/config.md
202
doc/config.md
@ -1,202 +0,0 @@
|
|||||||
Configuration
|
|
||||||
=============
|
|
||||||
|
|
||||||
If you use the scripts provided, sslh will get its
|
|
||||||
configuration from /etc/sslh.cfg. Please refer to
|
|
||||||
example.cfg for an overview of all the settings.
|
|
||||||
|
|
||||||
A good scheme is to use the external name of the machine in
|
|
||||||
`listen`, and bind `httpd` to `localhost:443` (instead of all
|
|
||||||
binding to all interfaces): that way, HTTPS connections
|
|
||||||
coming from inside your network don't need to go through
|
|
||||||
`sslh`, and `sslh` is only there as a frontal for connections
|
|
||||||
coming from the internet.
|
|
||||||
|
|
||||||
Note that 'external name' in this context refers to the
|
|
||||||
actual IP address of the machine as seen from your network,
|
|
||||||
i.e. that that is not `127.0.0.1` in the output of
|
|
||||||
`ifconfig(8)`.
|
|
||||||
|
|
||||||
Libwrap support
|
|
||||||
---------------
|
|
||||||
|
|
||||||
Sslh can optionally perform `libwrap` checks for the sshd
|
|
||||||
service: because the connection to `sshd` will be coming
|
|
||||||
locally from `sslh`, `sshd` cannot determine the IP of the
|
|
||||||
client.
|
|
||||||
|
|
||||||
OpenVPN support
|
|
||||||
---------------
|
|
||||||
|
|
||||||
OpenVPN clients connecting to OpenVPN running with
|
|
||||||
`-port-share` reportedly take more than one second between
|
|
||||||
the time the TCP connection is established and the time they
|
|
||||||
send the first data packet. This results in `sslh` with
|
|
||||||
default settings timing out and assuming an SSH connection.
|
|
||||||
To support OpenVPN connections reliably, it is necessary to
|
|
||||||
increase `sslh`'s timeout to 5 seconds.
|
|
||||||
|
|
||||||
Instead of using OpenVPN's port sharing, it is more reliable
|
|
||||||
to use `sslh`'s `--openvpn` option to get `sslh` to do the
|
|
||||||
port sharing.
|
|
||||||
|
|
||||||
Using proxytunnel with sslh
|
|
||||||
---------------------------
|
|
||||||
|
|
||||||
If you are connecting through a proxy that checks that the
|
|
||||||
outgoing connection really is SSL and rejects SSH, you can
|
|
||||||
encapsulate all your traffic in SSL using `proxytunnel` (this
|
|
||||||
should work with `corkscrew` as well). On the server side you
|
|
||||||
receive the traffic with `stunnel` to decapsulate SSL, then
|
|
||||||
pipe through `sslh` to switch HTTP on one side and SSL on the
|
|
||||||
other.
|
|
||||||
|
|
||||||
In that case, you end up with something like this:
|
|
||||||
|
|
||||||
ssh -> proxytunnel -e ----[ssh/ssl]---> stunnel ---[ssh]---> sslh --> sshd
|
|
||||||
Web browser -------------[http/ssl]---> stunnel ---[http]--> sslh --> httpd
|
|
||||||
|
|
||||||
Configuration goes like this on the server side, using `stunnel3`:
|
|
||||||
|
|
||||||
stunnel -f -p mycert.pem -d thelonious:443 -l /usr/local/sbin/sslh -- \
|
|
||||||
sslh -i --http localhost:80 --ssh localhost:22
|
|
||||||
|
|
||||||
* stunnel options:
|
|
||||||
* `-f` for foreground/debugging
|
|
||||||
* `-p` for specifying the key and certificate
|
|
||||||
* `-d` for specifying which interface and port
|
|
||||||
we're listening to for incoming connections
|
|
||||||
* `-l` summons `sslh` in inetd mode.
|
|
||||||
|
|
||||||
* sslh options:
|
|
||||||
* `-i` for inetd mode
|
|
||||||
* `--http` to forward HTTP connections to port 80,
|
|
||||||
and SSH connections to port 22.
|
|
||||||
|
|
||||||
Capabilities support
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
On Linux (only?), you can compile sslh with `USELIBCAP=1` set
|
|
||||||
in the Makefile to make use of POSIX capabilities; this will
|
|
||||||
save the required capabilities needed for transparent proxying
|
|
||||||
for unprivileged processes.
|
|
||||||
|
|
||||||
Alternatively, you may use filesystem capabilities instead
|
|
||||||
of starting sslh as root and asking it to drop privileges.
|
|
||||||
You will need `CAP_NET_BIND_SERVICE` for listening on port 443
|
|
||||||
and `CAP_NET_RAW` for transparent proxying (see
|
|
||||||
`capabilities(7)`).
|
|
||||||
|
|
||||||
You can use the `setcap(8)` utility to give these capabilities
|
|
||||||
to the executable:
|
|
||||||
|
|
||||||
sudo setcap cap_net_bind_service,cap_net_raw+pe sslh-select
|
|
||||||
|
|
||||||
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 is described in its own
|
|
||||||
[document](tproxy.md).
|
|
||||||
|
|
||||||
It might be easier to configure `sslh` to use Proxyprotocol
|
|
||||||
if the backend server supports it.
|
|
||||||
|
|
||||||
Systemd Socket Activation
|
|
||||||
-------------------------
|
|
||||||
If compiled with `USESYSTEMD` then it is possible to activate
|
|
||||||
the service on demand and avoid running any code as root.
|
|
||||||
|
|
||||||
In this mode any listen configuration options are ignored and
|
|
||||||
the sockets are passed by systemd to the service.
|
|
||||||
|
|
||||||
Example socket unit:
|
|
||||||
|
|
||||||
[Unit]
|
|
||||||
Before=sslh.service
|
|
||||||
|
|
||||||
[Socket]
|
|
||||||
ListenStream=1.2.3.4:443
|
|
||||||
ListenStream=5.6.7.8:444
|
|
||||||
ListenStream=9.10.11.12:445
|
|
||||||
FreeBind=true
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=sockets.target
|
|
||||||
|
|
||||||
Example service unit:
|
|
||||||
|
|
||||||
[Unit]
|
|
||||||
PartOf=sslh.socket
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
ExecStart=/usr/sbin/sslh -v -f --ssh 127.0.0.1:22 --tls 127.0.0.1:443
|
|
||||||
KillMode=process
|
|
||||||
CapabilityBoundingSet=CAP_NET_BIND_SERVICE CAP_NET_RAW
|
|
||||||
PrivateTmp=true
|
|
||||||
PrivateDevices=true
|
|
||||||
ProtectSystem=full
|
|
||||||
ProtectHome=true
|
|
||||||
User=sslh
|
|
||||||
|
|
||||||
|
|
||||||
With this setup only the socket needs to be enabled. The sslh service
|
|
||||||
will be started on demand and does not need to run as root to bind the
|
|
||||||
sockets as systemd has already bound and passed them over. If the sslh
|
|
||||||
service is started on its own without the sockets being passed by systemd
|
|
||||||
then it will look to use those defined on the command line or config
|
|
||||||
file as usual. Any number of ListenStreams can be defined in the socket
|
|
||||||
file and systemd will pass them all over to sslh to use as usual.
|
|
||||||
|
|
||||||
To avoid inconsistency between starting via socket and starting directly
|
|
||||||
via the service Requires=sslh.socket can be added to the service unit to
|
|
||||||
mandate the use of the socket configuration.
|
|
||||||
|
|
||||||
Rather than overwriting the entire socket file drop in values can be placed
|
|
||||||
in /etc/systemd/system/sslh.socket.d/<name>.conf with additional ListenStream
|
|
||||||
values that will be merged.
|
|
||||||
|
|
||||||
In addition to the above with manual .socket file configuration there is an
|
|
||||||
optional systemd generator which can be compiled - systemd-sslh-generator
|
|
||||||
|
|
||||||
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
|
|
||||||
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
|
|
||||||
the new dynamic socket unit.
|
|
||||||
|
|
||||||
Fail2ban
|
|
||||||
--------
|
|
||||||
|
|
||||||
If using transparent proxying, just use the standard ssh
|
|
||||||
rules. If you can't or don't want to use transparent
|
|
||||||
proxying, you can set `fail2ban` rules to block repeated ssh
|
|
||||||
connections from an IP address (obviously this depends
|
|
||||||
on the site, there might be legitimate reasons you would get
|
|
||||||
many connections to ssh from the same IP address...)
|
|
||||||
|
|
||||||
See example files in scripts/fail2ban.
|
|
||||||
|
|
||||||
UDP
|
|
||||||
---
|
|
||||||
|
|
||||||
`sslh` can perform demultiplexing on UDP packets as well.
|
|
||||||
This does not work with `sslh-fork` (it is not possible to
|
|
||||||
support UDP with a forking model). Specify a listening
|
|
||||||
address and target protocols with `is_udp: true`. `sslh`
|
|
||||||
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 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`.
|
|
Binary file not shown.
Before Width: | Height: | Size: 29 KiB |
File diff suppressed because it is too large
Load Diff
Before Width: | Height: | Size: 59 KiB |
@ -1,51 +0,0 @@
|
|||||||
# Three Scenarios for the simple transparent proxy setup #
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## Introduction ##
|
|
||||||
The first example is the configuration, which was described in the previous document. I omitted the loopback interface "lo" in those diagrams, trying not no overload the picture.
|
|
||||||
The connections have two different endings, showing the direction of the opening connection (SYN flag) and the answer connection (SYN-ACK flags). This is important, as the traffic in the transparent proxy setup flows somewhat unexpected.
|
|
||||||
|
|
||||||
## Example 1 ##
|
|
||||||
The first example shows the setup, which is described in the [previous document](./simple_transparent_proxy.md). You see the Client connecting to sslh (red connection). When sslh accepts this connection, the SYN-ACK packet is send back to the client, which sends the first data packet(s) together with the ACK for the SYN-ACK. So the bidirectional tcp connection is fully open.
|
|
||||||
Sslh opens now the blue connection to sshd and needs for that elevation rights, as it uses the clients IP address as its own address for opening this connection. Now things are becoming complicated: Sshd send back the first packet with SYN-ACK flags (green line), addressed to the clients IP (dotted line). As already described, that would go wrong, so our routing trick makes this packet beeing deflected back to sslh, so this tcp connection is also opened. But we have here now an asymetric behaviour, that the read and write pathes of the tcp connection are going different routes.
|
|
||||||
The sslh process shuffles now all the bytes coming from sshd from the green line to the red line, vice versa for the packets from the client.
|
|
||||||
|
|
||||||
## Example 2 ##
|
|
||||||
In this example sshd is running on another server. No matter, if this is docker, kvm, virtualbox or another physical host, connected with an ethernet cable. Here we need no dummy interface, so we need another way, to configure our routing deflection. The principle is the same: We need to force packets coming back from sshd going to sslh and not directly back to the client.
|
|
||||||
In this case its your decision, where those rules will be tied in, options are:
|
|
||||||
|
|
||||||
* the startscript of sslh
|
|
||||||
* the docker or kvm configuration
|
|
||||||
* the configuration of the outgoing interface
|
|
||||||
|
|
||||||
Its two lines you need:
|
|
||||||
```
|
|
||||||
ip rule add from SSHD_ADDRESS/32 table sslh
|
|
||||||
ip route add local 0.0.0.0/0 dev lo table sslh
|
|
||||||
```
|
|
||||||
On the sshd host, we need no additional rules, as all traffic is coming back to our sslh host, because this is in this setting the default gateway. The only thing, we need to do: Assign a unique IP address only for sshd and all other services, you wish to hide behind sslh and host on this device.
|
|
||||||
|
|
||||||
There are two ways, how you can add multiple ip addresses to one device. The new _**ip addr add**_ supports multiple add statements to one and the same interface name. So you can just duplicate the interface stancas in the _**/etc/network/interfaces**_ configuration. The problem with this method is, that some older managment tools, like ifconfig are unable to show the additional addresses. So when you are used to some older tools, you may configure sub-interfaces like eth0:1.
|
|
||||||
However my recommendation is, migrate to new tools, get used to it, as old tools don't show you the whole configuration!
|
|
||||||
|
|
||||||
## Example 3 ##
|
|
||||||
This is now the extended version of the previous example. The target host has another path back to the client, as there is a default route to another interface. Now we need **TWO** routing deflections, one on the sslh host, like in scenario 2, and one on the sshd target host.
|
|
||||||
The routing setup on the target host looks like:
|
|
||||||
* Add an routing table name for the deflection table in _**/etc/iproute2/rt_tables**_
|
|
||||||
* Find a location, where you will hook the two routing rules in
|
|
||||||
```
|
|
||||||
ip rule add from SSHD_ADDRESS/32 table sslh_routeback
|
|
||||||
ip route add default via SERVER1_ETH1_IP dev eth0 table sslh_routeback
|
|
||||||
|
|
||||||
```
|
|
||||||
This is setting up a default route for all traffic, originating from the ip address sshd (or any other service) is using, back to the host, hosting sslh. On that host, those packets will be deflected again with the same rule from scenario 2.
|
|
||||||
|
|
||||||
Be aware, that scenario 3 can look very different and the picture above shows only one of those setups. Each configuration, where packets from the target system can find their way back, without beeing forcibly routed through the sslh hosting system, belongs into this category. This are e.g. virtual machines or containers, having interfaces in the same network, like the sslh hosting system. Even, when they look in some drawings embedded in their host, as soon, as they have network interfaces, allowing a direct connection to the client, it is scenario 3!
|
|
||||||
|
|
||||||
## Modifications ##
|
|
||||||
Now you can think about many modifications, but the tools will be the same, for all other thinkable scenarios. You must always make sure, that packets from foreign hosts, will find their way back to the sslh host. So if the chain consists of three or four servers, all need the deflection rules.
|
|
||||||
|
|
||||||
## Important Finding On Routing ##
|
|
||||||
When I went ahead and wrote in my first drawings the warning, that the kernel in scenario 2 and 3 needs to have forwarding in place, I finally tested, that this is not true. **Both scenarios are working without kernel forwarding beeing activated!**
|
|
||||||
The background: The deflecting routing table cames into the game, before the kernel has to made the decision, that packets with non local ip addresses in source and destination must be forwarded. After the routing rule deliveres the packet to sslh and sslh rewrites the source ip, the packet is treated as local, and can pass the system.
|
|
@ -1,135 +0,0 @@
|
|||||||
# Transparent Proxy Configuration Using IP Routing#
|
|
||||||
This documentation is another explanation of the transparent proxy with the goal, beeing secure and minimalistic. Besides this documentation will explain, how and why this configuration works.
|
|
||||||
The explanation will only describe the connection to sshd, so the target sshd can be replace with any other target service, sslh supports.
|
|
||||||
|
|
||||||
## Introduction in the data flow ##
|
|
||||||
This chapter can be skipped, if you just like to configure things fast.
|
|
||||||
This chapter is a little excurse to the dataflow. First point of all is something, which you will unfortunately not see in the nice routing diagrams for iptables or netfilter-tables (nft) like: [Iptables at wikipedia](https://upload.wikimedia.org/wikipedia/commons/thumb/3/37/Netfilter-packet-flow.svg/2560px-Netfilter-packet-flow.svg.png), [Netfilter Flow](https://wiki.nftables.org/wiki-nftables/index.php/Netfilter_hooks#Netfilter_hooks_into_Linux_networking_packet_flows).
|
|
||||||
|
|
||||||
Packets from local application talking to other local applications are routed through the loopback-interface. They leave postrouting to lo and reentering from there prerouting, without passing ingress/egress.
|
|
||||||
This has nothing to do with the "**route_localnet = 1**" trick, which only makes, that the the local ip range 10.0.0.0/8 gets routed!
|
|
||||||
As you can read in many articles, this is nothing you should do, as you may bring your system at risk, because it allows to leak packets from outside to applications, which feel themselves secure, by using those unroutable addresses.
|
|
||||||
#### A Simple Simulation ####
|
|
||||||
You can prove this behaviour with a simple test:
|
|
||||||
```
|
|
||||||
# In one terminal start socat as a local echo server
|
|
||||||
# this is simulating sshd
|
|
||||||
|
|
||||||
socat TCP4-LISTEN:2000,bind=SERVER_IP,fork EXEC:cat
|
|
||||||
|
|
||||||
# In the next terminal start another instance of socat,
|
|
||||||
# simulating sslh
|
|
||||||
|
|
||||||
socat TCP-LISTEN:3000,bind=SERVER_IP,fork TCP:SERVER_IP:2000
|
|
||||||
|
|
||||||
# In another terminal you can watch the traffic on lo
|
|
||||||
|
|
||||||
tcpdump -i lo port 2000
|
|
||||||
|
|
||||||
# In the last terminal talk to the echo server
|
|
||||||
telnet SERVER_IP 2000
|
|
||||||
```
|
|
||||||
You will see your traffic on lo, but not on eth0, if you retry the tcpdump there.
|
|
||||||
|
|
||||||
If you setup sslh as non transparent proxy, it will just work, as what we have seen.
|
|
||||||
|
|
||||||
#### Going Transparent ####
|
|
||||||
|
|
||||||
In case of transparent proxy however, sslh uses some tricks, to reuse the clients IP on its outgoing interface to sshd. It opens the interface in raw mode, so it either needs to be started as root and drop privileges after binding, or you will need to give some capabilities to the sslh binary (cap_net_bind_service,cap_net_raw+ep), if you will start it as restricted user.
|
|
||||||
In this setup we continue, with dropping priviledges.
|
|
||||||
|
|
||||||
Doing so, you can send packets to the sshd, listening on another interface, but, the answer packets from sshd will get routed back to the client. This however will not work, as the client would refuse those packets, because they don't belong to a tcp session, the client opened. In most cases those packets would even not reach the client, as source ip addresses from private address space, will be blocked by most internet routers and connection providers.
|
|
||||||
|
|
||||||
So its mandatory, to use some tricks, to get those packets back to sslh. All configurations, I have seen so far, are using two components for that. They bind sshd to lo, and than they introduce some firewall rules, to mark packets, originating from the sshd port on lo, so that those packets can be routed in a next step -based on that marking- back to sslh.
|
|
||||||
|
|
||||||
##### Drawbacks From Using loopback #####
|
|
||||||
This idea has some serious drawbacks: First, you need to allow routing of the local address space, 127.0.0.0/8, with kernel configurations. Search for the string "net.ipv4.conf.default.route_localnet" and you will find lot of articles, why you should not do this.
|
|
||||||
By allowing this, you need additional firewall rules, dropping martian packets, which otherwise would get routed to the internet from other applications, running on lo, not aware, that their traffic could be routed. You need further firewall rules, blocking incoming packets to loopback addresses, as otherwise some applications (especially udp) could be the goal of some bad traffic.
|
|
||||||
|
|
||||||
##### Using A Dedicated Interface #####
|
|
||||||
So this configuration makes use of a own interface, just for the services, where sslh should hide the traffic for. We use a interface of the dummy kernel module, which was designed just for this case. It is an interface, beeing there, having no cable connection or whatsever, but applications can bind to it. We assign to this interface just a /32 private address, as this interface is not part of any network.
|
|
||||||
|
|
||||||
Doing so, we can avoid all the hassle with marking certain packets, coming from the single applications, sslh has to hide, as we now just route ALL traffic from this specific interface by its ip to sslh.
|
|
||||||
We need one routing rule and one routing table, this covers as many targets sslh will serve on this interface, without adding additional rules for adding apache, openvpn and others.
|
|
||||||
|
|
||||||
We need no firewall rules, preventing martians, as this single routing rule will deadroute all traffic from this interface, if sslh is not catching it up.
|
|
||||||
|
|
||||||
We only need firewall protection for this specific ip address, when we have activated ip forwarding on that system. If the system is no router and needs no forwarding, there is no protection needed.
|
|
||||||
|
|
||||||
## Finally The Configuration ##
|
|
||||||
|
|
||||||
As described, we need as a first step a dedicated interface, just for the services, sslh should hide. Its possible, to generate individual interfaces for different configurations, however, that makes things again more complex and has no advantages seen so far.
|
|
||||||
|
|
||||||
### Named Routing Table ###
|
|
||||||
As we configure the needed routing rules in the interface configuration, we need to define a name for the sslh routing table first.
|
|
||||||
Using named routing tables helps, understanding the routing configuration, as a name indicates, why this routing table is configured.
|
|
||||||
To do so go to _**/etc/iproute2/rt_tables**_ and add a line
|
|
||||||
|
|
||||||
```
|
|
||||||
111 sslh
|
|
||||||
```
|
|
||||||
With newer versions of iproute2 the /etc/iproute2 directory with the embedded templates got no longer installed. The cause maybe, that the example names, which were not used in any configuration, generated confusion. However, once you need those files, generate them and they will be honoured. You still can use just numbers for your routing table. But doing this, and having more than one routing table, you need a list, which numer belongs to which configuration.
|
|
||||||
And seeing in the output from `ip route list table all ` the tables names instead just numbers is worth creating the file.
|
|
||||||
|
|
||||||
### Dummy Interface ###
|
|
||||||
Now we configure our dedicated interface.
|
|
||||||
In the file _**/etc/network/interfaces**_, we place this entry:
|
|
||||||
```
|
|
||||||
auto dummy0
|
|
||||||
iface dummy0 inet static
|
|
||||||
address 192.168.255.254/32
|
|
||||||
pre-up modprobe dummy
|
|
||||||
## Attention! with kernels, not automatically creating a dummy0
|
|
||||||
## interface after module loading the following line should be:
|
|
||||||
## pre-up modprobe dummy; if [ ! -e /sys/class/net/dummy0 ]; then ip link add dummy0 type dummy ; fi
|
|
||||||
post-up ip rule add from 192.168.255.254 table sslh
|
|
||||||
post-up ip route add local 0.0.0.0/0 dev dummy0 table sslh
|
|
||||||
pre-down ip route del local 0.0.0.0/0 dev dummy0 table sslh
|
|
||||||
pre-down ip rule del from 192.168.255.254 table sslh
|
|
||||||
```
|
|
||||||
As long, as your system has no other interfaces with private address-space, or is routing such addresses, you can continue with the given example. Otherwise you need to select a conflict free address.
|
|
||||||
If you are updating a older current configuration, make sure, that you have no longer insecure localnet routing in place:
|
|
||||||
```
|
|
||||||
sysctl net.ipv4.conf.default.route_localnet
|
|
||||||
sysctl net.ipv4.conf.all.route_localnet
|
|
||||||
```
|
|
||||||
should both report "0"!
|
|
||||||
|
|
||||||
|
|
||||||
### Explanation Of The Routing Rules ###
|
|
||||||
The two routing rules in the dummy0 interface configuration are the key for this configuration.
|
|
||||||
|
|
||||||
The first line is an routing rule entry, routing everything coming from the dummy0 ip source address to a special routing table _**sslh**_.
|
|
||||||
|
|
||||||
The next line generates this table implicitly, by inserting a single rule, routing everything from that ip address to dummy0.
|
|
||||||
|
|
||||||
Opposite to other firewall based configurations, we have those rules now tied to the dummy0 device, dedicated to the hidden services.
|
|
||||||
When this interface comes up, the routing rules are making sure, that no martian packets can leave the system, by some processes using this IP address. When the interface goes down, we delete those rules.
|
|
||||||
Also the startup script needs lo longer special treatment for the transparent mode.
|
|
||||||
|
|
||||||
|
|
||||||
#### SSLH Default Configuration ####
|
|
||||||
And finally you need to configute _**/etc/default/sslh**_ with the right settings for all the services, sslh should work for.
|
|
||||||
```
|
|
||||||
DAEMON_OPTS="--user sslh --listen SERVER_IP:443 --transparent \
|
|
||||||
--ssh 192.168.255.254:22 --tls 192.168.255.254:443 \
|
|
||||||
--pidfile /var/run/sslh/sslh.pid"
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Systemd ####
|
|
||||||
This setup is now startup agnostic. As we don't need special treatment in the startup script, the sysV based init scripts will just work, like the systemd scripts. Nothing needs to be modified, when going transparent.
|
|
||||||
|
|
||||||
#### Remote Setups ####
|
|
||||||
This concept can also be adapted for several setups, where the sshd (or any other target service) is running in a container, kvm-virtual machine, etc.
|
|
||||||
Precondition is, that the target system is the next hop and uses the sslh-hosting system as default gateway. In addition you need to bind an additional ip-address, solely used for sshd on the corresponding interface.
|
|
||||||
Than you can adapt the routing rule, routing traffic coming back from this ip to the sslh-routing-table.
|
|
||||||
Its also possible, to forward to an next hop system, which has its own default gateway back, bypassing the sslh-host.
|
|
||||||
In this case, you need to add a special route back to the sslh host, for all traffic with the sshd source ip address. This can be done similar to the two rules described above:
|
|
||||||
```
|
|
||||||
# first define a name for the table in /etc/iproute2/rt_tables e.g. sslh-routeback
|
|
||||||
ip rule add from IPADRESS-OF-SERVIE table sslh-routeback
|
|
||||||
ip route add default via IPADDRESS-OF_SSLH-HOST dev eth0 table sslh-routeback
|
|
||||||
```
|
|
||||||
The details are depending on your network settings. Als long, as the forward chain to the hidden service passes systems under your control, you can add backroutes on each system in that route. Precondition: The used ip address produces no conflict on those systems.
|
|
||||||
|
|
||||||
[I added a second document](./scenarios-for-simple-transparent-proxy.md), describing three possible scenarios in detail. Those three scenarios should cover all setups related to transparent proxying.
|
|
Binary file not shown.
Before Width: | Height: | Size: 233 KiB |
File diff suppressed because it is too large
Load Diff
Before Width: | Height: | Size: 158 KiB |
426
doc/tproxy.md
426
doc/tproxy.md
@ -1,426 +0,0 @@
|
|||||||
# Transparent proxy using packet marking
|
|
||||||
|
|
||||||
Before reading further, make sure you try to set up
|
|
||||||
transparent proxying using [IP routing](doc/simple_transparent_proxy.md).
|
|
||||||
It is conceptually easier to understand, cleaner, and more
|
|
||||||
portable.
|
|
||||||
|
|
||||||
Using this method is very tricky and
|
|
||||||
detail-dependant: depending on whether the target server and
|
|
||||||
sslh are on the same machine, different machines, or
|
|
||||||
different dockers, and tool versions, all seem to change the
|
|
||||||
required network configuration somewhat. If it doesn't work,
|
|
||||||
it's almost certain that the problem is not linked to `sslh`
|
|
||||||
but to the network setup that surrounds it. If in trouble,
|
|
||||||
it might be worth trying to set up the network rules
|
|
||||||
with a simpler server than `sslh`, such as
|
|
||||||
[`socat`](http://www.dest-unreach.org/socat/)
|
|
||||||
|
|
||||||
|
|
||||||
Users have tried to do at least the following:
|
|
||||||
|
|
||||||
* `sslh` and the target servers run on the same host (see [below](#transparent-proxy-to-one-host))
|
|
||||||
* `sslh` runs on a host, and the target servers run on LXC or dockers running on the same host. No known working setup.
|
|
||||||
* `sslh` runs on a host, and the target servers run on different hosts on the same local network(see [below](#transparent-proxy-to-two-hosts))
|
|
||||||
* `sslh` runs on a host, and the target servers run on a different host on a different network (there is a [usecase](https://github.com/yrutschle/sslh/issues/295) for this). No known working setup, and it's unclear it is possible.
|
|
||||||
|
|
||||||
|
|
||||||
## Transparent proxy to one host
|
|
||||||
|
|
||||||
### Linux
|
|
||||||
|
|
||||||
`sslh` needs extended rights to perform this: you'll need to
|
|
||||||
give it `CAP_NET_RAW` capabilities (see appropriate chapter)
|
|
||||||
or run it as root (but don't do that).
|
|
||||||
|
|
||||||
The firewalling tables also need to be adjusted as follows.
|
|
||||||
I don't think it is possible to have `httpd` and `sslh` both listen to 443 in
|
|
||||||
this scheme -- let me know if you manage that:
|
|
||||||
|
|
||||||
# Set route_localnet = 1 on all interfaces so that ssl can use "localhost" as destination
|
|
||||||
sysctl -w net.ipv4.conf.default.route_localnet=1
|
|
||||||
sysctl -w net.ipv4.conf.all.route_localnet=1
|
|
||||||
|
|
||||||
# DROP martian packets as they would have been if route_localnet was zero
|
|
||||||
# Note: packets not leaving the server aren't affected by this, thus sslh will still work
|
|
||||||
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
|
|
||||||
|
|
||||||
# Mark all connections made by ssl for special treatment (here sslh is run as user "sslh")
|
|
||||||
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
|
|
||||||
|
|
||||||
# Outgoing packets that should go to sslh instead have to be rerouted, so mark them accordingly (copying over the connection mark)
|
|
||||||
iptables -t mangle -A OUTPUT ! -o lo -p tcp -m connmark --mark 0x01/0x0f -j CONNMARK --restore-mark --mask 0x0f
|
|
||||||
|
|
||||||
# Configure routing for those marked packets
|
|
||||||
ip rule add fwmark 0x1 lookup 100
|
|
||||||
ip route add local 0.0.0.0/0 dev lo table 100
|
|
||||||
|
|
||||||
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
|
|
||||||
sysctl -w net.ipv4.conf.default.route_localnet=1
|
|
||||||
sysctl -w net.ipv4.conf.all.route_localnet=1
|
|
||||||
|
|
||||||
# DROP martian packets as they would have been if route_localnet was zero
|
|
||||||
# Note: packets not leaving the server aren't affected by this, thus sslh will still work
|
|
||||||
ip6tables -t raw -A PREROUTING ! -i lo -d ::1/128 -j DROP
|
|
||||||
ip6tables -t mangle -A POSTROUTING ! -o lo -s ::1/128 -j DROP
|
|
||||||
|
|
||||||
# Mark all connections made by ssl for special treatment (here sslh is run as user "sslh")
|
|
||||||
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
|
|
||||||
|
|
||||||
# Outgoing packets that should go to sslh instead have to be rerouted, so mark them accordingly (copying over the connection mark)
|
|
||||||
ip6tables -t mangle -A OUTPUT ! -o lo -p tcp -m connmark --mark 0x01/0x0f -j CONNMARK --restore-mark --mask 0x0f
|
|
||||||
|
|
||||||
# Configure routing for those marked packets
|
|
||||||
ip -6 rule add fwmark 0x1 lookup 100
|
|
||||||
ip -6 route add local ::/0 dev lo table 100
|
|
||||||
|
|
||||||
Explanation:
|
|
||||||
To be able to use `localhost` as destination in your sslh config along with transparent proxying
|
|
||||||
you have to allow routing of loopback addresses as done above.
|
|
||||||
This is something you usually should not do (see [this stackoverflow post](https://serverfault.com/questions/656279/how-to-force-linux-to-accept-packet-with-loopback-ip/656484#656484))
|
|
||||||
The two `DROP` iptables rules emulate the behaviour of `route_localnet` set to off (with one small difference:
|
|
||||||
allowing the reroute-check to happen after the fwmark is set on packets destined for sslh).
|
|
||||||
See [this diagram](https://upload.wikimedia.org/wikipedia/commons/3/37/Netfilter-packet-flow.svg) for a good visualisation
|
|
||||||
showing how packets will traverse the iptables chains.
|
|
||||||
|
|
||||||
Note:
|
|
||||||
You have to run `sslh` as dedicated user (in this example the user is also named `sslh`), to not mess up with your normal networking.
|
|
||||||
These rules will allow you to connect directly to ssh on port
|
|
||||||
22 (or to any other service behind sslh) as well as through sslh on port 443.
|
|
||||||
|
|
||||||
Also remember that iptables configuration and ip routes and
|
|
||||||
rules won't be necessarily persisted after you reboot. Make
|
|
||||||
sure to save them properly. For example in CentOS7, you would
|
|
||||||
do `iptables-save > /etc/sysconfig/iptables`, and add both
|
|
||||||
`ip` commands to your `/etc/rc.local`.
|
|
||||||
|
|
||||||
### FreeBSD
|
|
||||||
|
|
||||||
Given you have no firewall defined yet, you can use the following configuration
|
|
||||||
to have ipfw properly redirect traffic back to sslh
|
|
||||||
|
|
||||||
/etc/rc.conf
|
|
||||||
firewall_enable="YES"
|
|
||||||
firewall_type="open"
|
|
||||||
firewall_logif="YES"
|
|
||||||
firewall_coscripts="/etc/ipfw/sslh.rules"
|
|
||||||
|
|
||||||
|
|
||||||
/etc/ipfw/sslh.rules
|
|
||||||
|
|
||||||
#! /bin/sh
|
|
||||||
|
|
||||||
# ssl
|
|
||||||
ipfw add 20000 fwd 192.0.2.1,443 log tcp from 192.0.2.1 8443 to any out
|
|
||||||
ipfw add 20010 fwd 2001:db8::1,443 log tcp from 2001:db8::1 8443 to any out
|
|
||||||
|
|
||||||
# ssh
|
|
||||||
ipfw add 20100 fwd 192.0.2.1,443 log tcp from 192.0.2.1 8022 to any out
|
|
||||||
ipfw add 20110 fwd 2001:db8::1,443 log tcp from 2001:db8::1 8022 to any out
|
|
||||||
|
|
||||||
# xmpp
|
|
||||||
ipfw add 20200 fwd 192.0.2.1,443 log tcp from 192.0.2.1 5222 to any out
|
|
||||||
ipfw add 20210 fwd 2001:db8::1,443 log tcp from 2001:db8::1 5222 to any out
|
|
||||||
|
|
||||||
# openvpn (running on other internal system)
|
|
||||||
ipfw add 20300 fwd 192.0.2.1,443 log tcp from 198.51.100.7 1194 to any out
|
|
||||||
ipfw add 20310 fwd 2001:db8::1,443 log tcp from 2001:db8:1::7 1194 to any out
|
|
||||||
|
|
||||||
General notes:
|
|
||||||
|
|
||||||
|
|
||||||
This will only work if `sslh` does not use any loopback
|
|
||||||
addresses (no `127.0.0.1` or `localhost`), you'll need to use
|
|
||||||
explicit IP addresses (or names):
|
|
||||||
|
|
||||||
sslh --listen 192.168.0.1:443 --ssh 192.168.0.1:22 --tls 192.168.0.1:4443
|
|
||||||
|
|
||||||
This will not work:
|
|
||||||
|
|
||||||
sslh --listen 192.168.0.1:443 --ssh 127.0.0.1:22 --tls 127.0.0.1:4443
|
|
||||||
|
|
||||||
Transparent proxying means the target server sees the real
|
|
||||||
origin address, so it means if the client connects using
|
|
||||||
IPv6, the server must also support IPv6. It is easy to
|
|
||||||
support both IPv4 and IPv6 by configuring the server
|
|
||||||
accordingly, and setting `sslh` to connect to a name that
|
|
||||||
resolves to both IPv4 and IPv6, e.g.:
|
|
||||||
|
|
||||||
sslh --transparent --listen <extaddr>:443 --ssh insideaddr:22
|
|
||||||
|
|
||||||
/etc/hosts:
|
|
||||||
192.168.0.1 insideaddr
|
|
||||||
201::::2 insideaddr
|
|
||||||
|
|
||||||
Upon incoming IPv6 connection, `sslh` will first try to
|
|
||||||
connect to the IPv4 address (which will fail), then connect
|
|
||||||
to the IPv6 address.
|
|
||||||
|
|
||||||
|
|
||||||
## Transparent Proxy to Two Hosts
|
|
||||||
|
|
||||||
Tutorial by Sean Warner. 19 June 2019 20:35
|
|
||||||
|
|
||||||
### Aim
|
|
||||||
|
|
||||||
* Show that `sslh` can transparently proxy requests from the internet to services on two separate hosts that are both on the same LAN.
|
|
||||||
* The IP address of the client initiating the request is what the destination should see… and not the IP address of the host that `sslh` is running on, which is what happens when `sslh` is not running in transparent mode.
|
|
||||||
* The solution here only works for my very specific use-case but hopefully others can adapt it to suits their needs.
|
|
||||||
|
|
||||||
### Overview of my Network
|
|
||||||
|
|
||||||
Two Raspberry Pis on my home LAN:
|
|
||||||
* Pi A: 192.168.1.124 – `sslh` (Port 4433), Apache2 web server for https (port 443), `stunnel` (port 4480) to decrypt ssh traffic and forward to SSH server (also on Pi A at Port 1022)
|
|
||||||
* Pi B: 192.168.1.123 - HTTP server (port 8000), SSH server (port 1022 on PiB).
|
|
||||||
* I send traffic from the internet to my router's external port 443 then use a port forward rule in my router to map that to internal port 4433 where sslh is listening.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### `sslh` build
|
|
||||||
|
|
||||||
`sslh` Version: sslh v1.19c-2-gf451cc8-dirty.
|
|
||||||
|
|
||||||
I compiled sslh from sources giving the binary pretty much all possible options such as Posix capabilities and systemd support.. here are the first few lines of the makefile:
|
|
||||||
|
|
||||||
```
|
|
||||||
# Configuration
|
|
||||||
|
|
||||||
VERSION=$(shell ./genver.sh -r)
|
|
||||||
ENABLE_REGEX=1 # Enable regex probes
|
|
||||||
USELIBCONFIG=1 # Use libconfig? (necessary to use configuration files)
|
|
||||||
USELIBPCRE=1 # Use libpcre? (needed for regex on musl)
|
|
||||||
USELIBWRAP=1 # Use libwrap?
|
|
||||||
USELIBCAP=1 # Use libcap?
|
|
||||||
USESYSTEMD=1 # Make use of systemd socket activation
|
|
||||||
COV_TEST= # Perform test coverage?
|
|
||||||
PREFIX=/usr/local
|
|
||||||
BINDIR=$(PREFIX)/sbin
|
|
||||||
MANDIR=$(PREFIX)/share/man/man8
|
|
||||||
MAN=sslh.8.gz # man page name
|
|
||||||
|
|
||||||
# End of configuration -- the rest should take care of
|
|
||||||
# itself
|
|
||||||
```
|
|
||||||
|
|
||||||
### systemd setup
|
|
||||||
|
|
||||||
Create an sslh systemd service file...
|
|
||||||
```
|
|
||||||
# nano /lib/systemd/system/sslh.service
|
|
||||||
```
|
|
||||||
|
|
||||||
Paste in this contents…
|
|
||||||
|
|
||||||
```
|
|
||||||
[Unit]
|
|
||||||
Description=SSL/SSH multiplexer
|
|
||||||
After=network.target
|
|
||||||
Documentation=man:sslh(8)
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
#EnvironmentFile=/etc/default/sslh
|
|
||||||
#ExecStart=/usr/local/sbin/sslh $DAEMON_OPTS
|
|
||||||
ExecStart=/usr/local/sbin/sslh -F /etc/sslh/sslh.cfg
|
|
||||||
KillMode=process
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
||||||
```
|
|
||||||
|
|
||||||
Save it and then…
|
|
||||||
```
|
|
||||||
# systemctl daemon-reload
|
|
||||||
```
|
|
||||||
|
|
||||||
Start it again to test…
|
|
||||||
```
|
|
||||||
# systemctl start sslh
|
|
||||||
```
|
|
||||||
|
|
||||||
### Configure `sslh`
|
|
||||||
|
|
||||||
First stop `sslh` then open the config file and replace with below, save and start `sslh` again
|
|
||||||
```
|
|
||||||
# systemctl stop sslh
|
|
||||||
# nano /etc/sslh/sslh.cfg
|
|
||||||
# systemctl start sslh
|
|
||||||
```
|
|
||||||
|
|
||||||
```
|
|
||||||
verbose: true;
|
|
||||||
foreground: true;
|
|
||||||
inetd: false;
|
|
||||||
numeric: true;
|
|
||||||
transparent: true;
|
|
||||||
timeout: 2;
|
|
||||||
user: "sslh";
|
|
||||||
pidfile: "/var/run/sslh.pid";
|
|
||||||
chroot: "/var/empty";
|
|
||||||
|
|
||||||
# You must have a port forward rule in the router: external port 443 <-> internal port 4433
|
|
||||||
# Local ip address of PiA is: 192.168.1.124, sslh and stunnel4 are running on this Pi
|
|
||||||
# Local ip address of PiB is: 192.168.1.123, http server and ssh server on this Pi
|
|
||||||
listen:
|
|
||||||
(
|
|
||||||
{ host: "192.168.1.124"; port: "4433"; }
|
|
||||||
);
|
|
||||||
|
|
||||||
# sslh demultiplexes based on the Protocol and Hostname
|
|
||||||
protocols:
|
|
||||||
(
|
|
||||||
{ name: "tls"; sni_hostnames: [ "www.example.com" ]; host: "192.168.1.124"; port: "443"; log_level: 1; },
|
|
||||||
# This probe is for tls encrypted ssh. SSLH forwards it to stunnel on port 4480 which decrypts it and sends it to the ssh server on PiA port 1022
|
|
||||||
{ name: "tls"; sni_hostnames: [ "ssh.example.com" ]; host: "192.168.1.124"; port: "4480"; log_level: 1; },
|
|
||||||
{ name: "http"; host: "192.168.1.123"; port: "8000"; log_level: 1; },
|
|
||||||
{ name: "ssh"; host: "192.168.1.123"; port: "1022"; log_level: 1; }
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
### Configure `stunnel`
|
|
||||||
|
|
||||||
First stop `stunnel` then open the config file and replace with below, save and start `stunnel` again
|
|
||||||
```
|
|
||||||
# systemctl stop stunnel4
|
|
||||||
# nano /etc/stunnel/stunnel.conf
|
|
||||||
# systemctl start stunnel4
|
|
||||||
```
|
|
||||||
|
|
||||||
```
|
|
||||||
# Debugging stuff (may be useful for troubleshooting)
|
|
||||||
foreground = yes
|
|
||||||
#debug = 5 # this is the default
|
|
||||||
debug = 7
|
|
||||||
output = /var/log/stunnel4/stunnel.log
|
|
||||||
pid = /var/run/stunnel4/stunnel.pid
|
|
||||||
fips = no
|
|
||||||
|
|
||||||
cert = /etc/letsencrypt/live/example.com/fullchain.pem
|
|
||||||
key = /etc/letsencrypt/live/example.com/privkey.pem
|
|
||||||
|
|
||||||
[ssh]
|
|
||||||
accept = 192.168.1.124:4480
|
|
||||||
connect = 192.168.1.124:1022
|
|
||||||
TIMEOUTclose = 0
|
|
||||||
```
|
|
||||||
|
|
||||||
### Configure iptables for Pi A
|
|
||||||
|
|
||||||
The `_add.sh` script creates the rules, the `_rm.sh` script removes the rules.
|
|
||||||
They will be lost if you reboot but there are ways to make them load again on start-up..
|
|
||||||
```
|
|
||||||
# nano /usr/local/sbin/piA_tproxy_add.sh
|
|
||||||
```
|
|
||||||
``` piA_tproxy_add.sh
|
|
||||||
iptables -t mangle -N SSLH
|
|
||||||
iptables -t mangle -A PREROUTING -p tcp -m socket --transparent -j SSLH
|
|
||||||
iptables -t mangle -A OUTPUT --protocol tcp --out-interface eth0 -m multiport --sport 443,4480 --jump SSLH
|
|
||||||
iptables -t mangle -A SSLH --jump MARK --set-mark 0x1
|
|
||||||
iptables -t mangle -A SSLH --jump ACCEPT
|
|
||||||
ip rule add fwmark 0x1 lookup 100
|
|
||||||
ip route add local 0.0.0.0/0 dev lo table 100
|
|
||||||
```
|
|
||||||
|
|
||||||
```
|
|
||||||
# nano /usr/local/sbin/piA_tproxy_rm.sh
|
|
||||||
```
|
|
||||||
``` piA_tproxy_rm.sh
|
|
||||||
iptables -t mangle -D PREROUTING -p tcp -m socket --transparent -j SSLH
|
|
||||||
iptables -t mangle -D OUTPUT --protocol tcp --out-interface eth0 -m multiport --sport 443,4480 --jump SSLH
|
|
||||||
iptables -t mangle -D SSLH --jump MARK --set-mark 0x1
|
|
||||||
iptables -t mangle -D SSLH --jump ACCEPT
|
|
||||||
iptables -t mangle -X SSLH
|
|
||||||
ip rule del fwmark 0x1 lookup 100
|
|
||||||
ip route del local 0.0.0.0/0 dev lo table 100
|
|
||||||
```
|
|
||||||
|
|
||||||
Make them executable..
|
|
||||||
```
|
|
||||||
# chmod +rx piA_tproxy_add.sh
|
|
||||||
# chmod +rx piA_tproxy_rm.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
Now run the "add" script on Pi A!
|
|
||||||
```
|
|
||||||
# piA_tproxy_add.sh
|
|
||||||
# piA_tproxy_rm.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configure iptables for Pi B
|
|
||||||
|
|
||||||
```
|
|
||||||
# nano /usr/local/sbin/piB_tproxy_add.sh
|
|
||||||
```
|
|
||||||
``` piB_tproxy_add.sh
|
|
||||||
iptables -t mangle -N SSLHSSL
|
|
||||||
iptables -t mangle -A OUTPUT -o eth0 -p tcp -m multiport --sport 1022,8000 -j SSLHSSL
|
|
||||||
iptables -t mangle -A SSLHSSL --jump MARK --set-mark 0x1
|
|
||||||
iptables -t mangle -A SSLHSSL --jump ACCEPT
|
|
||||||
ip rule add fwmark 0x1 lookup 100
|
|
||||||
ip route add default via 192.168.1.124 table 100
|
|
||||||
ip route flush cache
|
|
||||||
```
|
|
||||||
|
|
||||||
```
|
|
||||||
# nano /usr/local/sbin/piB_tproxy_rm.sh
|
|
||||||
```
|
|
||||||
```
|
|
||||||
iptables -t mangle -D OUTPUT -o eth0 -p tcp -m multiport --sport 1022,8000 -j SSLHSSL
|
|
||||||
iptables -t mangle -D SSLHSSL --jump MARK --set-mark 0x1
|
|
||||||
iptables -t mangle -D SSLHSSL --jump ACCEPT
|
|
||||||
iptables -t mangle -X SSLHSSL
|
|
||||||
ip rule del fwmark 0x1 lookup 100
|
|
||||||
ip route del default via 192.168.1.124 table 100
|
|
||||||
ip route flush cache
|
|
||||||
```
|
|
||||||
|
|
||||||
Make them executable..
|
|
||||||
```
|
|
||||||
# chmod +rx piB_tproxy_add.sh
|
|
||||||
# chmod +rx piB_tproxy_rm.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
Now run the "add" script on Pi B!
|
|
||||||
```
|
|
||||||
# piB_tproxy_add.sh
|
|
||||||
# piB_tproxy_rm.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
### Testing
|
|
||||||
|
|
||||||
* Getting to sshd on PiA
|
|
||||||
|
|
||||||
I did this test using 4G from my phone (outside the LAN)
|
|
||||||
|
|
||||||
To simulate this I use `proxytunnel`. External port 443 is forwarded by my router to 4433. I need to arrive at `sslh` (port 4433) with ssh encrypted as TLS (hence I use the -e switch) and the `sni_hostname` set to ssh.example.com so that `sslh` will demultiplex to `stunnel` (port 4480) which will decrypt and forward to ssh server on PiA… see `sslh.cfg` and `stunnel.conf`.
|
|
||||||
|
|
||||||
The first IP:port is just a free HTTPS proxy I found on https://free-proxy-list.net
|
|
||||||
I execute this command from a terminal window..
|
|
||||||
|
|
||||||
```
|
|
||||||
# proxytunnel -v -e -C root.pem -p 78.141.192.198:8080 -d ssh.example.com:443
|
|
||||||
```
|
|
||||||
* Getting to sshd on PiB
|
|
||||||
|
|
||||||
I did this test using 4G from my phone (outside the LAN)
|
|
||||||
|
|
||||||
My smartphone telecom provider blocks ssh over port 443 so I need to use `proxytunnel` to encrypt.
|
|
||||||
|
|
||||||
Use the Proxytunnel `-X` switch to encrypt from local proxy to destination only so by the time we get to the destination it is unencrypted and `sslh` will see the ssh protocol and demultiplex to PiB as per `sslh.cfg`.
|
|
||||||
|
|
||||||
```
|
|
||||||
# proxytunnel -v -X -C root.pem -p 78.141.192.198:8080 -d ssh.example.com:443
|
|
||||||
```
|
|
||||||
|
|
||||||
Now when you test it all look at the output in daemon.log like this:
|
|
||||||
```
|
|
||||||
# grep -i 'ssl' /var/log/daemon.log
|
|
||||||
```
|
|
||||||
You should see that the IP address and port from the “connection from” and “forwarded from” fields are the same.
|
|
||||||
|
|
||||||
Special thanks and appreciation to Michael Yelsukov without whom I would never have got this working.
|
|
||||||
|
|
||||||
Any feedback or corrections very welcome!
|
|
635
doc/tproxy.svg
635
doc/tproxy.svg
@ -1,635 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
||||||
|
|
||||||
<svg
|
|
||||||
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
width="297mm"
|
|
||||||
height="210mm"
|
|
||||||
viewBox="0 0 297 210"
|
|
||||||
version="1.1"
|
|
||||||
id="svg288"
|
|
||||||
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
|
|
||||||
sodipodi:docname="Transparent Proxy to Two Hosts-new.svg">
|
|
||||||
<defs
|
|
||||||
id="defs282">
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient4840"
|
|
||||||
osb:paint="solid">
|
|
||||||
<stop
|
|
||||||
style="stop-color:#000000;stop-opacity:1;"
|
|
||||||
offset="0"
|
|
||||||
id="stop4838" />
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient4802"
|
|
||||||
osb:paint="solid">
|
|
||||||
<stop
|
|
||||||
style="stop-color:#000000;stop-opacity:1;"
|
|
||||||
offset="0"
|
|
||||||
id="stop4800" />
|
|
||||||
</linearGradient>
|
|
||||||
</defs>
|
|
||||||
<sodipodi:namedview
|
|
||||||
id="base"
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#666666"
|
|
||||||
borderopacity="1.0"
|
|
||||||
inkscape:pageopacity="0.0"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:zoom="1.01"
|
|
||||||
inkscape:cx="511.12411"
|
|
||||||
inkscape:cy="388.13656"
|
|
||||||
inkscape:document-units="mm"
|
|
||||||
inkscape:current-layer="layer1"
|
|
||||||
showgrid="false"
|
|
||||||
inkscape:snap-page="true"
|
|
||||||
inkscape:window-width="1920"
|
|
||||||
inkscape:window-height="1005"
|
|
||||||
inkscape:window-x="-9"
|
|
||||||
inkscape:window-y="-9"
|
|
||||||
inkscape:window-maximized="1"
|
|
||||||
showguides="false" />
|
|
||||||
<metadata
|
|
||||||
id="metadata285">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title />
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<g
|
|
||||||
inkscape:label="Layer 1"
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
id="layer1"
|
|
||||||
transform="translate(0,-87)">
|
|
||||||
<rect
|
|
||||||
style="fill:#000000;fill-opacity:0;stroke:#fa9600;stroke-width:0.68479234;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
|
||||||
id="rect4977-0"
|
|
||||||
width="37.333149"
|
|
||||||
height="17.888765"
|
|
||||||
x="234.24397"
|
|
||||||
y="232.81883" />
|
|
||||||
<ellipse
|
|
||||||
id="path290"
|
|
||||||
cx="38.405174"
|
|
||||||
cy="133.55173"
|
|
||||||
rx="21.853449"
|
|
||||||
ry="28.965515"
|
|
||||||
style="stroke-width:0.26458332;fill-opacity:1;fill:none" />
|
|
||||||
<ellipse
|
|
||||||
style="fill:none;stroke-width:0.26458332"
|
|
||||||
id="path4806"
|
|
||||||
cx="44.482758"
|
|
||||||
cy="135.49138"
|
|
||||||
rx="28.965515"
|
|
||||||
ry="27.025862" />
|
|
||||||
<ellipse
|
|
||||||
style="fill:none;stroke-width:0.26458332"
|
|
||||||
id="path4808"
|
|
||||||
cx="124.26724"
|
|
||||||
cy="151.26724"
|
|
||||||
rx="29.612068"
|
|
||||||
ry="24.698277" />
|
|
||||||
<ellipse
|
|
||||||
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.76499999;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
id="path4812"
|
|
||||||
ry="30.796005"
|
|
||||||
rx="24.16864"
|
|
||||||
cy="140.54999"
|
|
||||||
cx="43.31974" />
|
|
||||||
<rect
|
|
||||||
style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:0.59869254;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
id="rect4852"
|
|
||||||
width="27.302826"
|
|
||||||
height="13.554869"
|
|
||||||
x="29.797318"
|
|
||||||
y="133.28833" />
|
|
||||||
<text
|
|
||||||
xml:space="preserve"
|
|
||||||
style="font-style:normal;font-weight:normal;font-size:6.6431241px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.31139645"
|
|
||||||
x="38.145908"
|
|
||||||
y="130.0871"
|
|
||||||
id="text138"
|
|
||||||
transform="scale(0.9172844,1.0901744)"><tspan
|
|
||||||
sodipodi:role="line"
|
|
||||||
x="38.145908"
|
|
||||||
y="130.0871"
|
|
||||||
id="tspan136"
|
|
||||||
style="stroke-width:0.31139645"><tspan
|
|
||||||
x="38.145908"
|
|
||||||
y="130.0871"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93888903px;font-family:'Courier New';-inkscape-font-specification:'Courier New';stroke-width:0.31139645"
|
|
||||||
id="tspan134">Client</tspan></tspan></text>
|
|
||||||
<rect
|
|
||||||
style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:0.66499996;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
id="rect4862"
|
|
||||||
width="20.232847"
|
|
||||||
height="37.612331"
|
|
||||||
x="101.42361"
|
|
||||||
y="121.64869" />
|
|
||||||
<rect
|
|
||||||
style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:0.67072004;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
id="rect4864"
|
|
||||||
width="121.91585"
|
|
||||||
height="91.301468"
|
|
||||||
x="156.41544"
|
|
||||||
y="116.72592" />
|
|
||||||
<rect
|
|
||||||
style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:0.66499996;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
id="rect4866"
|
|
||||||
width="122.95342"
|
|
||||||
height="37.871742"
|
|
||||||
x="155.89668"
|
|
||||||
y="223.04117" />
|
|
||||||
<rect
|
|
||||||
style="fill:#000000;fill-opacity:0;stroke:#f40000;stroke-width:0.701;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
id="rect4868"
|
|
||||||
width="82.747147"
|
|
||||||
height="23.645355"
|
|
||||||
x="175.0919"
|
|
||||||
y="128.09322" />
|
|
||||||
<rect
|
|
||||||
style="fill:#000000;fill-opacity:0;stroke:#009600;stroke-width:0.66499996;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
id="rect4870-7"
|
|
||||||
width="26.977121"
|
|
||||||
height="14.785542"
|
|
||||||
x="220.4861"
|
|
||||||
y="185.84906" />
|
|
||||||
<rect
|
|
||||||
style="fill:#000000;fill-opacity:0;stroke:#009600;stroke-width:0.66499996;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
id="rect4870-7-1"
|
|
||||||
width="26.977123"
|
|
||||||
height="14.785542"
|
|
||||||
x="184.17078"
|
|
||||||
y="185.84906" />
|
|
||||||
<rect
|
|
||||||
style="fill:#000000;fill-opacity:0;stroke:#009600;stroke-width:0.66499996;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
id="rect4919"
|
|
||||||
width="27.495911"
|
|
||||||
height="11.413391"
|
|
||||||
x="219.96732"
|
|
||||||
y="164.44894" />
|
|
||||||
<rect
|
|
||||||
style="fill:none;fill-opacity:0;stroke:#800080;stroke-width:0.85740757;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.96517412"
|
|
||||||
id="rect4921"
|
|
||||||
width="139.8217"
|
|
||||||
height="161.06387"
|
|
||||||
x="148.11099"
|
|
||||||
y="108.15634" />
|
|
||||||
<path
|
|
||||||
style="fill:#000000;stroke:#c98f26;stroke-width:0.665;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:0.88627452;stroke-miterlimit:4;stroke-dasharray:none;fill-opacity:0"
|
|
||||||
d="m 57.326389,140.58456 c 43.837821,-0.25939 44.097221,-0.25939 44.097221,-0.25939"
|
|
||||||
id="path4925"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
<path
|
|
||||||
style="fill:#000000;fill-opacity:0;stroke:#c98f26;stroke-width:0.665;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.88627451"
|
|
||||||
d="m 122.01937,140.18462 c 53.1252,-0.25845 53.43955,-0.25845 53.43955,-0.25845"
|
|
||||||
id="path4925-7"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
<path
|
|
||||||
style="fill:#000000;fill-opacity:0;stroke:#c98f26;stroke-width:0.58399999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:1.75200005, 0.58400002;stroke-dashoffset:0;stroke-opacity:0.88627451"
|
|
||||||
d="m 101.35344,140.71552 c 19.77892,-0.26191 19.89596,-0.26191 19.89596,-0.26191"
|
|
||||||
id="path4925-4"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
<path
|
|
||||||
style="fill:none;stroke:#009600;stroke-width:0.665;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
|
|
||||||
d="m 198.1781,185.71937 c -0.25939,-33.9808 -0.25939,-33.9808 -0.25939,-33.9808"
|
|
||||||
id="path4971"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
<path
|
|
||||||
style="fill:none;stroke:#009600;stroke-width:0.665;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
|
|
||||||
d="m 233.45588,164.18955 c 0,-12.45098 0,-12.45098 0,-12.45098"
|
|
||||||
id="path4973"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
<path
|
|
||||||
style="fill:none;stroke:#009600;stroke-width:0.665;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
|
|
||||||
d="m 233.19648,185.45997 c 0,-9.59763 0,-9.59763 0,-9.59763"
|
|
||||||
id="path4975"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
<rect
|
|
||||||
style="fill:none;fill-opacity:0;stroke:#fa9600;stroke-width:0.665;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
|
||||||
id="rect4977"
|
|
||||||
width="37.35294"
|
|
||||||
height="16.860703"
|
|
||||||
x="161.2299"
|
|
||||||
y="233.1991" />
|
|
||||||
<path
|
|
||||||
style="fill:none;stroke:#fa9600;stroke-width:0.665;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:0.60784316;stroke-miterlimit:4;stroke-dasharray:none"
|
|
||||||
d="m 178.79609,151.73857 c 0.25941,81.45016 0.25941,81.45016 0.25941,81.45016"
|
|
||||||
id="path4994"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
<path
|
|
||||||
style="fill:none;stroke:#fa9600;stroke-width:0.66078299;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.60784316"
|
|
||||||
d="m 253.45564,151.97084 c 0.25959,80.36694 0.25959,80.36694 0.25959,80.36694"
|
|
||||||
id="path4994-0"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
<text
|
|
||||||
xml:space="preserve"
|
|
||||||
style="font-style:normal;font-weight:normal;font-size:10.58333302px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
|
|
||||||
x="32.424427"
|
|
||||||
y="103.23161"
|
|
||||||
id="text5013"><tspan
|
|
||||||
sodipodi:role="line"
|
|
||||||
id="tspan5011"
|
|
||||||
x="32.424427"
|
|
||||||
y="103.23161"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:5.29166698px;line-height:0;font-family:'Courier New';-inkscape-font-specification:'Courier New';stroke-width:0.26458332">Internet</tspan></text>
|
|
||||||
<text
|
|
||||||
xml:space="preserve"
|
|
||||||
style="font-style:normal;font-weight:normal;font-size:10.58333302px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
|
|
||||||
x="102.3172"
|
|
||||||
y="116.44047"
|
|
||||||
id="text5013-6"><tspan
|
|
||||||
sodipodi:role="line"
|
|
||||||
id="tspan5011-2"
|
|
||||||
x="102.3172"
|
|
||||||
y="116.44047"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:5.29166698px;line-height:0;font-family:'Courier New';-inkscape-font-specification:'Courier New';stroke-width:0.26458332">Router</tspan></text>
|
|
||||||
<text
|
|
||||||
xml:space="preserve"
|
|
||||||
style="font-style:normal;font-weight:normal;font-size:10.58333302px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#800080;fill-opacity:0.96470588;stroke:none;stroke-width:0.26458332"
|
|
||||||
x="188.76727"
|
|
||||||
y="105.30679"
|
|
||||||
id="text5053"><tspan
|
|
||||||
sodipodi:role="line"
|
|
||||||
id="tspan5051"
|
|
||||||
x="188.76727"
|
|
||||||
y="105.30679"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93888903px;font-family:'Courier New';-inkscape-font-specification:'Courier New';fill:#800080;fill-opacity:0.96470588;stroke:none;stroke-width:0.26458332;stroke-opacity:0.96517412">Local Area Network</tspan></text>
|
|
||||||
<text
|
|
||||||
xml:space="preserve"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10.58333302px;line-height:1.25;font-family:'Courier New';-inkscape-font-specification:'Courier New';letter-spacing:0px;word-spacing:0px;fill:#c98f26;fill-opacity:0.88627451;stroke:none;stroke-width:0.26458332"
|
|
||||||
x="92.344772"
|
|
||||||
y="138.5094"
|
|
||||||
id="text5057"><tspan
|
|
||||||
sodipodi:role="line"
|
|
||||||
id="tspan5055"
|
|
||||||
x="92.344772"
|
|
||||||
y="138.5094"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.23333311px;font-family:'Courier New';-inkscape-font-specification:'Courier New';fill:#c98f26;fill-opacity:0.88627451;stroke-width:0.26458332">443</tspan></text>
|
|
||||||
<text
|
|
||||||
xml:space="preserve"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10.58333302px;line-height:1.25;font-family:'Courier New';-inkscape-font-specification:'Courier New';letter-spacing:0px;word-spacing:0px;fill:#c98f26;fill-opacity:0.88627451;stroke:none;stroke-width:0.26458332"
|
|
||||||
x="92.414749"
|
|
||||||
y="138.5529"
|
|
||||||
id="text5057-0"><tspan
|
|
||||||
sodipodi:role="line"
|
|
||||||
id="tspan5055-6"
|
|
||||||
x="92.414749"
|
|
||||||
y="138.5529"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.23333311px;font-family:'Courier New';-inkscape-font-specification:'Courier New';fill:#c98f26;fill-opacity:0.88627451;stroke-width:0.26458332">443</tspan></text>
|
|
||||||
<text
|
|
||||||
xml:space="preserve"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10.58333302px;line-height:1.25;font-family:'Courier New';-inkscape-font-specification:'Courier New';letter-spacing:0px;word-spacing:0px;fill:#c98f26;fill-opacity:0.88627451;stroke:none;stroke-width:0.26458332"
|
|
||||||
x="92.414749"
|
|
||||||
y="138.5529"
|
|
||||||
id="text5057-0-8"><tspan
|
|
||||||
sodipodi:role="line"
|
|
||||||
id="tspan5055-6-2"
|
|
||||||
x="92.414749"
|
|
||||||
y="138.5529"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.23333333px;font-family:'Courier New';-inkscape-font-specification:'Courier New';fill:#c98f26;fill-opacity:0.88627451;stroke-width:0.26458332">443</tspan></text>
|
|
||||||
<text
|
|
||||||
xml:space="preserve"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.3396225px;line-height:1.25;font-family:'Courier New';-inkscape-font-specification:'Courier New';letter-spacing:0px;word-spacing:0px;fill:#c98f26;fill-opacity:0.88627451;stroke:none;stroke-width:0.28349054"
|
|
||||||
x="151.81532"
|
|
||||||
y="147.89809"
|
|
||||||
id="text5057-0-8-6"
|
|
||||||
transform="scale(1.0714604,0.93330561)"><tspan
|
|
||||||
sodipodi:role="line"
|
|
||||||
id="tspan5055-6-2-7"
|
|
||||||
x="151.81532"
|
|
||||||
y="147.89809"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.23333333px;font-family:'Courier New';-inkscape-font-specification:'Courier New';fill:#c98f26;fill-opacity:0.88627451;stroke-width:0.28349054">4433</tspan></text>
|
|
||||||
<text
|
|
||||||
xml:space="preserve"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.33962345px;line-height:1.25;font-family:'Courier New';-inkscape-font-specification:'Courier New';letter-spacing:0px;word-spacing:0px;fill:#c98f26;fill-opacity:0.88627451;stroke:none;stroke-width:0.28349057"
|
|
||||||
x="114.9891"
|
|
||||||
y="148.08023"
|
|
||||||
id="text5057-0-8-6-4"
|
|
||||||
transform="scale(1.0714604,0.93330561)"><tspan
|
|
||||||
sodipodi:role="line"
|
|
||||||
id="tspan5055-6-2-7-3"
|
|
||||||
x="114.9891"
|
|
||||||
y="148.08023"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.23333311px;font-family:'Courier New';-inkscape-font-specification:'Courier New';fill:#c98f26;fill-opacity:0.88627451;stroke-width:0.28349057">4433</tspan></text>
|
|
||||||
<text
|
|
||||||
xml:space="preserve"
|
|
||||||
style="font-style:normal;font-weight:normal;font-size:10.58333302px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
|
|
||||||
x="208.55414"
|
|
||||||
y="135.19525"
|
|
||||||
id="text144"><tspan
|
|
||||||
sodipodi:role="line"
|
|
||||||
x="208.55414"
|
|
||||||
y="135.19525"
|
|
||||||
id="tspan142"
|
|
||||||
style="stroke-width:0.26458332"><tspan
|
|
||||||
x="208.55414"
|
|
||||||
y="135.19525"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:5.64444447px;font-family:'Courier New';-inkscape-font-specification:'Courier New';fill:#f40000;fill-opacity:1;stroke-width:0.26458332"
|
|
||||||
id="tspan140">SSLH</tspan></tspan></text>
|
|
||||||
<text
|
|
||||||
xml:space="preserve"
|
|
||||||
style="font-style:normal;font-weight:normal;font-size:3.52777767px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
|
|
||||||
x="158.75"
|
|
||||||
y="121.48432"
|
|
||||||
id="text5159"><tspan
|
|
||||||
sodipodi:role="line"
|
|
||||||
id="tspan5157"
|
|
||||||
x="158.75"
|
|
||||||
y="121.48432"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:'Courier New';-inkscape-font-specification:'Courier New Bold';stroke-width:0.26458332;font-size:3.88055556px">192.168.1.124 (Pi A)</tspan></text>
|
|
||||||
<text
|
|
||||||
xml:space="preserve"
|
|
||||||
style="font-style:normal;font-weight:normal;font-size:3.52777767px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
|
|
||||||
x="157.30107"
|
|
||||||
y="258.37653"
|
|
||||||
id="text5159-0"><tspan
|
|
||||||
sodipodi:role="line"
|
|
||||||
id="tspan5157-1"
|
|
||||||
x="157.30107"
|
|
||||||
y="258.37653"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88055563px;font-family:'Courier New';-inkscape-font-specification:'Courier New Bold';stroke-width:0.26458332">192.168.1.123 (Pi B)</tspan></text>
|
|
||||||
<text
|
|
||||||
xml:space="preserve"
|
|
||||||
style="font-style:normal;font-weight:normal;font-size:3.52777767px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#fa9600;fill-opacity:1;stroke:none;stroke-width:0.26458332"
|
|
||||||
x="167.25511"
|
|
||||||
y="242.15076"
|
|
||||||
id="text168"><tspan
|
|
||||||
sodipodi:role="line"
|
|
||||||
x="167.25511"
|
|
||||||
y="242.15076"
|
|
||||||
id="tspan166"
|
|
||||||
style="stroke-width:0.26458332"><tspan
|
|
||||||
x="167.25511"
|
|
||||||
y="242.15076"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.23333311px;font-family:'Courier New';-inkscape-font-specification:'Courier New Bold';fill:#fa9600;fill-opacity:1;stroke-width:0.26458332"
|
|
||||||
id="tspan164">Web Server</tspan></tspan></text>
|
|
||||||
<text
|
|
||||||
xml:space="preserve"
|
|
||||||
style="font-style:normal;font-weight:normal;font-size:3.52777767px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#fa9600;fill-opacity:1;stroke:none;stroke-width:0.26458332"
|
|
||||||
x="239.92085"
|
|
||||||
y="241.98419"
|
|
||||||
id="text174"><tspan
|
|
||||||
sodipodi:role="line"
|
|
||||||
x="239.92085"
|
|
||||||
y="241.98419"
|
|
||||||
id="tspan172"
|
|
||||||
style="stroke-width:0.26458332"><tspan
|
|
||||||
x="239.92085"
|
|
||||||
y="241.98419"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.23333311px;font-family:'Courier New';-inkscape-font-specification:'Courier New Bold';fill:#fa9600;fill-opacity:1;stroke-width:0.26458332"
|
|
||||||
id="tspan170">SSH Server</tspan></tspan></text>
|
|
||||||
<text
|
|
||||||
xml:space="preserve"
|
|
||||||
style="font-style:normal;font-weight:normal;font-size:3.52777767px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
|
|
||||||
x="167.9187"
|
|
||||||
y="190.6398"
|
|
||||||
id="text186"><tspan
|
|
||||||
sodipodi:role="line"
|
|
||||||
x="167.9187"
|
|
||||||
y="190.6398"
|
|
||||||
id="tspan184"
|
|
||||||
style="stroke-width:0.26458332"><tspan
|
|
||||||
x="167.9187"
|
|
||||||
y="190.6398"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88055563px;font-family:'Courier New';-inkscape-font-specification:'Courier New Bold';fill:#fa9600;fill-opacity:1;stroke-width:0.26458332"
|
|
||||||
id="tspan182">http</tspan></tspan></text>
|
|
||||||
<text
|
|
||||||
xml:space="preserve"
|
|
||||||
style="font-style:normal;font-weight:normal;font-size:3.52777767px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
|
|
||||||
x="167.48399"
|
|
||||||
y="157.93468"
|
|
||||||
id="text180"><tspan
|
|
||||||
sodipodi:role="line"
|
|
||||||
x="167.48399"
|
|
||||||
y="157.93468"
|
|
||||||
id="tspan178"
|
|
||||||
style="stroke-width:0.26458332"><tspan
|
|
||||||
x="167.48399"
|
|
||||||
y="157.93468"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88055563px;font-family:'Courier New';-inkscape-font-specification:'Courier New Bold';fill:#fa9600;fill-opacity:1;stroke-width:0.26458332"
|
|
||||||
id="tspan176">8000</tspan></tspan></text>
|
|
||||||
<text
|
|
||||||
xml:space="preserve"
|
|
||||||
style="font-style:normal;font-weight:normal;font-size:3.52777767px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
|
|
||||||
x="167.74596"
|
|
||||||
y="229.97469"
|
|
||||||
id="text192"><tspan
|
|
||||||
sodipodi:role="line"
|
|
||||||
x="167.74596"
|
|
||||||
y="229.97469"
|
|
||||||
id="tspan190"
|
|
||||||
style="stroke-width:0.26458332"><tspan
|
|
||||||
x="167.74596"
|
|
||||||
y="229.97469"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88055563px;font-family:'Courier New';-inkscape-font-specification:'Courier New Bold';fill:#fa9600;fill-opacity:1;stroke-width:0.26458332"
|
|
||||||
id="tspan188">8000</tspan></tspan></text>
|
|
||||||
<text
|
|
||||||
xml:space="preserve"
|
|
||||||
style="font-style:normal;font-weight:normal;font-size:3.52777767px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
|
|
||||||
x="255.3237"
|
|
||||||
y="190.91431"
|
|
||||||
id="text246"><tspan
|
|
||||||
sodipodi:role="line"
|
|
||||||
x="255.3237"
|
|
||||||
y="190.91431"
|
|
||||||
id="tspan244"
|
|
||||||
style="stroke-width:0.26458332"><tspan
|
|
||||||
x="255.3237"
|
|
||||||
y="190.91431"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88055563px;font-family:'Courier New';-inkscape-font-specification:'Courier New Bold';fill:#fa9600;fill-opacity:1;stroke-width:0.26458332"
|
|
||||||
id="tspan242">ssh</tspan></tspan></text>
|
|
||||||
<text
|
|
||||||
xml:space="preserve"
|
|
||||||
style="font-style:normal;font-weight:normal;font-size:3.52777767px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
|
|
||||||
x="254.88899"
|
|
||||||
y="158.20918"
|
|
||||||
id="text240"><tspan
|
|
||||||
sodipodi:role="line"
|
|
||||||
x="254.88899"
|
|
||||||
y="158.20918"
|
|
||||||
id="tspan238"
|
|
||||||
style="stroke-width:0.26458332"><tspan
|
|
||||||
x="254.88899"
|
|
||||||
y="158.20918"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88055563px;font-family:'Courier New';-inkscape-font-specification:'Courier New Bold';fill:#fa9600;fill-opacity:1;stroke-width:0.26458332"
|
|
||||||
id="tspan236">1022</tspan></tspan></text>
|
|
||||||
<text
|
|
||||||
xml:space="preserve"
|
|
||||||
style="font-style:normal;font-weight:normal;font-size:3.52777767px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
|
|
||||||
x="255.15096"
|
|
||||||
y="230.24921"
|
|
||||||
id="text252"><tspan
|
|
||||||
sodipodi:role="line"
|
|
||||||
x="255.15096"
|
|
||||||
y="230.24921"
|
|
||||||
id="tspan250"
|
|
||||||
style="stroke-width:0.26458332"><tspan
|
|
||||||
x="255.15096"
|
|
||||||
y="230.24921"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88055563px;font-family:'Courier New';-inkscape-font-specification:'Courier New Bold';fill:#fa9600;fill-opacity:1;stroke-width:0.26458332"
|
|
||||||
id="tspan248">1022</tspan></tspan></text>
|
|
||||||
<text
|
|
||||||
xml:space="preserve"
|
|
||||||
style="font-style:normal;font-weight:normal;font-size:3.52777767px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#009600;fill-opacity:1;stroke:none;stroke-width:0.26458332;stroke-opacity:1"
|
|
||||||
x="186.26637"
|
|
||||||
y="193.70544"
|
|
||||||
id="text156"><tspan
|
|
||||||
sodipodi:role="line"
|
|
||||||
x="186.26637"
|
|
||||||
y="193.70544"
|
|
||||||
id="tspan154"
|
|
||||||
style="stroke-width:0.26458332"><tspan
|
|
||||||
x="186.26637"
|
|
||||||
y="193.70544"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88055563px;font-family:'Courier New';-inkscape-font-specification:'Courier New Bold';fill:#009600;fill-opacity:1;stroke:none;stroke-width:0.26458332;stroke-opacity:1"
|
|
||||||
id="tspan152">Web Server</tspan></tspan></text>
|
|
||||||
<text
|
|
||||||
xml:space="preserve"
|
|
||||||
style="font-style:normal;font-weight:normal;font-size:3.52777767px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#009600;fill-opacity:1;stroke:none;stroke-width:0.26458332;stroke-opacity:1"
|
|
||||||
x="225.25404"
|
|
||||||
y="170.88316"
|
|
||||||
id="text150"><tspan
|
|
||||||
sodipodi:role="line"
|
|
||||||
x="225.25404"
|
|
||||||
y="170.88316"
|
|
||||||
id="tspan148"
|
|
||||||
style="stroke-width:0.26458332"><tspan
|
|
||||||
x="225.25404"
|
|
||||||
y="170.88316"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88055563px;font-family:'Courier New';-inkscape-font-specification:'Courier New Bold';fill:#009600;fill-opacity:1;stroke:none;stroke-width:0.26458332;stroke-opacity:1"
|
|
||||||
id="tspan146">STUNNEL</tspan></tspan></text>
|
|
||||||
<text
|
|
||||||
xml:space="preserve"
|
|
||||||
style="font-style:normal;font-weight:normal;font-size:3.52777767px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#009600;fill-opacity:1;stroke:none;stroke-width:0.26458332;stroke-opacity:1"
|
|
||||||
x="222.6344"
|
|
||||||
y="194.19792"
|
|
||||||
id="text162"><tspan
|
|
||||||
sodipodi:role="line"
|
|
||||||
x="222.6344"
|
|
||||||
y="194.19792"
|
|
||||||
id="tspan160"
|
|
||||||
style="stroke-width:0.26458332"><tspan
|
|
||||||
x="222.6344"
|
|
||||||
y="194.19792"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88055563px;font-family:'Courier New';-inkscape-font-specification:'Courier New Bold';fill:#009600;fill-opacity:1;stroke:none;stroke-width:0.26458332;stroke-opacity:1"
|
|
||||||
id="tspan158">SSH Server</tspan></tspan></text>
|
|
||||||
<text
|
|
||||||
xml:space="preserve"
|
|
||||||
style="font-style:normal;font-weight:normal;font-size:3.52777767px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#009600;fill-opacity:1;stroke:none;stroke-width:0.26458332"
|
|
||||||
x="199.15128"
|
|
||||||
y="157.29231"
|
|
||||||
id="text198"><tspan
|
|
||||||
sodipodi:role="line"
|
|
||||||
x="199.15128"
|
|
||||||
y="157.29231"
|
|
||||||
id="tspan196"
|
|
||||||
style="stroke-width:0.26458332"><tspan
|
|
||||||
x="199.15128"
|
|
||||||
y="157.29231"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88055563px;font-family:'Courier New';-inkscape-font-specification:'Courier New Bold';fill:#009600;fill-opacity:1;stroke-width:0.26458332"
|
|
||||||
id="tspan194">443</tspan></tspan></text>
|
|
||||||
<text
|
|
||||||
xml:space="preserve"
|
|
||||||
style="font-style:normal;font-weight:normal;font-size:3.52777767px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#009600;fill-opacity:1;stroke:none;stroke-width:0.26458332"
|
|
||||||
x="199.41324"
|
|
||||||
y="182.17886"
|
|
||||||
id="text210"><tspan
|
|
||||||
sodipodi:role="line"
|
|
||||||
x="199.41324"
|
|
||||||
y="182.17886"
|
|
||||||
id="tspan208"
|
|
||||||
style="stroke-width:0.26458332"><tspan
|
|
||||||
x="199.41324"
|
|
||||||
y="182.17886"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88055563px;font-family:'Courier New';-inkscape-font-specification:'Courier New Bold';fill:#009600;fill-opacity:1;stroke-width:0.26458332"
|
|
||||||
id="tspan206">443</tspan></tspan></text>
|
|
||||||
<text
|
|
||||||
xml:space="preserve"
|
|
||||||
style="font-style:normal;font-weight:normal;font-size:3.52777767px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#009600;fill-opacity:1;stroke:none;stroke-width:0.26458332"
|
|
||||||
x="199.15128"
|
|
||||||
y="170.65247"
|
|
||||||
id="text204"><tspan
|
|
||||||
sodipodi:role="line"
|
|
||||||
x="199.15128"
|
|
||||||
y="170.65247"
|
|
||||||
id="tspan202"
|
|
||||||
style="stroke-width:0.26458332"><tspan
|
|
||||||
x="199.15128"
|
|
||||||
y="170.65247"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88055563px;font-family:'Courier New';-inkscape-font-specification:'Courier New Bold';fill:#009600;fill-opacity:1;stroke-width:0.26458332"
|
|
||||||
id="tspan200">tls</tspan></tspan></text>
|
|
||||||
<text
|
|
||||||
xml:space="preserve"
|
|
||||||
style="font-style:normal;font-weight:normal;font-size:3.52777767px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#009600;fill-opacity:1;stroke:none;stroke-width:0.26458332"
|
|
||||||
x="223.09605"
|
|
||||||
y="158.60213"
|
|
||||||
id="text216"><tspan
|
|
||||||
sodipodi:role="line"
|
|
||||||
x="223.09605"
|
|
||||||
y="158.60213"
|
|
||||||
id="tspan214"
|
|
||||||
style="stroke-width:0.26458332"><tspan
|
|
||||||
x="223.09605"
|
|
||||||
y="158.60213"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88055563px;font-family:'Courier New';-inkscape-font-specification:'Courier New Bold';fill:#009600;fill-opacity:1;stroke-width:0.26458332"
|
|
||||||
id="tspan212">4480</tspan></tspan></text>
|
|
||||||
<text
|
|
||||||
xml:space="preserve"
|
|
||||||
style="font-style:normal;font-weight:normal;font-size:3.52777767px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#009600;fill-opacity:1;stroke:none;stroke-width:0.26458332"
|
|
||||||
x="235.14638"
|
|
||||||
y="158.60213"
|
|
||||||
id="text222"><tspan
|
|
||||||
sodipodi:role="line"
|
|
||||||
x="235.14638"
|
|
||||||
y="158.60213"
|
|
||||||
id="tspan220"
|
|
||||||
style="stroke-width:0.26458332"><tspan
|
|
||||||
x="235.14638"
|
|
||||||
y="158.60213"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88055563px;font-family:'Courier New';-inkscape-font-specification:'Courier New Bold';fill:#009600;fill-opacity:1;stroke-width:0.26458332"
|
|
||||||
id="tspan218">tls</tspan></tspan></text>
|
|
||||||
<text
|
|
||||||
xml:space="preserve"
|
|
||||||
style="font-style:normal;font-weight:normal;font-size:3.52777767px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#009600;fill-opacity:1;stroke:none;stroke-width:0.26458332"
|
|
||||||
x="222.57213"
|
|
||||||
y="182.44083"
|
|
||||||
id="text228"><tspan
|
|
||||||
sodipodi:role="line"
|
|
||||||
x="222.57213"
|
|
||||||
y="182.44083"
|
|
||||||
id="tspan226"
|
|
||||||
style="stroke-width:0.26458332"><tspan
|
|
||||||
x="222.57213"
|
|
||||||
y="182.44083"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88055563px;font-family:'Courier New';-inkscape-font-specification:'Courier New Bold';fill:#009600;fill-opacity:1;stroke-width:0.26458332"
|
|
||||||
id="tspan224">1022</tspan></tspan></text>
|
|
||||||
<text
|
|
||||||
xml:space="preserve"
|
|
||||||
style="font-style:normal;font-weight:normal;font-size:3.52777767px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#009600;fill-opacity:1;stroke:none;stroke-width:0.26458332"
|
|
||||||
x="234.88441"
|
|
||||||
y="182.44083"
|
|
||||||
id="text234"><tspan
|
|
||||||
sodipodi:role="line"
|
|
||||||
x="234.88441"
|
|
||||||
y="182.44083"
|
|
||||||
id="tspan232"
|
|
||||||
style="stroke-width:0.26458332"><tspan
|
|
||||||
x="234.88441"
|
|
||||||
y="182.44083"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88055563px;font-family:'Courier New';-inkscape-font-specification:'Courier New Bold';fill:#009600;fill-opacity:1;stroke-width:0.26458332"
|
|
||||||
id="tspan230">ssh</tspan></tspan></text>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 32 KiB |
@ -1,12 +0,0 @@
|
|||||||
|
|
||||||
# TODO: c2s does not warn if udp: 1 (instead of 'true')
|
|
||||||
|
|
||||||
udp: true;
|
|
||||||
|
|
||||||
prefix: "hello";
|
|
||||||
|
|
||||||
listen: "localhost:9000";
|
|
||||||
|
|
||||||
listen-host: "localhost";
|
|
||||||
listen-port: "9000";
|
|
||||||
|
|
1330
echosrv-conf.c
1330
echosrv-conf.c
File diff suppressed because it is too large
Load Diff
@ -1,66 +0,0 @@
|
|||||||
/* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README)
|
|
||||||
* on Sun Apr 6 11:44:59 2025.
|
|
||||||
|
|
||||||
# conf2struct: generate libconf parsers that read to structs
|
|
||||||
# Copyright (C) 2018-2024 Yves Rutschle
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
# Redistribution and use in source and binary forms, with or without
|
|
||||||
# modification, are permitted provided that the following conditions are met:
|
|
||||||
#
|
|
||||||
# 1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
# this list of conditions and the following disclaimer.
|
|
||||||
# 2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
# notice, this list of conditions and the following disclaimer in the
|
|
||||||
# documentation and/or other materials provided with the distribution.
|
|
||||||
#
|
|
||||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
||||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
# POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef C2S_ECHOCFG_H
|
|
||||||
#define C2S_ECHOCFG_H
|
|
||||||
#ifdef LIBCONFIG
|
|
||||||
# include <libconfig.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
struct echocfg_listen_item {
|
|
||||||
char* host;
|
|
||||||
char* port;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct echocfg_item {
|
|
||||||
int udp;
|
|
||||||
char* prefix;
|
|
||||||
size_t listen_len;
|
|
||||||
struct echocfg_listen_item* listen;
|
|
||||||
};
|
|
||||||
|
|
||||||
int echocfg_parse_file(
|
|
||||||
const char* filename,
|
|
||||||
struct echocfg_item* echocfg,
|
|
||||||
const char** errmsg);
|
|
||||||
|
|
||||||
void echocfg_fprint(
|
|
||||||
FILE* out,
|
|
||||||
struct echocfg_item *echocfg,
|
|
||||||
int depth);
|
|
||||||
|
|
||||||
int echocfg_cl_parse(
|
|
||||||
int argc,
|
|
||||||
char* argv[],
|
|
||||||
struct echocfg_item *echocfg);
|
|
||||||
|
|
||||||
#endif
|
|
330
echosrv.c
330
echosrv.c
@ -1,6 +1,6 @@
|
|||||||
/* echosrv: a simple line echo server with optional prefix adding.
|
/* echosrv: a simple line echo server with optional prefix adding.
|
||||||
*
|
*
|
||||||
* echosrv --listen localhost6:1234 --prefix "ssl: "
|
* echsrv --listen localhost6:1234 --prefix "ssl: "
|
||||||
*
|
*
|
||||||
* This will bind to 1234, and echo every line pre-pending "ssl: ". This is
|
* 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,
|
* used for testing: we create several such servers with different prefixes,
|
||||||
@ -27,13 +27,8 @@
|
|||||||
#include <syslog.h>
|
#include <syslog.h>
|
||||||
#include <libgen.h>
|
#include <libgen.h>
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <errno.h>
|
|
||||||
|
|
||||||
#define cfg sslhcfg
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#undef cfg
|
|
||||||
|
|
||||||
#include "echosrv-conf.h"
|
|
||||||
|
|
||||||
/* Added to make the code compilable under CYGWIN
|
/* Added to make the code compilable under CYGWIN
|
||||||
* */
|
* */
|
||||||
@ -41,53 +36,80 @@
|
|||||||
#define SA_NOCLDWAIT 0
|
#define SA_NOCLDWAIT 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct echocfg_item cfg;
|
const char* USAGE_STRING =
|
||||||
|
"echosrv\n" \
|
||||||
|
"usage:\n" \
|
||||||
|
"\techosrv [-v] --listen <address:port> [--prefix <prefix>]\n"
|
||||||
|
"-v: verbose\n" \
|
||||||
|
"--listen: address to listen on. Can be specified multiple times.\n" \
|
||||||
|
"--prefix: add specified prefix before every line echoed.\n"
|
||||||
|
"";
|
||||||
|
|
||||||
void check_res_dump(int res, struct addrinfo *addr, char* syscall)
|
const char* server_type = "echsrv"; /* keep setup_syslog happy */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Settings that depend on the command line.
|
||||||
|
*/
|
||||||
|
char* prefix = "";
|
||||||
|
int port;
|
||||||
|
|
||||||
|
void parse_cmdline(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
char buf[NI_MAXHOST];
|
int c;
|
||||||
|
struct option options[] = {
|
||||||
|
{ "verbose", no_argument, &verbose, 1 },
|
||||||
|
{ "numeric", no_argument, &numeric, 1 },
|
||||||
|
{ "listen", required_argument, 0, 'l' },
|
||||||
|
{ "prefix", required_argument, 0, 'p' },
|
||||||
|
};
|
||||||
|
struct addrinfo **a;
|
||||||
|
|
||||||
if (res == -1) {
|
while ((c = getopt_long_only(argc, argv, "l:p:", options, NULL)) != -1) {
|
||||||
if (addr)
|
if (c == 0) continue;
|
||||||
fprintf(stderr, "error %s:%s: %s\n",
|
|
||||||
sprintaddr(buf, sizeof(buf), addr),
|
|
||||||
syscall,
|
|
||||||
strerror(errno));
|
|
||||||
else
|
|
||||||
fprintf(stderr, "Dying just because\n");
|
|
||||||
|
|
||||||
|
switch (c) {
|
||||||
|
|
||||||
|
case 'l':
|
||||||
|
/* find the end of the listen list */
|
||||||
|
for (a = &addr_listen; *a; a = &((*a)->ai_next));
|
||||||
|
/* append the specified addresses */
|
||||||
|
resolve_name(a, optarg);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'p':
|
||||||
|
prefix = optarg;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "%s", USAGE_STRING);
|
||||||
|
exit(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!addr_listen) {
|
||||||
|
fprintf(stderr, "No listening port specified\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void start_echo(int fd)
|
void start_echo(int fd)
|
||||||
{
|
{
|
||||||
ssize_t res;
|
int res;
|
||||||
char buffer[1 << 20];
|
char buffer[1 << 20];
|
||||||
ssize_t ret;
|
int ret, prefix_len;
|
||||||
size_t prefix_len;
|
|
||||||
int first = 1;
|
|
||||||
|
|
||||||
prefix_len = strlen(cfg.prefix);
|
prefix_len = strlen(prefix);
|
||||||
|
|
||||||
memset(buffer, 0, sizeof(buffer));
|
memset(buffer, 0, sizeof(buffer));
|
||||||
strcpy(buffer, cfg.prefix);
|
strcpy(buffer, prefix);
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
ret = read(fd, buffer + prefix_len, sizeof(buffer) - prefix_len);
|
ret = read(fd, buffer + prefix_len, sizeof(buffer));
|
||||||
if (ret <= 0) {
|
if (ret == -1) {
|
||||||
fprintf(stderr, "%s", strerror(errno));
|
fprintf(stderr, "%s", strerror(errno));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (first) {
|
res = write(fd, buffer, ret + prefix_len);
|
||||||
res = write(fd, buffer, ret + prefix_len);
|
|
||||||
first = 0;
|
|
||||||
if (write(1, buffer, ret + prefix_len) < 0) {
|
|
||||||
fprintf(stderr, "%s", strerror(errno));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
res = write(fd, buffer + prefix_len, ret);
|
|
||||||
}
|
|
||||||
if (res < 0) {
|
if (res < 0) {
|
||||||
fprintf(stderr, "%s", strerror(errno));
|
fprintf(stderr, "%s", strerror(errno));
|
||||||
return;
|
return;
|
||||||
@ -95,232 +117,30 @@ void start_echo(int fd)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TCP echo server: accepts connections to an endpoint, forks an echo for each
|
void main_loop(int listen_sockets[], int num_addr_listen)
|
||||||
* connection, forever. Prefix is added at start of response stream */
|
|
||||||
void tcp_echo(struct listen_endpoint* listen_socket)
|
|
||||||
{
|
{
|
||||||
while (1) {
|
int in_socket, i;
|
||||||
int in_socket = accept(listen_socket->socketfd, 0, 0);
|
|
||||||
if (in_socket == -1) {
|
|
||||||
perror("tcp_echo:accept");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!fork())
|
|
||||||
{
|
|
||||||
close(listen_socket->socketfd);
|
|
||||||
start_echo(in_socket);
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
close(in_socket);
|
|
||||||
waitpid(-1, NULL, WNOHANG);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void print_udp_xchange(int sockfd, struct sockaddr* addr, socklen_t addrlen)
|
|
||||||
{
|
|
||||||
struct addrinfo src_addrinfo, to_addrinfo;
|
|
||||||
char str_addr[NI_MAXHOST+1+NI_MAXSERV+1];
|
|
||||||
char str_addr2[NI_MAXHOST+1+NI_MAXSERV+1];
|
|
||||||
struct sockaddr_storage ss;
|
|
||||||
|
|
||||||
src_addrinfo.ai_addr = (struct sockaddr*)&ss;
|
|
||||||
src_addrinfo.ai_addrlen = sizeof(ss);
|
|
||||||
getsockname(sockfd, src_addrinfo.ai_addr, &src_addrinfo.ai_addrlen);
|
|
||||||
|
|
||||||
to_addrinfo.ai_addr = addr;
|
|
||||||
to_addrinfo.ai_addrlen = sizeof(*addr);
|
|
||||||
|
|
||||||
fprintf(stderr, "UDP local %s remote %s\n",
|
|
||||||
sprintaddr(str_addr, sizeof(str_addr), &src_addrinfo),
|
|
||||||
sprintaddr(str_addr2, sizeof(str_addr2), &to_addrinfo)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* UDP echo server: receive packets, return them, forever.
|
|
||||||
* Prefix is added at each packet */
|
|
||||||
void udp_echo(struct listen_endpoint* listen_socket)
|
|
||||||
{
|
|
||||||
char data[65536];
|
|
||||||
struct sockaddr src_addr;
|
|
||||||
socklen_t addrlen;
|
|
||||||
|
|
||||||
memset(data, 0, sizeof(data));
|
|
||||||
|
|
||||||
size_t prefix_len = strlen(cfg.prefix);
|
|
||||||
memcpy(data, cfg.prefix, prefix_len);
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
addrlen = sizeof(src_addr);
|
|
||||||
ssize_t len = recvfrom(listen_socket->socketfd,
|
|
||||||
data + prefix_len,
|
|
||||||
sizeof(data) - prefix_len,
|
|
||||||
0,
|
|
||||||
&src_addr,
|
|
||||||
&addrlen);
|
|
||||||
|
|
||||||
if (len < 0) {
|
|
||||||
perror("recvfrom");
|
|
||||||
}
|
|
||||||
*(data + prefix_len + len) = 0;
|
|
||||||
fprintf(stderr, "%zd %s\n", len, data + prefix_len);
|
|
||||||
|
|
||||||
print_udp_xchange(listen_socket->socketfd, &src_addr, addrlen);
|
|
||||||
|
|
||||||
ssize_t res = sendto(listen_socket->socketfd,
|
|
||||||
data,
|
|
||||||
len + prefix_len,
|
|
||||||
0,
|
|
||||||
&src_addr,
|
|
||||||
addrlen);
|
|
||||||
if (res < 0) {
|
|
||||||
perror("sendto");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void main_loop(struct listen_endpoint listen_sockets[], int num_addr_listen)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < num_addr_listen; i++) {
|
for (i = 0; i < num_addr_listen; i++) {
|
||||||
if (!fork()) {
|
if (!fork()) {
|
||||||
if (cfg.udp) {
|
while (1)
|
||||||
udp_echo(&listen_sockets[i]);
|
{
|
||||||
} else {
|
in_socket = accept(listen_sockets[i], 0, 0);
|
||||||
tcp_echo(&listen_sockets[i]);
|
if (verbose) fprintf(stderr, "accepted fd %d\n", in_socket);
|
||||||
|
|
||||||
|
if (!fork())
|
||||||
|
{
|
||||||
|
close(listen_sockets[i]);
|
||||||
|
start_echo(in_socket);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
close(in_socket);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
wait(NULL);
|
wait(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Following is a number of utility functions copied from common.c: linking
|
|
||||||
* against common.o directly means echosrv has to work with sslh config struct,
|
|
||||||
* which makes it all too awkward */
|
|
||||||
|
|
||||||
/* simplified from common.c */
|
|
||||||
char* sprintaddr(char* buf, size_t size, struct addrinfo *a)
|
|
||||||
{
|
|
||||||
char host[NI_MAXHOST], serv[NI_MAXSERV];
|
|
||||||
int res;
|
|
||||||
|
|
||||||
res = getnameinfo(a->ai_addr, a->ai_addrlen,
|
|
||||||
host, sizeof(host),
|
|
||||||
serv, sizeof(serv),
|
|
||||||
0 );
|
|
||||||
|
|
||||||
if (res) {
|
|
||||||
/* Name resolution failed: do it numerically instead */
|
|
||||||
res = getnameinfo(a->ai_addr, a->ai_addrlen,
|
|
||||||
host, sizeof(host),
|
|
||||||
serv, sizeof(serv),
|
|
||||||
NI_NUMERICHOST | NI_NUMERICSERV);
|
|
||||||
/* should not fail but... */
|
|
||||||
if (res) {
|
|
||||||
strcpy(host, "?");
|
|
||||||
strcpy(serv, "?");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
snprintf(buf, size, "%s:%s", host, serv);
|
|
||||||
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* simplified from common.c */
|
|
||||||
int listen_single_addr(struct addrinfo* addr, int keepalive, int udp)
|
|
||||||
{
|
|
||||||
struct sockaddr_storage *saddr;
|
|
||||||
int sockfd, one, res;
|
|
||||||
|
|
||||||
saddr = (struct sockaddr_storage*)addr->ai_addr;
|
|
||||||
|
|
||||||
sockfd = socket(saddr->ss_family, udp ? SOCK_DGRAM : SOCK_STREAM, 0);
|
|
||||||
check_res_dump(sockfd, addr, "socket");
|
|
||||||
|
|
||||||
one = 1;
|
|
||||||
res = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one));
|
|
||||||
check_res_dump(res, addr, "setsockopt(SO_REUSEADDR)");
|
|
||||||
|
|
||||||
if (addr->ai_addr->sa_family == AF_INET6) {
|
|
||||||
res = setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&one, sizeof(one));
|
|
||||||
check_res_dump(res, addr, "setsockopt(IPV6_V6ONLY)");
|
|
||||||
}
|
|
||||||
|
|
||||||
res = bind(sockfd, addr->ai_addr, addr->ai_addrlen);
|
|
||||||
check_res_dump(res, addr, "bind");
|
|
||||||
|
|
||||||
if (!udp) {
|
|
||||||
res = listen (sockfd, 50);
|
|
||||||
check_res_dump(res, addr, "listen");
|
|
||||||
}
|
|
||||||
|
|
||||||
return sockfd;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* simplified from common.c */
|
|
||||||
int resolve_split_name(struct addrinfo **out, char* host, char* serv)
|
|
||||||
{
|
|
||||||
struct addrinfo hint;
|
|
||||||
char *end;
|
|
||||||
int res;
|
|
||||||
|
|
||||||
memset(&hint, 0, sizeof(hint));
|
|
||||||
hint.ai_family = PF_UNSPEC;
|
|
||||||
hint.ai_socktype = SOCK_STREAM;
|
|
||||||
|
|
||||||
/* If it is a RFC-Compliant IPv6 address ("[1234::12]:443"), remove brackets
|
|
||||||
* around IP address */
|
|
||||||
if (host[0] == '[') {
|
|
||||||
end = strrchr(host, ']');
|
|
||||||
if (!end) {
|
|
||||||
fprintf(stderr, "%s: no closing bracket in IPv6 address?\n", host);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
host++; /* skip first bracket */
|
|
||||||
*end = 0; /* remove last bracket */
|
|
||||||
}
|
|
||||||
|
|
||||||
res = getaddrinfo(host, serv, &hint, out);
|
|
||||||
|
|
||||||
if (res)
|
|
||||||
fprintf(stderr, "%s `%s:%s'\n", gai_strerror(res), host, serv);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
int start_listen_sockets(struct listen_endpoint *sockfd[])
|
|
||||||
{
|
|
||||||
struct addrinfo *addr, *start_addr;
|
|
||||||
char buf[NI_MAXHOST];
|
|
||||||
int i, res;
|
|
||||||
int num_addr = 0, keepalive = 0, udp = 0;
|
|
||||||
|
|
||||||
*sockfd = NULL;
|
|
||||||
|
|
||||||
fprintf(stderr, "Listening to:\n");
|
|
||||||
|
|
||||||
for (i = 0; i < cfg.listen_len; i++) {
|
|
||||||
udp = cfg.udp;
|
|
||||||
|
|
||||||
|
|
||||||
res = resolve_split_name(&start_addr, cfg.listen[i].host, cfg.listen[i].port);
|
|
||||||
if (res) exit(4);
|
|
||||||
|
|
||||||
for (addr = start_addr; addr; addr = addr->ai_next) {
|
|
||||||
num_addr++;
|
|
||||||
*sockfd = realloc(*sockfd, num_addr * sizeof(*sockfd));
|
|
||||||
(*sockfd)[num_addr-1].socketfd = listen_single_addr(addr, keepalive, udp);
|
|
||||||
(*sockfd)[num_addr-1].type = udp ? SOCK_DGRAM : SOCK_STREAM;
|
|
||||||
fprintf(stderr, "%d:\t%s\n", (*sockfd)[num_addr-1].socketfd, sprintaddr(buf, sizeof(buf), addr));
|
|
||||||
}
|
|
||||||
freeaddrinfo(start_addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
return num_addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -328,15 +148,11 @@ int main(int argc, char *argv[])
|
|||||||
extern int optind;
|
extern int optind;
|
||||||
int num_addr_listen;
|
int num_addr_listen;
|
||||||
|
|
||||||
struct listen_endpoint *listen_sockets;
|
int *listen_sockets;
|
||||||
|
|
||||||
memset(&cfg, 0, sizeof(cfg));
|
parse_cmdline(argc, argv);
|
||||||
if (echocfg_cl_parse(argc, argv, &cfg))
|
|
||||||
exit(1);
|
|
||||||
|
|
||||||
echocfg_fprint(stdout, &cfg, 0);
|
num_addr_listen = start_listen_sockets(&listen_sockets, addr_listen);
|
||||||
|
|
||||||
num_addr_listen = start_listen_sockets(&listen_sockets);
|
|
||||||
|
|
||||||
main_loop(listen_sockets, num_addr_listen);
|
main_loop(listen_sockets, num_addr_listen);
|
||||||
|
|
||||||
|
37
echosrv.cfg
37
echosrv.cfg
@ -1,37 +0,0 @@
|
|||||||
# conf2struct for echosrv
|
|
||||||
|
|
||||||
header: "echosrv-conf.h";
|
|
||||||
parser: "echosrv-conf.c";
|
|
||||||
|
|
||||||
printer: true;
|
|
||||||
|
|
||||||
conffile_option: ("F", "config");
|
|
||||||
|
|
||||||
config: {
|
|
||||||
name: "echocfg",
|
|
||||||
type: "list",
|
|
||||||
items: (
|
|
||||||
{name: "udp", type: "bool"; default: false; },
|
|
||||||
{name: "prefix", type: "string"; },
|
|
||||||
{ name: "listen",
|
|
||||||
type: "list",
|
|
||||||
items: (
|
|
||||||
{ name: "host"; type: "string"; var: true; },
|
|
||||||
{ name: "port"; type: "string"; var: true; }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
cl_groups: (
|
|
||||||
{ name: "listen"; pattern: "(.+):(\w+)"; description: "Listen on host:port";
|
|
||||||
short: "p"; argdesc: "<host:port>";
|
|
||||||
list: "listen";
|
|
||||||
# no override, this just adds to the list (and thus can be specified several times)
|
|
||||||
targets: (
|
|
||||||
{ path: "host"; value: "$1" },
|
|
||||||
{ path: "port"; value: "$2" }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
)
|
|
109
echoѕrv-conf.h
109
echoѕrv-conf.h
@ -1,109 +0,0 @@
|
|||||||
/* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README)
|
|
||||||
* on Sat Nov 7 09:19:26 2020.
|
|
||||||
|
|
||||||
# conf2struct: generate libconf parsers that read to structs
|
|
||||||
# Copyright (C) 2018-2019 Yves Rutschle
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
# Redistribution and use in source and binary forms, with or without
|
|
||||||
# modification, are permitted provided that the following conditions are met:
|
|
||||||
#
|
|
||||||
# 1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
# this list of conditions and the following disclaimer.
|
|
||||||
# 2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
# notice, this list of conditions and the following disclaimer in the
|
|
||||||
# documentation and/or other materials provided with the distribution.
|
|
||||||
#
|
|
||||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
||||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
# POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef C2S_SSLHCFG_H
|
|
||||||
#define C2S_SSLHCFG_H
|
|
||||||
#ifdef LIBCONFIG
|
|
||||||
# include <libconfig.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#include "probe.h"
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <netdb.h>
|
|
||||||
|
|
||||||
struct sslhcfg_listen_item {
|
|
||||||
char* host;
|
|
||||||
char* port;
|
|
||||||
int keepalive;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct sslhcfg_protocols_item {
|
|
||||||
char* name;
|
|
||||||
char* host;
|
|
||||||
char* port;
|
|
||||||
int service_is_present;
|
|
||||||
char* service;
|
|
||||||
int fork;
|
|
||||||
int tfo_ok;
|
|
||||||
int log_level;
|
|
||||||
int keepalive;
|
|
||||||
size_t sni_hostnames_len;
|
|
||||||
char** sni_hostnames;
|
|
||||||
size_t alpn_protocols_len;
|
|
||||||
char** alpn_protocols;
|
|
||||||
size_t regex_patterns_len;
|
|
||||||
char** regex_patterns;
|
|
||||||
int minlength_is_present;
|
|
||||||
int minlength;
|
|
||||||
T_PROBE* probe;
|
|
||||||
struct addrinfo* saddr;
|
|
||||||
void* data;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct sslhcfg_item {
|
|
||||||
char* prefix;
|
|
||||||
int verbose;
|
|
||||||
int foreground;
|
|
||||||
int inetd;
|
|
||||||
int numeric;
|
|
||||||
int transparent;
|
|
||||||
int timeout;
|
|
||||||
int user_is_present;
|
|
||||||
char* user;
|
|
||||||
int pidfile_is_present;
|
|
||||||
char* pidfile;
|
|
||||||
int chroot_is_present;
|
|
||||||
char* chroot;
|
|
||||||
char* syslog_facility;
|
|
||||||
char* on_timeout;
|
|
||||||
size_t listen_len;
|
|
||||||
struct sslhcfg_listen_item* listen;
|
|
||||||
size_t protocols_len;
|
|
||||||
struct sslhcfg_protocols_item* protocols;
|
|
||||||
};
|
|
||||||
|
|
||||||
int sslhcfg_parse_file(
|
|
||||||
const char* filename,
|
|
||||||
struct sslhcfg_item* sslhcfg,
|
|
||||||
const char** errmsg);
|
|
||||||
|
|
||||||
void sslhcfg_fprint(
|
|
||||||
FILE* out,
|
|
||||||
struct sslhcfg_item *sslhcfg,
|
|
||||||
int depth);
|
|
||||||
|
|
||||||
int sslhcfg_cl_parse(
|
|
||||||
int argc,
|
|
||||||
char* argv[],
|
|
||||||
struct sslhcfg_item *sslhcfg);
|
|
||||||
|
|
||||||
#endif
|
|
154
example.cfg
154
example.cfg
@ -3,6 +3,7 @@
|
|||||||
# not be used as a starting point for a working
|
# not be used as a starting point for a working
|
||||||
# configuration. Instead use basic.cfg.
|
# configuration. Instead use basic.cfg.
|
||||||
|
|
||||||
|
verbose: true;
|
||||||
foreground: true;
|
foreground: true;
|
||||||
inetd: false;
|
inetd: false;
|
||||||
numeric: false;
|
numeric: false;
|
||||||
@ -10,163 +11,42 @@ transparent: false;
|
|||||||
timeout: 2;
|
timeout: 2;
|
||||||
user: "nobody";
|
user: "nobody";
|
||||||
pidfile: "/var/run/sslh.pid";
|
pidfile: "/var/run/sslh.pid";
|
||||||
chroot: "/var/empty";
|
|
||||||
|
|
||||||
# Logging configuration
|
|
||||||
# Value: 1: stdout; 2: syslog; 3: stdout+syslog; 4: logfile; ...; 7: all
|
|
||||||
# Defaults are indicated here, and should be sensible. Generally, you want *-error
|
|
||||||
# to be always enabled, to know if something is going wrong.
|
|
||||||
# Each option relates to a different set of messages.
|
|
||||||
verbose-config: 0; # print configuration at startup
|
|
||||||
verbose-config-error: 3; # print configuration errors
|
|
||||||
verbose-connections: 3; # trace established incoming address to forward address
|
|
||||||
verbose-connections-error: 3; # connection errors
|
|
||||||
verbose-connections-try: 0; # connection attempts towards targets
|
|
||||||
verbose-fd: 0; # file descriptor activity, open/close/whatnot
|
|
||||||
verbose-packets: 0; # hexdump packets on which probing is done
|
|
||||||
verbose-probe-info: 0; # what's happening during the probe process
|
|
||||||
verbose-probe-error: 3; # failures and problems during probing
|
|
||||||
verbose-system-error: 3; # system call problem, i.e. malloc, fork, failing
|
|
||||||
verbose-int-error: 3; # internal errors, the kind that should never happen
|
|
||||||
|
|
||||||
# This one is special and overrides all previous options if
|
|
||||||
# set, as a quick way to get "as much as possible"
|
|
||||||
#verbose: 3;
|
|
||||||
|
|
||||||
# Specify a path to the logfile.
|
|
||||||
#logfile: "/var/log/sslh.log"
|
|
||||||
|
|
||||||
# Specify the number of concurrent UDP connection that can
|
|
||||||
# be managed (default 1024)
|
|
||||||
udp_max_connections: 16;
|
|
||||||
|
|
||||||
# Specify which syslog facility to use (names for your
|
|
||||||
# system are usually defined in /usr/include/*/sys/syslog.h
|
|
||||||
# or equivalent)
|
|
||||||
# Default is "auth"
|
|
||||||
# "none" disables use of syslog
|
|
||||||
syslog_facility: "auth";
|
|
||||||
|
|
||||||
# List of interfaces on which we should listen
|
# List of interfaces on which we should listen
|
||||||
# Options:
|
|
||||||
listen:
|
listen:
|
||||||
(
|
(
|
||||||
{ host: "thelonious"; port: "443"; },
|
{ host: "thelonious"; port: "443"; },
|
||||||
{ host: "thelonious"; port: "8080"; keepalive: true; },
|
{ host: "thelonious"; port: "8080"; }
|
||||||
{ host: "thelonious"; is_udp: true; port: "443"; },
|
|
||||||
{ host: "/tmp/unix_socket"; is_unix: true; port: ""; }
|
|
||||||
);
|
);
|
||||||
|
|
||||||
# List of protocols
|
# List of protocols
|
||||||
#
|
#
|
||||||
# Each protocol entry consists of:
|
# Each protocol entry consists of:
|
||||||
# name: name of the probe. These are listed on the command
|
# name: name of the protocol
|
||||||
# line (ssh -?), plus 'regex' and 'timeout'.
|
|
||||||
|
|
||||||
# service: (optional) libwrap service name (see hosts_access(5))
|
# service: (optional) libwrap service name (see hosts_access(5))
|
||||||
# host, port: where to connect when this probe succeeds
|
# host: host name to connect that protocol
|
||||||
# log_level: 0 to turn off logging
|
# port: port number to connect that protocol
|
||||||
# 1 to log each incoming connection
|
# probe: "builtin" or a list of regular expressions
|
||||||
# keepalive: Should TCP keepalive be on or off for that
|
# (can be left out, e.g. to use with on-timeout)
|
||||||
# connection (default is off)
|
|
||||||
# fork: Should a new process be forked for this protocol?
|
|
||||||
# (only useful for sslh-select)
|
|
||||||
# tfo_ok: Set to true if the server supports TCP FAST OPEN
|
|
||||||
# resolve_on_forward: Set to true if server address should be resolved on
|
|
||||||
# (every) newly incoming connection (again)
|
|
||||||
# transparent: Set to true to proxy this protocol
|
|
||||||
# transparently (server sees the remote client IP
|
|
||||||
# address). Same as the global option, but per-protocol
|
|
||||||
# is_unix: [true|false] connect to a UNIX socket. The host
|
|
||||||
# field becomes the pathname to the socket, and the port
|
|
||||||
# field is unused (but necessary).
|
|
||||||
# proxyprotocol: <1|2>; When connecting to the backend
|
|
||||||
# server, a proxyprotocol header of the specified
|
|
||||||
# version will be added, containing the client's
|
|
||||||
# connection information.
|
|
||||||
#
|
|
||||||
# Probe-specific options:
|
|
||||||
# (sslh will try each probe in order they are declared, and
|
|
||||||
# connect to the first that matches.)
|
|
||||||
#
|
|
||||||
# tls:
|
|
||||||
# sni_hostnames: list of FQDN for that target. Each name can
|
|
||||||
# include wildcard following glob(7) rules.
|
|
||||||
|
|
||||||
# alpn_protocols: list of ALPN protocols for that target, see:
|
|
||||||
# https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids
|
|
||||||
#
|
|
||||||
# if both sni_hostnames AND alpn_protocols are specified, both must match
|
|
||||||
#
|
|
||||||
# if neither are set, it is just checked whether this is the TLS protocol or not
|
|
||||||
#
|
|
||||||
# Obviously set the most specific probes
|
|
||||||
# first, and if you use TLS with no ALPN/SNI
|
|
||||||
# set it as the last TLS probe
|
|
||||||
# regex:
|
|
||||||
# regex_patterns: list of patterns to match for
|
|
||||||
# that target.
|
|
||||||
#
|
#
|
||||||
# You can specify several of 'regex' and 'tls'.
|
# sslh will try each probe in order they are declared, and
|
||||||
#
|
# connect to the first that matches.
|
||||||
# If you want to filter on incoming IP addresses, you can
|
|
||||||
# use libwrap which will use /etc/hosts.allow and
|
|
||||||
# /etc/hosts.deny.
|
|
||||||
|
|
||||||
protocols:
|
protocols:
|
||||||
(
|
(
|
||||||
{ name: "ssh"; service: "ssh"; host: "localhost"; port: "22";
|
{ name: "ssh"; service: "ssh"; host: "localhost"; port: "22"; probe: "builtin"; },
|
||||||
keepalive: true; fork: true; tfo_ok: true },
|
{ name: "openvpn"; host: "localhost"; port: "1194"; probe: [ "^\x00[\x0D-\xFF]$", "^\x00[\x0D-\xFF]\x38" ]; },
|
||||||
|
{ name: "xmpp"; host: "localhost"; port: "5222"; probe: [ "jabber" ]; },
|
||||||
# UNIX socket to a local NGINX. The socket name is in 'host'; 'port' is necessary but not used.
|
{ name: "http"; host: "localhost"; port: "80"; probe: "builtin"; },
|
||||||
{ name: "http"; is_unix: true; host: "/tmp/nginx.sock"; port: ""; },
|
{ name: "ssl"; host: "localhost"; port: "443"; probe: [ "" ]; },
|
||||||
|
|
||||||
# match BOTH ALPN/SNI
|
|
||||||
{ name: "tls"; host: "localhost"; port: "5223"; alpn_protocols: [ "xmpp-client" ]; sni_hostnames: [ "im.somethingelse.net" ]; log_level: 0; tfo_ok: true },
|
|
||||||
|
|
||||||
# just match ALPN
|
|
||||||
{ name: "tls"; host: "localhost"; port: "443"; alpn_protocols: [ "h2", "http/1.1", "spdy/1", "spdy/2", "spdy/3" ]; log_level: 0; tfo_ok: true },
|
|
||||||
{ name: "tls"; host: "localhost"; port: "xmpp-client"; alpn_protocols: [ "xmpp-client" ]; log_level: 0; tfo_ok: true },
|
|
||||||
|
|
||||||
# just match SNI
|
|
||||||
{ name: "tls"; host: "localhost"; port: "993"; sni_hostnames: [ "mail.rutschle.net", "mail.englishintoulouse.com" ]; log_level: 0; tfo_ok: true },
|
|
||||||
{ name: "tls"; host: "localhost"; port: "xmpp-client"; sni_hostnames: [ "im.rutschle.net", "im.englishintoulouse.com" ]; log_level: 0; tfo_ok: true },
|
|
||||||
|
|
||||||
# Let's Encrypt (tls-alpn-* challenges)
|
|
||||||
{ name: "tls"; host: "localhost"; port: "letsencrypt-client"; alpn_protocols: [ "acme-tls/1" ]; log_level: 0;},
|
|
||||||
|
|
||||||
# catch anything else TLS
|
|
||||||
{ name: "tls"; host: "localhost"; port: "443"; tfo_ok: true },
|
|
||||||
|
|
||||||
# Forward UDP
|
|
||||||
{ name: "regex"; host: "localhost"; is_udp: true; port: "123";
|
|
||||||
udp_timeout: 20; # Time after which the "connection" is forgotten
|
|
||||||
regex_patterns: [ "hello" ]; },
|
|
||||||
# Forward Teamspeak3 (Voice only)
|
|
||||||
{ name: "teamspeak"; host: "localhost"; is_udp: true; port: "9987"; },
|
|
||||||
# Forward IETF QUIC-50 ("Q050" -> "\x51\x30\x35\x30")
|
|
||||||
# Remember that the regex needs to be adjusted for every supported QUIC version.
|
|
||||||
{ name: "regex"; host: "localhost"; is_udp: true; port: "4433"; regex_patterns: [ "\x51\x30\x35\x30" ]; },
|
|
||||||
|
|
||||||
# Regex examples -- better use the built-in probes for real-world use!
|
|
||||||
# OpenVPN
|
|
||||||
{ name: "regex"; host: "localhost"; port: "1194"; regex_patterns: [ "^\x00[\x0D-\xFF]$", "^\x00[\x0D-\xFF]\x38" ]; },
|
|
||||||
# Jabber
|
|
||||||
{ name: "regex"; host: "localhost"; port: "5222"; regex_patterns: [ "jabber" ];
|
|
||||||
minlength: 60; # Won't even try to match the regex if we don't have that many bytes
|
|
||||||
},
|
|
||||||
|
|
||||||
# Catch-all (but better use 'anyprot')
|
|
||||||
{ name: "regex"; host: "localhost"; port: "443"; regex_patterns: [ "" ]; },
|
|
||||||
|
|
||||||
# Where to connect in case of timeout (defaults to ssh)
|
|
||||||
{ name: "timeout"; service: "daytime"; host: "localhost"; port: "daytime"; }
|
{ name: "timeout"; service: "daytime"; host: "localhost"; port: "daytime"; }
|
||||||
);
|
);
|
||||||
|
|
||||||
# Optionally, specify to which protocol to connect in case
|
# Optionally, specify to which protocol to connect in case
|
||||||
# of timeout (defaults to "ssh").
|
# of timeout (defaults to "ssh").
|
||||||
# You can timeout to any arbitrary address by setting an
|
# You can timeout to any arbitrary address by setting a
|
||||||
# entry in 'protocols' named "timeout".
|
# protocol with no probe, as is the case with this example.
|
||||||
# This enables you to set a tcpd service name for this
|
# This enables you to set a tcpd service name for this
|
||||||
# protocol too.
|
# protocol too.
|
||||||
on-timeout: "timeout";
|
on-timeout: "timeout";
|
||||||
|
111
gap.c
111
gap.c
@ -1,111 +0,0 @@
|
|||||||
/*
|
|
||||||
gap.c: gap, a simple, dynamically-growing array
|
|
||||||
of pointers that never shrinks
|
|
||||||
|
|
||||||
# Copyright (C) 2021 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 <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#include "sslh-conf.h"
|
|
||||||
#include "gap.h"
|
|
||||||
|
|
||||||
|
|
||||||
/* Allocate one page-worth of elements */
|
|
||||||
static int gap_len_alloc(int elem_size)
|
|
||||||
{
|
|
||||||
return getpagesize() / elem_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Creates a new gap at least `len` big, all pointers are initialised at NULL */
|
|
||||||
gap_array* gap_init(int len)
|
|
||||||
{
|
|
||||||
gap_array* gap = malloc(sizeof(*gap));
|
|
||||||
if (!gap) return NULL;
|
|
||||||
memset(gap, 0, sizeof(*gap));
|
|
||||||
|
|
||||||
int elem_size = sizeof(gap->array[0]);
|
|
||||||
gap->len = gap_len_alloc(elem_size);
|
|
||||||
if (gap->len < len) gap->len = len;
|
|
||||||
gap->array = malloc(gap->len * elem_size);
|
|
||||||
if (!gap->array) {
|
|
||||||
free(gap);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < gap->len; i++)
|
|
||||||
gap->array[i] = NULL;
|
|
||||||
|
|
||||||
return gap;
|
|
||||||
}
|
|
||||||
|
|
||||||
int gap_extend(gap_array* gap)
|
|
||||||
{
|
|
||||||
int elem_size = sizeof(gap->array[0]);
|
|
||||||
int new_length = gap->len + gap_len_alloc(elem_size);
|
|
||||||
void** new = realloc(gap->array, new_length * elem_size);
|
|
||||||
if (!new) return -1;
|
|
||||||
|
|
||||||
gap->array = new;
|
|
||||||
|
|
||||||
for (int i = gap->len; i < new_length; i++) {
|
|
||||||
gap->array[i] = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
gap->len = new_length;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void gap_destroy(gap_array* gap)
|
|
||||||
{
|
|
||||||
free(gap->array);
|
|
||||||
free(gap);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* In gap, find element pointing to ptr, then shift the rest of the array that
|
|
||||||
* is considered len elements long.
|
|
||||||
* A poor man's list, if you will. Currently only used to remove probing
|
|
||||||
* connections, so it only copies a few pointers at most.
|
|
||||||
* Returns -1 if ptr was not found */
|
|
||||||
int gap_remove_ptr(gap_array* gap, void* ptr, int len)
|
|
||||||
{
|
|
||||||
int start, i;
|
|
||||||
|
|
||||||
for (i = 0; i < len; i++)
|
|
||||||
if (gap->array[i] == ptr)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (i < len)
|
|
||||||
start = i;
|
|
||||||
else
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
for (i = start; i < len - 1; i++) {
|
|
||||||
gap->array[i] = gap->array[i+1];
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
43
gap.h
43
gap.h
@ -1,43 +0,0 @@
|
|||||||
#ifndef GAP_H
|
|
||||||
#define GAP_H
|
|
||||||
|
|
||||||
typedef struct gap_array gap_array;
|
|
||||||
|
|
||||||
gap_array* gap_init(int len);
|
|
||||||
static void* gap_get(gap_array* gap, int index);
|
|
||||||
static int gap_set(gap_array* gap, int index, void* ptr);
|
|
||||||
void gap_destroy(gap_array* gap);
|
|
||||||
|
|
||||||
int gap_remove_ptr(gap_array* gap, void* ptr, int len);
|
|
||||||
|
|
||||||
/* Private declarations to allow inlining.
|
|
||||||
* Don't assume my implementation. */
|
|
||||||
typedef struct gap_array {
|
|
||||||
int len; /* Number of elements in array */
|
|
||||||
void** array;
|
|
||||||
} gap_array;
|
|
||||||
|
|
||||||
int gap_extend(gap_array* gap);
|
|
||||||
|
|
||||||
static inline int __attribute__((unused)) gap_set(gap_array* gap, int index, void* ptr)
|
|
||||||
{
|
|
||||||
while (index >= gap->len) {
|
|
||||||
int res = gap_extend(gap);
|
|
||||||
if (res == -1) return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
gap->array[index] = ptr;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
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];
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
29
genver.sh
29
genver.sh
@ -1,4 +1,4 @@
|
|||||||
#! /bin/sh
|
#! /bin/bash
|
||||||
|
|
||||||
if [ ${#} -eq 1 ] && [ "x$1" = "x-r" ]; then
|
if [ ${#} -eq 1 ] && [ "x$1" = "x-r" ]; then
|
||||||
# release text only
|
# release text only
|
||||||
@ -7,28 +7,19 @@ else
|
|||||||
QUIET=0
|
QUIET=0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ ! -d .git ] || ! `(git status | grep -q "On branch") 2> /dev/null`; then
|
if ! `(git status | grep -q "On branch") 2> /dev/null`; then
|
||||||
# If we don't have git, we can't work out what
|
# If we don't have git, we can't work out what
|
||||||
# version this is. It must have been downloaded as a
|
# version this is. It must have been downloaded as a
|
||||||
# zip file.
|
# zip file. Github creates the zip file with all
|
||||||
|
# files dated from the last change: use the
|
||||||
# If downloaded from the release page, the directory
|
# Makefile's modification time as a release number
|
||||||
# has the version number.
|
release=zip-`stat -c "%y" Makefile | sed 's/ .*//'`
|
||||||
release=`pwd | sed s/.*sslh-// | grep "[[:digit:]]"`
|
|
||||||
|
|
||||||
if [ "x$release" = "x" ]; then
|
|
||||||
# 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
|
|
||||||
release=head-`perl -MPOSIX -e 'print strftime "%Y-%m-%d",localtime((stat "Makefile")[9])'`
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -d .git ] && head=`git rev-parse --verify HEAD 2>/dev/null`; then
|
if head=`git rev-parse --verify HEAD 2>/dev/null`; then
|
||||||
# generate the version info based on the tag
|
# generate the version info based on the tag
|
||||||
release=`(git describe --tags || git --describe || git describe --all --long) \
|
release=`(git describe --tags || git --describe || git describe --all --long) \
|
||||||
2>/dev/null | tr -s '/' '-' | tr -d '\n'`
|
2>/dev/null | tr -d '\n'`
|
||||||
|
|
||||||
# Are there uncommitted changes?
|
# Are there uncommitted changes?
|
||||||
git update-index --refresh --unmerged > /dev/null
|
git update-index --refresh --unmerged > /dev/null
|
||||||
@ -40,8 +31,8 @@ fi
|
|||||||
|
|
||||||
|
|
||||||
if [ $QUIET -ne 1 ]; then
|
if [ $QUIET -ne 1 ]; then
|
||||||
printf "#ifndef VERSION_H \n"
|
printf "#ifndef _VERSION_H_ \n"
|
||||||
printf "#define VERSION_H \n\n"
|
printf "#define _VERSION_H_ \n\n"
|
||||||
printf "#define VERSION \"$release\"\n"
|
printf "#define VERSION \"$release\"\n"
|
||||||
printf "#endif\n"
|
printf "#endif\n"
|
||||||
else
|
else
|
||||||
|
224
hash.c
224
hash.c
@ -1,224 +0,0 @@
|
|||||||
/*
|
|
||||||
* a fixed-sized hash
|
|
||||||
*
|
|
||||||
# Copyright (C) 2022 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
|
|
||||||
#
|
|
||||||
# */
|
|
||||||
|
|
||||||
|
|
||||||
/* * The hash is open-addressing, linear search, robin-hood insertion, with
|
|
||||||
* backward shift deletion. References:
|
|
||||||
* https://codecapsule.com/2013/11/11/robin-hood-hashing/
|
|
||||||
* https://codecapsule.com/2013/11/17/robin-hood-hashing-backward-shift-deletion/
|
|
||||||
* This means items are reordered upon insertion and deletion, and the hash
|
|
||||||
* is well-ordered at all times with no tombstones.
|
|
||||||
*
|
|
||||||
* Each pointer is either:
|
|
||||||
* - to a connection struct
|
|
||||||
* - FREE (NULL) if not allocated
|
|
||||||
*
|
|
||||||
* */
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
|
|
||||||
#include "gap.h"
|
|
||||||
|
|
||||||
typedef void* hash_item;
|
|
||||||
#include "hash.h"
|
|
||||||
|
|
||||||
static void* const FREE = NULL;
|
|
||||||
|
|
||||||
struct hash {
|
|
||||||
int hash_size; /* Max number of items in the hash */
|
|
||||||
int item_cnt; /* Number of items in the hash */
|
|
||||||
gap_array* data;
|
|
||||||
|
|
||||||
hash_make_key_fn hash_make_key;
|
|
||||||
hash_cmp_item_fn cmp_item;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct hash hash;
|
|
||||||
|
|
||||||
|
|
||||||
static int hash_make_key(hash* h, hash_item item)
|
|
||||||
{
|
|
||||||
return h->hash_make_key(item) % h->hash_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
hash* hash_init(int hash_size, hash_make_key_fn make_key, hash_cmp_item_fn cmp_item)
|
|
||||||
{
|
|
||||||
hash* h = malloc(sizeof(*h));
|
|
||||||
if (!h) return NULL;
|
|
||||||
|
|
||||||
h->hash_size = hash_size;
|
|
||||||
h->item_cnt = 0;
|
|
||||||
h->data = gap_init(hash_size);
|
|
||||||
h->hash_make_key = make_key;
|
|
||||||
h->cmp_item = cmp_item;
|
|
||||||
|
|
||||||
return h;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return the index following i in h */
|
|
||||||
static int hash_next_index(hash* h, int i)
|
|
||||||
{
|
|
||||||
return (i + 1) % h->hash_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns the index in h of specified address, -1 if not found
|
|
||||||
* item is an item object that must return the target wanted index and for
|
|
||||||
* which comparison with the searched object will succeed.
|
|
||||||
* */
|
|
||||||
static int hash_find_index(hash* h, hash_item item)
|
|
||||||
{
|
|
||||||
hash_item cnx;
|
|
||||||
int index = hash_make_key(h, item);
|
|
||||||
int cnt = 0;
|
|
||||||
|
|
||||||
cnx = gap_get(h->data, index);
|
|
||||||
#ifdef DEBUG
|
|
||||||
fprintf(stderr, "searching %d\n", index);
|
|
||||||
#endif
|
|
||||||
while (cnx != FREE) {
|
|
||||||
if (cnt++ > h->hash_size) return -1;
|
|
||||||
|
|
||||||
if (!h->cmp_item(cnx, item))
|
|
||||||
break;
|
|
||||||
|
|
||||||
index = hash_next_index(h, index);
|
|
||||||
cnx = gap_get(h->data, index);
|
|
||||||
#ifdef DEBUG
|
|
||||||
fprintf(stderr, "searching %d\n", index);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
if (cnx == FREE) return -1;
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
hash_item hash_find(hash* h, hash_item item)
|
|
||||||
{
|
|
||||||
int index = hash_find_index(h, item);
|
|
||||||
if (index == -1) return NULL;
|
|
||||||
hash_item out = gap_get(h->data, index);
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Returns DIB: distance to initial bucket */
|
|
||||||
static int distance(int current_index, hash* h, hash_item item)
|
|
||||||
{
|
|
||||||
int wanted_index = hash_make_key(h, item);
|
|
||||||
if (wanted_index <= current_index)
|
|
||||||
return current_index - wanted_index;
|
|
||||||
else
|
|
||||||
return current_index - wanted_index + h->hash_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int hash_insert(hash* h, hash_item new)
|
|
||||||
{
|
|
||||||
int bubble_wanted_index = hash_make_key(h, new);
|
|
||||||
int index = bubble_wanted_index;
|
|
||||||
gap_array* hash = h->data;
|
|
||||||
|
|
||||||
if (h->item_cnt == h->hash_size)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
hash_item curr_item = gap_get(hash, index);
|
|
||||||
while (curr_item) {
|
|
||||||
if (distance(index, h, curr_item) < distance(index, h, new)) {
|
|
||||||
gap_set(h->data, index, new);
|
|
||||||
#if DEBUG
|
|
||||||
fprintf(stderr, "intermediate insert [%s] at %d\n", &new->client_addr, index);
|
|
||||||
#endif
|
|
||||||
new = curr_item;
|
|
||||||
}
|
|
||||||
|
|
||||||
index = hash_next_index(h, index);
|
|
||||||
curr_item = gap_get(hash, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
fprintf(stderr, "final insert at %d\n", index);
|
|
||||||
#endif
|
|
||||||
gap_set(hash, index, new);
|
|
||||||
h->item_cnt++;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Remove cnx from the hash */
|
|
||||||
int hash_remove(hash* h, hash_item item)
|
|
||||||
{
|
|
||||||
gap_array* hash = h->data;
|
|
||||||
|
|
||||||
int index = hash_find_index(h, item);
|
|
||||||
if (index == -1) return -1; /* Tried to remove something that isn't there */
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
int next_index = hash_next_index(h, index);
|
|
||||||
hash_item next = gap_get(h->data, next_index);
|
|
||||||
if ((next == FREE) || (distance(next_index, h, next) == 0)) {
|
|
||||||
h->item_cnt--;
|
|
||||||
gap_set(hash, index, FREE);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
gap_set(hash, index, next);
|
|
||||||
|
|
||||||
index = hash_next_index(h, index);;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if HASH_TESTING
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#define STR_LENGTH 16
|
|
||||||
struct hash_item {
|
|
||||||
int wanted_index;
|
|
||||||
char str[STR_LENGTH];
|
|
||||||
};
|
|
||||||
void hash_dump(hash* h, char* filename)
|
|
||||||
{
|
|
||||||
char str[STR_LENGTH];
|
|
||||||
FILE* out = fopen(filename, "w");
|
|
||||||
|
|
||||||
if (!out) {
|
|
||||||
perror(filename);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(out, "<hash elem=%d>\n", h->item_cnt);
|
|
||||||
for (int i = 0; i < h->hash_size; i++) {
|
|
||||||
hash_item item = gap_get(h->data, i);
|
|
||||||
int idx = 0;
|
|
||||||
|
|
||||||
memset(str, 0, STR_LENGTH);
|
|
||||||
if (item) {
|
|
||||||
idx = hash_make_key(h, item);
|
|
||||||
memcpy(str, ((struct hash_item*)item)->str, STR_LENGTH);
|
|
||||||
}
|
|
||||||
fprintf(out, "\t%d:%d:%s\n", i, idx, str);
|
|
||||||
}
|
|
||||||
fprintf(out, "</hash>\n");
|
|
||||||
fclose(out);
|
|
||||||
}
|
|
||||||
#endif
|
|
28
hash.h
28
hash.h
@ -1,28 +0,0 @@
|
|||||||
#ifndef HASH_H
|
|
||||||
#define HASH_H
|
|
||||||
|
|
||||||
/* You will need to typedef a pointer type to hash_item before including this
|
|
||||||
* .h */
|
|
||||||
|
|
||||||
typedef struct hash hash;
|
|
||||||
|
|
||||||
/* Function that returns a key (index) for a given item. The key must be always
|
|
||||||
* the same for an item. It doesn't need to be bounded (hash.c masks it for you) */
|
|
||||||
typedef int (*hash_make_key_fn)(hash_item item);
|
|
||||||
|
|
||||||
/* Function that compares two items: returns 0 if they are the same */
|
|
||||||
typedef int (*hash_cmp_item_fn)(hash_item item1, hash_item item2);
|
|
||||||
|
|
||||||
hash* hash_init(int hash_size, hash_make_key_fn make_key, hash_cmp_item_fn cmp_item);
|
|
||||||
|
|
||||||
int hash_insert(hash* h, hash_item new);
|
|
||||||
int hash_remove(hash* h, hash_item item);
|
|
||||||
|
|
||||||
/* Returns the hash item that matches specification (meaning the
|
|
||||||
* comparison function returns true for cmp(x, item), or NULL if not found */
|
|
||||||
hash_item hash_find(hash* h, hash_item item);
|
|
||||||
|
|
||||||
|
|
||||||
void hash_dump(hash* h, char* filename); /* For development only */
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,7 +0,0 @@
|
|||||||
|
|
||||||
CFLAGS=-DHASH_TESTING -O2 -Wall
|
|
||||||
OBJ=../hash.o ../gap.o htest.o
|
|
||||||
|
|
||||||
htest: $(OBJ)
|
|
||||||
$(CC) -o htest $(OBJ)
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
|||||||
# Basic delete
|
|
||||||
a 10 aa
|
|
||||||
a 10 ab
|
|
||||||
a 10 ac
|
|
||||||
a 20 ba
|
|
||||||
a 21 bb
|
|
||||||
|
|
||||||
d 21 bb
|
|
@ -1,34 +0,0 @@
|
|||||||
<hash elem=4>
|
|
||||||
0:0:
|
|
||||||
1:0:
|
|
||||||
2:0:
|
|
||||||
3:0:
|
|
||||||
4:0:
|
|
||||||
5:0:
|
|
||||||
6:0:
|
|
||||||
7:0:
|
|
||||||
8:0:
|
|
||||||
9:0:
|
|
||||||
10:10:aa
|
|
||||||
11:10:ab
|
|
||||||
12:10:ac
|
|
||||||
13:0:
|
|
||||||
14:0:
|
|
||||||
15:0:
|
|
||||||
16:0:
|
|
||||||
17:0:
|
|
||||||
18:0:
|
|
||||||
19:0:
|
|
||||||
20:20:ba
|
|
||||||
21:0:
|
|
||||||
22:0:
|
|
||||||
23:0:
|
|
||||||
24:0:
|
|
||||||
25:0:
|
|
||||||
26:0:
|
|
||||||
27:0:
|
|
||||||
28:0:
|
|
||||||
29:0:
|
|
||||||
30:0:
|
|
||||||
31:0:
|
|
||||||
</hash>
|
|
@ -1,9 +0,0 @@
|
|||||||
# Delete inside a block with nothing after
|
|
||||||
|
|
||||||
a 10 aa
|
|
||||||
a 10 ab
|
|
||||||
a 12 ac
|
|
||||||
a 13 ad
|
|
||||||
a 14 ae
|
|
||||||
|
|
||||||
d 14 ae
|
|
@ -1,34 +0,0 @@
|
|||||||
<hash elem=4>
|
|
||||||
0:0:
|
|
||||||
1:0:
|
|
||||||
2:0:
|
|
||||||
3:0:
|
|
||||||
4:0:
|
|
||||||
5:0:
|
|
||||||
6:0:
|
|
||||||
7:0:
|
|
||||||
8:0:
|
|
||||||
9:0:
|
|
||||||
10:10:aa
|
|
||||||
11:10:ab
|
|
||||||
12:12:ac
|
|
||||||
13:13:ad
|
|
||||||
14:0:
|
|
||||||
15:0:
|
|
||||||
16:0:
|
|
||||||
17:0:
|
|
||||||
18:0:
|
|
||||||
19:0:
|
|
||||||
20:0:
|
|
||||||
21:0:
|
|
||||||
22:0:
|
|
||||||
23:0:
|
|
||||||
24:0:
|
|
||||||
25:0:
|
|
||||||
26:0:
|
|
||||||
27:0:
|
|
||||||
28:0:
|
|
||||||
29:0:
|
|
||||||
30:0:
|
|
||||||
31:0:
|
|
||||||
</hash>
|
|
@ -1,9 +0,0 @@
|
|||||||
# wrap-around and delete below floor
|
|
||||||
a 2 ba
|
|
||||||
a 30 aa
|
|
||||||
a 30 ab
|
|
||||||
a 30 ac
|
|
||||||
a 30 ad
|
|
||||||
a 2 bb
|
|
||||||
|
|
||||||
d 30 ab
|
|
@ -1,34 +0,0 @@
|
|||||||
<hash elem=5>
|
|
||||||
0:30:ad
|
|
||||||
1:0:
|
|
||||||
2:2:ba
|
|
||||||
3:2:bb
|
|
||||||
4:0:
|
|
||||||
5:0:
|
|
||||||
6:0:
|
|
||||||
7:0:
|
|
||||||
8:0:
|
|
||||||
9:0:
|
|
||||||
10:0:
|
|
||||||
11:0:
|
|
||||||
12:0:
|
|
||||||
13:0:
|
|
||||||
14:0:
|
|
||||||
15:0:
|
|
||||||
16:0:
|
|
||||||
17:0:
|
|
||||||
18:0:
|
|
||||||
19:0:
|
|
||||||
20:0:
|
|
||||||
21:0:
|
|
||||||
22:0:
|
|
||||||
23:0:
|
|
||||||
24:0:
|
|
||||||
25:0:
|
|
||||||
26:0:
|
|
||||||
27:0:
|
|
||||||
28:0:
|
|
||||||
29:0:
|
|
||||||
30:30:aa
|
|
||||||
31:30:ac
|
|
||||||
</hash>
|
|
@ -1,10 +0,0 @@
|
|||||||
# delete in a discontinuous block
|
|
||||||
|
|
||||||
a 10 aa
|
|
||||||
a 11 ab
|
|
||||||
a 12 ac
|
|
||||||
a 14 ad
|
|
||||||
a 10 bc
|
|
||||||
|
|
||||||
d 11 ab
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
|||||||
<hash elem=4>
|
|
||||||
0:0:
|
|
||||||
1:0:
|
|
||||||
2:0:
|
|
||||||
3:0:
|
|
||||||
4:0:
|
|
||||||
5:0:
|
|
||||||
6:0:
|
|
||||||
7:0:
|
|
||||||
8:0:
|
|
||||||
9:0:
|
|
||||||
10:10:aa
|
|
||||||
11:10:bc
|
|
||||||
12:12:ac
|
|
||||||
13:0:
|
|
||||||
14:14:ad
|
|
||||||
15:0:
|
|
||||||
16:0:
|
|
||||||
17:0:
|
|
||||||
18:0:
|
|
||||||
19:0:
|
|
||||||
20:0:
|
|
||||||
21:0:
|
|
||||||
22:0:
|
|
||||||
23:0:
|
|
||||||
24:0:
|
|
||||||
25:0:
|
|
||||||
26:0:
|
|
||||||
27:0:
|
|
||||||
28:0:
|
|
||||||
29:0:
|
|
||||||
30:0:
|
|
||||||
31:0:
|
|
||||||
</hash>
|
|
@ -1,11 +0,0 @@
|
|||||||
# Delete an unexisting element. And on an empty hash
|
|
||||||
|
|
||||||
a 10 aa
|
|
||||||
|
|
||||||
d 10 ab
|
|
||||||
d 12 bc
|
|
||||||
|
|
||||||
# Empty for real
|
|
||||||
d 10 aa
|
|
||||||
|
|
||||||
d 10 aa
|
|
@ -1,34 +0,0 @@
|
|||||||
<hash elem=0>
|
|
||||||
0:0:
|
|
||||||
1:0:
|
|
||||||
2:0:
|
|
||||||
3:0:
|
|
||||||
4:0:
|
|
||||||
5:0:
|
|
||||||
6:0:
|
|
||||||
7:0:
|
|
||||||
8:0:
|
|
||||||
9:0:
|
|
||||||
10:0:
|
|
||||||
11:0:
|
|
||||||
12:0:
|
|
||||||
13:0:
|
|
||||||
14:0:
|
|
||||||
15:0:
|
|
||||||
16:0:
|
|
||||||
17:0:
|
|
||||||
18:0:
|
|
||||||
19:0:
|
|
||||||
20:0:
|
|
||||||
21:0:
|
|
||||||
22:0:
|
|
||||||
23:0:
|
|
||||||
24:0:
|
|
||||||
25:0:
|
|
||||||
26:0:
|
|
||||||
27:0:
|
|
||||||
28:0:
|
|
||||||
29:0:
|
|
||||||
30:0:
|
|
||||||
31:0:
|
|
||||||
</hash>
|
|
@ -1,39 +0,0 @@
|
|||||||
# delete on a full hash
|
|
||||||
|
|
||||||
# First, fill the hash :-)
|
|
||||||
|
|
||||||
a 0 aa
|
|
||||||
a 1 ab
|
|
||||||
a 2 ac
|
|
||||||
a 3 ad
|
|
||||||
a 4 ae
|
|
||||||
a 5 af
|
|
||||||
a 6 ag
|
|
||||||
a 7 ah
|
|
||||||
a 8 ai
|
|
||||||
a 9 af
|
|
||||||
a 10 ba
|
|
||||||
a 11 bb
|
|
||||||
a 12 bc
|
|
||||||
a 13 bd
|
|
||||||
a 14 be
|
|
||||||
a 15 bf
|
|
||||||
a 16 bg
|
|
||||||
a 17 bh
|
|
||||||
a 18 bi
|
|
||||||
a 19 bj
|
|
||||||
a 20 ca
|
|
||||||
a 21 cb
|
|
||||||
a 22 cd
|
|
||||||
a 23 ce
|
|
||||||
a 24 cf
|
|
||||||
a 25 cg
|
|
||||||
a 26 ch
|
|
||||||
a 27 ci
|
|
||||||
a 28 cj
|
|
||||||
a 29 ck
|
|
||||||
a 30 da
|
|
||||||
a 31 db
|
|
||||||
|
|
||||||
|
|
||||||
d 21 cb
|
|
@ -1,34 +0,0 @@
|
|||||||
<hash elem=31>
|
|
||||||
0:0:aa
|
|
||||||
1:1:ab
|
|
||||||
2:2:ac
|
|
||||||
3:3:ad
|
|
||||||
4:4:ae
|
|
||||||
5:5:af
|
|
||||||
6:6:ag
|
|
||||||
7:7:ah
|
|
||||||
8:8:ai
|
|
||||||
9:9:af
|
|
||||||
10:10:ba
|
|
||||||
11:11:bb
|
|
||||||
12:12:bc
|
|
||||||
13:13:bd
|
|
||||||
14:14:be
|
|
||||||
15:15:bf
|
|
||||||
16:16:bg
|
|
||||||
17:17:bh
|
|
||||||
18:18:bi
|
|
||||||
19:19:bj
|
|
||||||
20:20:ca
|
|
||||||
21:0:
|
|
||||||
22:22:cd
|
|
||||||
23:23:ce
|
|
||||||
24:24:cf
|
|
||||||
25:25:cg
|
|
||||||
26:26:ch
|
|
||||||
27:27:ci
|
|
||||||
28:28:cj
|
|
||||||
29:29:ck
|
|
||||||
30:30:da
|
|
||||||
31:31:db
|
|
||||||
</hash>
|
|
@ -1,10 +0,0 @@
|
|||||||
# Delete inside a block with something discontinuous
|
|
||||||
|
|
||||||
a 10 aa
|
|
||||||
a 10 ab
|
|
||||||
a 12 ac
|
|
||||||
a 13 ad
|
|
||||||
a 14 ae
|
|
||||||
|
|
||||||
# ab shifts, ac and next doesn't
|
|
||||||
d 10 aa
|
|
@ -1,34 +0,0 @@
|
|||||||
<hash elem=4>
|
|
||||||
0:0:
|
|
||||||
1:0:
|
|
||||||
2:0:
|
|
||||||
3:0:
|
|
||||||
4:0:
|
|
||||||
5:0:
|
|
||||||
6:0:
|
|
||||||
7:0:
|
|
||||||
8:0:
|
|
||||||
9:0:
|
|
||||||
10:10:ab
|
|
||||||
11:0:
|
|
||||||
12:12:ac
|
|
||||||
13:13:ad
|
|
||||||
14:14:ae
|
|
||||||
15:0:
|
|
||||||
16:0:
|
|
||||||
17:0:
|
|
||||||
18:0:
|
|
||||||
19:0:
|
|
||||||
20:0:
|
|
||||||
21:0:
|
|
||||||
22:0:
|
|
||||||
23:0:
|
|
||||||
24:0:
|
|
||||||
25:0:
|
|
||||||
26:0:
|
|
||||||
27:0:
|
|
||||||
28:0:
|
|
||||||
29:0:
|
|
||||||
30:0:
|
|
||||||
31:0:
|
|
||||||
</hash>
|
|
@ -1,8 +0,0 @@
|
|||||||
# Basic delete when wrapping, between wrap and floor
|
|
||||||
a 30 aa
|
|
||||||
a 30 ab
|
|
||||||
a 30 ac
|
|
||||||
a 30 ba
|
|
||||||
a 30 bb
|
|
||||||
|
|
||||||
d 30 ac
|
|
@ -1,34 +0,0 @@
|
|||||||
<hash elem=4>
|
|
||||||
0:30:ba
|
|
||||||
1:30:bb
|
|
||||||
2:0:
|
|
||||||
3:0:
|
|
||||||
4:0:
|
|
||||||
5:0:
|
|
||||||
6:0:
|
|
||||||
7:0:
|
|
||||||
8:0:
|
|
||||||
9:0:
|
|
||||||
10:0:
|
|
||||||
11:0:
|
|
||||||
12:0:
|
|
||||||
13:0:
|
|
||||||
14:0:
|
|
||||||
15:0:
|
|
||||||
16:0:
|
|
||||||
17:0:
|
|
||||||
18:0:
|
|
||||||
19:0:
|
|
||||||
20:0:
|
|
||||||
21:0:
|
|
||||||
22:0:
|
|
||||||
23:0:
|
|
||||||
24:0:
|
|
||||||
25:0:
|
|
||||||
26:0:
|
|
||||||
27:0:
|
|
||||||
28:0:
|
|
||||||
29:0:
|
|
||||||
30:30:aa
|
|
||||||
31:30:ab
|
|
||||||
</hash>
|
|
@ -1,10 +0,0 @@
|
|||||||
# Delete inside a block with wrapping, with something after
|
|
||||||
|
|
||||||
a 30 aa
|
|
||||||
a 30 ab
|
|
||||||
a 30 ac
|
|
||||||
a 1 ad
|
|
||||||
a 3 ae
|
|
||||||
|
|
||||||
# shift ad but not ae
|
|
||||||
d 14 ae
|
|
@ -1,34 +0,0 @@
|
|||||||
<hash elem=5>
|
|
||||||
0:30:ac
|
|
||||||
1:1:ad
|
|
||||||
2:0:
|
|
||||||
3:3:ae
|
|
||||||
4:0:
|
|
||||||
5:0:
|
|
||||||
6:0:
|
|
||||||
7:0:
|
|
||||||
8:0:
|
|
||||||
9:0:
|
|
||||||
10:0:
|
|
||||||
11:0:
|
|
||||||
12:0:
|
|
||||||
13:0:
|
|
||||||
14:0:
|
|
||||||
15:0:
|
|
||||||
16:0:
|
|
||||||
17:0:
|
|
||||||
18:0:
|
|
||||||
19:0:
|
|
||||||
20:0:
|
|
||||||
21:0:
|
|
||||||
22:0:
|
|
||||||
23:0:
|
|
||||||
24:0:
|
|
||||||
25:0:
|
|
||||||
26:0:
|
|
||||||
27:0:
|
|
||||||
28:0:
|
|
||||||
29:0:
|
|
||||||
30:30:aa
|
|
||||||
31:30:ab
|
|
||||||
</hash>
|
|
@ -1,8 +0,0 @@
|
|||||||
# delete before wrap
|
|
||||||
a 30 aa
|
|
||||||
a 30 ab
|
|
||||||
a 30 ac
|
|
||||||
a 30 ad
|
|
||||||
|
|
||||||
# shift ac and ad
|
|
||||||
d 30 ab
|
|
@ -1,34 +0,0 @@
|
|||||||
<hash elem=3>
|
|
||||||
0:30:ad
|
|
||||||
1:0:
|
|
||||||
2:0:
|
|
||||||
3:0:
|
|
||||||
4:0:
|
|
||||||
5:0:
|
|
||||||
6:0:
|
|
||||||
7:0:
|
|
||||||
8:0:
|
|
||||||
9:0:
|
|
||||||
10:0:
|
|
||||||
11:0:
|
|
||||||
12:0:
|
|
||||||
13:0:
|
|
||||||
14:0:
|
|
||||||
15:0:
|
|
||||||
16:0:
|
|
||||||
17:0:
|
|
||||||
18:0:
|
|
||||||
19:0:
|
|
||||||
20:0:
|
|
||||||
21:0:
|
|
||||||
22:0:
|
|
||||||
23:0:
|
|
||||||
24:0:
|
|
||||||
25:0:
|
|
||||||
26:0:
|
|
||||||
27:0:
|
|
||||||
28:0:
|
|
||||||
29:0:
|
|
||||||
30:30:aa
|
|
||||||
31:30:ac
|
|
||||||
</hash>
|
|
@ -1,11 +0,0 @@
|
|||||||
# Delete with wrapping in discontinuous group
|
|
||||||
|
|
||||||
a 30 aa
|
|
||||||
a 30 ab
|
|
||||||
a 30 ac
|
|
||||||
a 31 ad
|
|
||||||
a 2 ba
|
|
||||||
a 3 bb
|
|
||||||
|
|
||||||
# shift ac and ad but not ba and bb
|
|
||||||
d 30 ab
|
|
@ -1,34 +0,0 @@
|
|||||||
<hash elem=5>
|
|
||||||
0:31:ad
|
|
||||||
1:0:
|
|
||||||
2:2:ba
|
|
||||||
3:3:bb
|
|
||||||
4:0:
|
|
||||||
5:0:
|
|
||||||
6:0:
|
|
||||||
7:0:
|
|
||||||
8:0:
|
|
||||||
9:0:
|
|
||||||
10:0:
|
|
||||||
11:0:
|
|
||||||
12:0:
|
|
||||||
13:0:
|
|
||||||
14:0:
|
|
||||||
15:0:
|
|
||||||
16:0:
|
|
||||||
17:0:
|
|
||||||
18:0:
|
|
||||||
19:0:
|
|
||||||
20:0:
|
|
||||||
21:0:
|
|
||||||
22:0:
|
|
||||||
23:0:
|
|
||||||
24:0:
|
|
||||||
25:0:
|
|
||||||
26:0:
|
|
||||||
27:0:
|
|
||||||
28:0:
|
|
||||||
29:0:
|
|
||||||
30:30:aa
|
|
||||||
31:30:ac
|
|
||||||
</hash>
|
|
BIN
hashtest/htest
BIN
hashtest/htest
Binary file not shown.
109
hashtest/htest.c
109
hashtest/htest.c
@ -1,109 +0,0 @@
|
|||||||
/* Wee testing program from the hash code:
|
|
||||||
* htest <script> <dump>
|
|
||||||
*
|
|
||||||
* scripts are a list of operations:
|
|
||||||
* a $index $string
|
|
||||||
* => add an element at specified index
|
|
||||||
* d $index $string
|
|
||||||
* => remove an element
|
|
||||||
* s $index $string
|
|
||||||
* => prints the actual element index, if it's there
|
|
||||||
*
|
|
||||||
* The hash is dumped to the dump file at each iteration.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
|
|
||||||
/* tests have been written for a hash that holds 32 items */
|
|
||||||
#define HASH_SIZE 32
|
|
||||||
|
|
||||||
#define STR_LENGTH 16
|
|
||||||
struct hash_item {
|
|
||||||
int wanted_index;
|
|
||||||
char str[STR_LENGTH];
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct hash_item* hash_item;
|
|
||||||
|
|
||||||
#include "../hash.h"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static int cmp_item(hash_item item1, hash_item item2)
|
|
||||||
{
|
|
||||||
return strcmp(item1->str, item2->str);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int hash_make_key(hash_item item)
|
|
||||||
{
|
|
||||||
return item->wanted_index;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void htest_next_key(FILE* f, char* action, int* key, char str[STR_LENGTH])
|
|
||||||
{
|
|
||||||
|
|
||||||
int res = 0;
|
|
||||||
while ((res != 3) && (res != EOF))
|
|
||||||
res = fscanf(f, "%c %d %s\n", action, key, str);
|
|
||||||
if (res == EOF) exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char* argv[])
|
|
||||||
{
|
|
||||||
hash* h = hash_init(HASH_SIZE, &hash_make_key, &cmp_item);
|
|
||||||
char action;
|
|
||||||
hash_item item;
|
|
||||||
int line = 0;
|
|
||||||
FILE* f;
|
|
||||||
|
|
||||||
if (argc != 3) {
|
|
||||||
fprintf(stderr, "Usage: htest <script file> <dump file>\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
char* script_file = argv[1];
|
|
||||||
char* dump_file = argv[2];
|
|
||||||
f = fopen(argv[1], "r");
|
|
||||||
if (!f) {
|
|
||||||
perror(script_file);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
item= malloc(sizeof(*item));
|
|
||||||
action = ' ';
|
|
||||||
|
|
||||||
line++;
|
|
||||||
htest_next_key(f, &action, &item->wanted_index, item->str);
|
|
||||||
fprintf(stderr, "action %d: %c %d %s\n", line, action, item->wanted_index, item->str);
|
|
||||||
|
|
||||||
switch (action) {
|
|
||||||
case 'a': /* add */
|
|
||||||
fprintf(stderr, "inserting [%s] at %d\n", item->str, item->wanted_index);
|
|
||||||
hash_insert(h, item);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'd': /* del */
|
|
||||||
fprintf(stderr, "removing [%s] at %d\n", item->str, item->wanted_index);
|
|
||||||
hash_remove(h, item);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 's': /* search */
|
|
||||||
fprintf(stderr, "searching\n");
|
|
||||||
struct hash_item* found = hash_find(h, item);
|
|
||||||
fprintf(stderr, "searching %d[%s]: %p\n", item->wanted_index, item->str, found);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'q': /* quit */
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
hash_dump(h, dump_file);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
# Basic insertions
|
|
||||||
a 10 aa
|
|
||||||
a 10 ab
|
|
||||||
a 10 ac
|
|
||||||
a 20 ba
|
|
||||||
a 21 bb
|
|
@ -1,34 +0,0 @@
|
|||||||
<hash elem=5>
|
|
||||||
0:0:
|
|
||||||
1:0:
|
|
||||||
2:0:
|
|
||||||
3:0:
|
|
||||||
4:0:
|
|
||||||
5:0:
|
|
||||||
6:0:
|
|
||||||
7:0:
|
|
||||||
8:0:
|
|
||||||
9:0:
|
|
||||||
10:10:aa
|
|
||||||
11:10:ab
|
|
||||||
12:10:ac
|
|
||||||
13:0:
|
|
||||||
14:0:
|
|
||||||
15:0:
|
|
||||||
16:0:
|
|
||||||
17:0:
|
|
||||||
18:0:
|
|
||||||
19:0:
|
|
||||||
20:20:ba
|
|
||||||
21:21:bb
|
|
||||||
22:0:
|
|
||||||
23:0:
|
|
||||||
24:0:
|
|
||||||
25:0:
|
|
||||||
26:0:
|
|
||||||
27:0:
|
|
||||||
28:0:
|
|
||||||
29:0:
|
|
||||||
30:0:
|
|
||||||
31:0:
|
|
||||||
</hash>
|
|
@ -1,8 +0,0 @@
|
|||||||
# insert and bubble with single empty space
|
|
||||||
|
|
||||||
a 10 aa
|
|
||||||
a 11 ab
|
|
||||||
a 12 ac
|
|
||||||
a 14 ad
|
|
||||||
a 10 bc
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
|||||||
<hash elem=5>
|
|
||||||
0:0:
|
|
||||||
1:0:
|
|
||||||
2:0:
|
|
||||||
3:0:
|
|
||||||
4:0:
|
|
||||||
5:0:
|
|
||||||
6:0:
|
|
||||||
7:0:
|
|
||||||
8:0:
|
|
||||||
9:0:
|
|
||||||
10:10:aa
|
|
||||||
11:10:bc
|
|
||||||
12:11:ab
|
|
||||||
13:12:ac
|
|
||||||
14:14:ad
|
|
||||||
15:0:
|
|
||||||
16:0:
|
|
||||||
17:0:
|
|
||||||
18:0:
|
|
||||||
19:0:
|
|
||||||
20:0:
|
|
||||||
21:0:
|
|
||||||
22:0:
|
|
||||||
23:0:
|
|
||||||
24:0:
|
|
||||||
25:0:
|
|
||||||
26:0:
|
|
||||||
27:0:
|
|
||||||
28:0:
|
|
||||||
29:0:
|
|
||||||
30:0:
|
|
||||||
31:0:
|
|
||||||
</hash>
|
|
@ -1,40 +0,0 @@
|
|||||||
# Insert on a full hash
|
|
||||||
|
|
||||||
# First, fill the hash :-)
|
|
||||||
|
|
||||||
a 0 aa
|
|
||||||
a 1 ab
|
|
||||||
a 2 ac
|
|
||||||
a 3 ad
|
|
||||||
a 4 ae
|
|
||||||
a 5 af
|
|
||||||
a 6 ag
|
|
||||||
a 7 ah
|
|
||||||
a 8 ai
|
|
||||||
a 9 af
|
|
||||||
a 10 ba
|
|
||||||
a 11 bb
|
|
||||||
a 12 bc
|
|
||||||
a 13 bd
|
|
||||||
a 14 be
|
|
||||||
a 15 bf
|
|
||||||
a 16 bg
|
|
||||||
a 17 bh
|
|
||||||
a 18 bi
|
|
||||||
a 19 bj
|
|
||||||
a 20 ca
|
|
||||||
a 21 cb
|
|
||||||
a 22 cd
|
|
||||||
a 23 ce
|
|
||||||
a 24 cf
|
|
||||||
a 25 cg
|
|
||||||
a 26 ch
|
|
||||||
a 27 ci
|
|
||||||
a 28 cj
|
|
||||||
a 29 ck
|
|
||||||
a 30 da
|
|
||||||
a 31 db
|
|
||||||
|
|
||||||
# it's full!
|
|
||||||
a 20 zz
|
|
||||||
a 31 za
|
|
@ -1,34 +0,0 @@
|
|||||||
<hash elem=32>
|
|
||||||
0:0:aa
|
|
||||||
1:1:ab
|
|
||||||
2:2:ac
|
|
||||||
3:3:ad
|
|
||||||
4:4:ae
|
|
||||||
5:5:af
|
|
||||||
6:6:ag
|
|
||||||
7:7:ah
|
|
||||||
8:8:ai
|
|
||||||
9:9:af
|
|
||||||
10:10:ba
|
|
||||||
11:11:bb
|
|
||||||
12:12:bc
|
|
||||||
13:13:bd
|
|
||||||
14:14:be
|
|
||||||
15:15:bf
|
|
||||||
16:16:bg
|
|
||||||
17:17:bh
|
|
||||||
18:18:bi
|
|
||||||
19:19:bj
|
|
||||||
20:20:ca
|
|
||||||
21:21:cb
|
|
||||||
22:22:cd
|
|
||||||
23:23:ce
|
|
||||||
24:24:cf
|
|
||||||
25:25:cg
|
|
||||||
26:26:ch
|
|
||||||
27:27:ci
|
|
||||||
28:28:cj
|
|
||||||
29:29:ck
|
|
||||||
30:30:da
|
|
||||||
31:31:db
|
|
||||||
</hash>
|
|
@ -1,7 +0,0 @@
|
|||||||
# wrap-around and insert at full floor
|
|
||||||
a 2 ba
|
|
||||||
a 30 aa
|
|
||||||
a 30 ab
|
|
||||||
a 30 ac
|
|
||||||
a 30 ad
|
|
||||||
a 2 bb
|
|
@ -1,34 +0,0 @@
|
|||||||
<hash elem=6>
|
|
||||||
0:30:ac
|
|
||||||
1:30:ad
|
|
||||||
2:2:ba
|
|
||||||
3:2:bb
|
|
||||||
4:0:
|
|
||||||
5:0:
|
|
||||||
6:0:
|
|
||||||
7:0:
|
|
||||||
8:0:
|
|
||||||
9:0:
|
|
||||||
10:0:
|
|
||||||
11:0:
|
|
||||||
12:0:
|
|
||||||
13:0:
|
|
||||||
14:0:
|
|
||||||
15:0:
|
|
||||||
16:0:
|
|
||||||
17:0:
|
|
||||||
18:0:
|
|
||||||
19:0:
|
|
||||||
20:0:
|
|
||||||
21:0:
|
|
||||||
22:0:
|
|
||||||
23:0:
|
|
||||||
24:0:
|
|
||||||
25:0:
|
|
||||||
26:0:
|
|
||||||
27:0:
|
|
||||||
28:0:
|
|
||||||
29:0:
|
|
||||||
30:30:aa
|
|
||||||
31:30:ab
|
|
||||||
</hash>
|
|
@ -1,7 +0,0 @@
|
|||||||
# wrap-around and insert above floor
|
|
||||||
a 30 aa
|
|
||||||
a 30 ab
|
|
||||||
a 30 ac
|
|
||||||
a 30 ad
|
|
||||||
a 0 ba
|
|
||||||
a 0 bb
|
|
@ -1,34 +0,0 @@
|
|||||||
<hash elem=6>
|
|
||||||
0:30:ac
|
|
||||||
1:30:ad
|
|
||||||
2:0:ba
|
|
||||||
3:0:bb
|
|
||||||
4:0:
|
|
||||||
5:0:
|
|
||||||
6:0:
|
|
||||||
7:0:
|
|
||||||
8:0:
|
|
||||||
9:0:
|
|
||||||
10:0:
|
|
||||||
11:0:
|
|
||||||
12:0:
|
|
||||||
13:0:
|
|
||||||
14:0:
|
|
||||||
15:0:
|
|
||||||
16:0:
|
|
||||||
17:0:
|
|
||||||
18:0:
|
|
||||||
19:0:
|
|
||||||
20:0:
|
|
||||||
21:0:
|
|
||||||
22:0:
|
|
||||||
23:0:
|
|
||||||
24:0:
|
|
||||||
25:0:
|
|
||||||
26:0:
|
|
||||||
27:0:
|
|
||||||
28:0:
|
|
||||||
29:0:
|
|
||||||
30:30:aa
|
|
||||||
31:30:ab
|
|
||||||
</hash>
|
|
@ -1,41 +0,0 @@
|
|||||||
#! /usr/bin/perl
|
|
||||||
|
|
||||||
# Creates a script of random accesses and deletes
|
|
||||||
|
|
||||||
use strict;
|
|
||||||
|
|
||||||
|
|
||||||
my $i = 0;
|
|
||||||
sub mkstr {
|
|
||||||
$i++;
|
|
||||||
return chr(ord('a') + ($i / 26) % 26) . chr(ord('a') + $i % 26);
|
|
||||||
}
|
|
||||||
|
|
||||||
my @elems;
|
|
||||||
|
|
||||||
|
|
||||||
sub add_elem {
|
|
||||||
my $val = int(rand(32));
|
|
||||||
my $str = mkstr($val);
|
|
||||||
push @elems, "$val $str";
|
|
||||||
print "a $val $str\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
sub del_elem {
|
|
||||||
my $remove = splice(@elems, rand @elems, 1);
|
|
||||||
print "d $remove\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
if (@elems < 5) {
|
|
||||||
add_elem;
|
|
||||||
} elsif (@elems > 28) {
|
|
||||||
del_elem;
|
|
||||||
} else {
|
|
||||||
if (rand() < .5) {
|
|
||||||
add_elem;
|
|
||||||
} else {
|
|
||||||
del_elem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
30
hashtest/run
30
hashtest/run
@ -1,30 +0,0 @@
|
|||||||
#! /usr/bin/perl -w
|
|
||||||
|
|
||||||
# This runs all the tests.
|
|
||||||
|
|
||||||
# Tests scripts are in *.tst files.
|
|
||||||
# Corresponding output is put in *.out.
|
|
||||||
# Reference output is put in *.ref.
|
|
||||||
# Any discrepancy will be reported!
|
|
||||||
|
|
||||||
use strict;
|
|
||||||
|
|
||||||
my @res;
|
|
||||||
foreach my $fn (`ls *.tst`) {
|
|
||||||
chomp $fn;
|
|
||||||
my $cmd = "./htest $fn $fn.out";
|
|
||||||
print "$cmd\n";
|
|
||||||
`$cmd`;
|
|
||||||
my $res = system("diff -u $fn.ref $fn.out");
|
|
||||||
push @res, [$fn, ($res == 0 ? "OK" : "*KO*")];
|
|
||||||
}
|
|
||||||
|
|
||||||
format =
|
|
||||||
@<<<<<<<<<<<<<<<<<<<<<<<<<< @>>>
|
|
||||||
$_->[0], $_->[1]
|
|
||||||
.
|
|
||||||
|
|
||||||
|
|
||||||
#format_name STDOUT test_result;
|
|
||||||
map { write; } @res;
|
|
||||||
|
|
186
landlock.c
186
landlock.c
@ -1,186 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// close helper handle
|
|
||||||
close(fd);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int add_libs(int ruleset_fd)
|
|
||||||
{
|
|
||||||
/* 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... */
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int add_resolv(int ruleset_fd)
|
|
||||||
{
|
|
||||||
/* 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");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int add_libwrap(int ruleset_fd)
|
|
||||||
{
|
|
||||||
/* Files for libwrap */
|
|
||||||
#ifdef LIBWRAP
|
|
||||||
add_path_ro(ruleset_fd, LL_FILE, "/etc/hosts.allow");
|
|
||||||
add_path_ro(ruleset_fd, LL_FILE, "/etc/hosts.deny");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Add all the paths we need */
|
|
||||||
add_libs(ruleset_fd);
|
|
||||||
add_resolv(ruleset_fd);
|
|
||||||
add_libwrap(ruleset_fd);
|
|
||||||
|
|
||||||
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 */
|
|
211
log.c
211
log.c
@ -1,211 +0,0 @@
|
|||||||
/*
|
|
||||||
# log: processing of all outgoing messages
|
|
||||||
#
|
|
||||||
# Copyright (C) 2007-2021 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
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#define SYSLOG_NAMES
|
|
||||||
#define _GNU_SOURCE
|
|
||||||
#include <errno.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include "sslh-conf.h"
|
|
||||||
#include "common.h"
|
|
||||||
#include "log.h"
|
|
||||||
|
|
||||||
msg_info msg_config = {
|
|
||||||
LOG_INFO,
|
|
||||||
&cfg.verbose_config
|
|
||||||
};
|
|
||||||
|
|
||||||
msg_info msg_config_error = {
|
|
||||||
LOG_ERR,
|
|
||||||
&cfg.verbose_config_error
|
|
||||||
};
|
|
||||||
|
|
||||||
msg_info msg_fd = {
|
|
||||||
LOG_DEBUG,
|
|
||||||
&cfg.verbose_fd
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Internal errors: inconsistent states, impossible values, things that should never happen, and are therefore the sign of memory corruption: hence the LOG_CRIT */
|
|
||||||
msg_info msg_int_error = {
|
|
||||||
LOG_CRIT,
|
|
||||||
&cfg.verbose_system_error
|
|
||||||
};
|
|
||||||
|
|
||||||
/* System errors: when the system around us fails us: memory allocation, fork, ... */
|
|
||||||
msg_info msg_system_error = {
|
|
||||||
LOG_ERR,
|
|
||||||
&cfg.verbose_system_error
|
|
||||||
};
|
|
||||||
|
|
||||||
msg_info msg_packets = {
|
|
||||||
LOG_INFO,
|
|
||||||
&cfg.verbose_packets
|
|
||||||
};
|
|
||||||
|
|
||||||
/* additional info when attempting outgoing connections */
|
|
||||||
msg_info msg_connections_try = {
|
|
||||||
LOG_DEBUG,
|
|
||||||
&cfg.verbose_connections_try
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Connection information and failures (e.g. forbidden by policy) */
|
|
||||||
msg_info msg_connections = {
|
|
||||||
LOG_INFO,
|
|
||||||
&cfg.verbose_connections
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Connection failures, e.g. target server not present */
|
|
||||||
msg_info msg_connections_error = {
|
|
||||||
LOG_ERR,
|
|
||||||
&cfg.verbose_connections_error
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/* comment the probing process */
|
|
||||||
msg_info msg_probe_info = {
|
|
||||||
LOG_INFO,
|
|
||||||
&cfg.verbose_probe_info
|
|
||||||
};
|
|
||||||
|
|
||||||
/* probing errors, e.g. inconsistent data in connections */
|
|
||||||
msg_info msg_probe_error = {
|
|
||||||
LOG_ERR,
|
|
||||||
&cfg.verbose_probe_error
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Bitmasks in verbose-* values */
|
|
||||||
#define MSG_STDOUT 1
|
|
||||||
#define MSG_SYSLOG 2
|
|
||||||
#define MSG_FILE 4
|
|
||||||
|
|
||||||
static FILE* logfile_fp = NULL;
|
|
||||||
|
|
||||||
/* Prints a message to stderr and/or syslog if appropriate */
|
|
||||||
void print_message(msg_info info, const char* str, ...)
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
|
|
||||||
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);
|
|
||||||
vsyslog(info.log_level, str, ap);
|
|
||||||
va_end(ap);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*info.verbose & MSG_FILE && logfile_fp != NULL) {
|
|
||||||
va_start(ap, str);
|
|
||||||
vfprintf(logfile_fp, str, ap);
|
|
||||||
fflush(logfile_fp);
|
|
||||||
va_end(ap);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int do_syslog = 1; /* Should we syslog? controled by syslog_facility = "none" */
|
|
||||||
|
|
||||||
/* Open syslog connection with appropriate banner;
|
|
||||||
* banner is made up of basename(bin_name)+"[pid]" */
|
|
||||||
void setup_syslog(const char* bin_name) {
|
|
||||||
char *name1, *name2;
|
|
||||||
int res, fn;
|
|
||||||
|
|
||||||
if (!strcmp(cfg.syslog_facility, "none")) {
|
|
||||||
do_syslog = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
name1 = strdup(bin_name);
|
|
||||||
res = asprintf(&name2, "%s[%d]", basename(name1), getpid());
|
|
||||||
CHECK_RES_DIE(res, "asprintf");
|
|
||||||
|
|
||||||
for (fn = 0; facilitynames[fn].c_val != -1; fn++)
|
|
||||||
if (strcmp(facilitynames[fn].c_name, cfg.syslog_facility) == 0)
|
|
||||||
break;
|
|
||||||
if (facilitynames[fn].c_val == -1) {
|
|
||||||
fprintf(stderr, "Unknown facility %s\n", cfg.syslog_facility);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
openlog(name2, LOG_CONS, facilitynames[fn].c_val);
|
|
||||||
free(name1);
|
|
||||||
/* Don't free name2, as openlog(3) uses it (at least in glibc) */
|
|
||||||
}
|
|
||||||
|
|
||||||
void setup_logfile()
|
|
||||||
{
|
|
||||||
if (cfg.logfile == NULL)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
logfile_fp = fopen(cfg.logfile, "a");
|
|
||||||
if (logfile_fp == NULL)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Could not open logfile %s for writing: %s\n", cfg.logfile, strerror(errno));
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void close_logfile()
|
|
||||||
{
|
|
||||||
if (logfile_fp != NULL)
|
|
||||||
{
|
|
||||||
fclose(logfile_fp);
|
|
||||||
logfile_fp = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* syslogs who connected to where
|
|
||||||
* desc: string description of the connection. if NULL, log_connection will
|
|
||||||
* manage on its own
|
|
||||||
* cnx: connection descriptor
|
|
||||||
* */
|
|
||||||
void log_connection(struct connection_desc* desc, const struct connection *cnx)
|
|
||||||
{
|
|
||||||
struct connection_desc d;
|
|
||||||
|
|
||||||
if (cnx->proto->log_level < 1)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!desc) {
|
|
||||||
desc = &d;
|
|
||||||
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",
|
|
||||||
cnx->proto->name,
|
|
||||||
desc->peer,
|
|
||||||
desc->service,
|
|
||||||
desc->local,
|
|
||||||
desc->target);
|
|
||||||
}
|
|
36
log.h
36
log.h
@ -1,36 +0,0 @@
|
|||||||
#ifndef LOG_H
|
|
||||||
#define LOG_H
|
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
|
|
||||||
void setup_syslog(const char* bin_name);
|
|
||||||
|
|
||||||
void setup_logfile();
|
|
||||||
|
|
||||||
void close_logfile();
|
|
||||||
|
|
||||||
void log_connection(struct connection_desc* desc, const struct connection *cnx);
|
|
||||||
|
|
||||||
typedef struct s_msg_info{
|
|
||||||
int log_level;
|
|
||||||
int *verbose;
|
|
||||||
} msg_info;
|
|
||||||
|
|
||||||
void print_message(msg_info info, const char* str, ...);
|
|
||||||
extern msg_info msg_config;
|
|
||||||
extern msg_info msg_config_error;
|
|
||||||
|
|
||||||
extern msg_info msg_fd;
|
|
||||||
extern msg_info msg_packets;
|
|
||||||
|
|
||||||
extern msg_info msg_int_error;
|
|
||||||
extern msg_info msg_system_error;
|
|
||||||
|
|
||||||
extern msg_info msg_connections_try;
|
|
||||||
extern msg_info msg_connections_error;
|
|
||||||
extern msg_info msg_connections;
|
|
||||||
|
|
||||||
extern msg_info msg_probe_info;
|
|
||||||
extern msg_info msg_probe_error;
|
|
||||||
|
|
||||||
#endif /* LOG_H */
|
|
480
probe.c
480
probe.c
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
# probe.c: Code for probing protocols
|
# probe.c: Code for probing protocols
|
||||||
#
|
#
|
||||||
# Copyright (C) 2007-2021 Yves Rutschle
|
# Copyright (C) 2007-2012 Yves Rutschle
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it
|
# This program is free software; you can redistribute it
|
||||||
# and/or modify it under the terms of the GNU General Public
|
# and/or modify it under the terms of the GNU General Public
|
||||||
@ -21,51 +21,38 @@
|
|||||||
|
|
||||||
#define _GNU_SOURCE
|
#define _GNU_SOURCE
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#ifdef ENABLE_REGEX
|
#include <regex.h>
|
||||||
#define PCRE2_CODE_UNIT_WIDTH 8
|
|
||||||
#include <pcre2.h>
|
|
||||||
#endif
|
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include "probe.h"
|
#include "probe.h"
|
||||||
#include "log.h"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static int is_ssh_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_item*);
|
static int is_ssh_protocol(const char *p, int len, struct proto*);
|
||||||
static int is_openvpn_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_item*);
|
static int is_openvpn_protocol(const char *p, int len, struct proto*);
|
||||||
static int is_wireguard_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_item*);
|
static int is_tinc_protocol(const char *p, int len, struct proto*);
|
||||||
static int is_tinc_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_item*);
|
static int is_xmpp_protocol(const char *p, int len, struct proto*);
|
||||||
static int is_xmpp_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_item*);
|
static int is_http_protocol(const char *p, int len, struct proto*);
|
||||||
static int is_http_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_item*);
|
static int is_tls_protocol(const char *p, int len, struct proto*);
|
||||||
static int is_tls_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_item*);
|
static int is_true(const char *p, int len, struct proto* proto) { return 1; }
|
||||||
static int is_adb_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_item*);
|
|
||||||
static int is_socks5_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_item*);
|
|
||||||
static int is_syslog_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_item*);
|
|
||||||
static int is_teamspeak_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_item*);
|
|
||||||
static int is_msrdp_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_item*);
|
|
||||||
static int is_true(const char *p, ssize_t len, struct sslhcfg_protocols_item* proto) { return 1; }
|
|
||||||
|
|
||||||
/* Table of protocols that have a built-in probe
|
/* Table of protocols that have a built-in probe
|
||||||
*/
|
*/
|
||||||
static struct protocol_probe_desc builtins[] = {
|
static struct proto builtins[] = {
|
||||||
/* description probe */
|
/* description service saddr probe */
|
||||||
{ "ssh", is_ssh_protocol},
|
{ "ssh", "sshd", NULL, is_ssh_protocol},
|
||||||
{ "openvpn", is_openvpn_protocol },
|
{ "openvpn", NULL, NULL, is_openvpn_protocol },
|
||||||
{ "wireguard", is_wireguard_protocol },
|
{ "tinc", NULL, NULL, is_tinc_protocol },
|
||||||
{ "tinc", is_tinc_protocol },
|
{ "xmpp", NULL, NULL, is_xmpp_protocol },
|
||||||
{ "xmpp", is_xmpp_protocol },
|
{ "http", NULL, NULL, is_http_protocol },
|
||||||
{ "http", is_http_protocol },
|
{ "ssl", NULL, NULL, is_tls_protocol },
|
||||||
{ "tls", is_tls_protocol },
|
{ "tls", NULL, NULL, is_tls_protocol },
|
||||||
{ "adb", is_adb_protocol },
|
{ "anyprot", NULL, NULL, is_true }
|
||||||
{ "socks5", is_socks5_protocol },
|
|
||||||
{ "syslog", is_syslog_protocol },
|
|
||||||
{ "teamspeak", is_teamspeak_protocol },
|
|
||||||
{ "msrdp", is_msrdp_protocol },
|
|
||||||
{ "anyprot", is_true }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* TODO I think this has to go */
|
static struct proto *protocols;
|
||||||
struct protocol_probe_desc* get_builtins(void) {
|
static char* on_timeout = "ssh";
|
||||||
|
|
||||||
|
struct proto* get_builtins(void) {
|
||||||
return builtins;
|
return builtins;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,59 +60,70 @@ int get_num_builtins(void) {
|
|||||||
return ARRAY_SIZE(builtins);
|
return ARRAY_SIZE(builtins);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Sets the protocol name to connect to in case of timeout */
|
||||||
|
void set_ontimeout(const char* name)
|
||||||
|
{
|
||||||
|
int res = asprintf(&on_timeout, "%s", name);
|
||||||
|
CHECK_RES_DIE(res, "asprintf");
|
||||||
|
}
|
||||||
|
|
||||||
/* Returns the protocol to connect to in case of timeout;
|
/* Returns the protocol to connect to in case of timeout;
|
||||||
* if not found, return the first protocol specified
|
* if not found, return the first protocol specified
|
||||||
*/
|
*/
|
||||||
struct sslhcfg_protocols_item* timeout_protocol(void)
|
struct proto* timeout_protocol(void)
|
||||||
{
|
{
|
||||||
int i;
|
struct proto* p = get_first_protocol();
|
||||||
for (i = 0; i < cfg.protocols_len; i++) {
|
for (; p && strcmp(p->description, on_timeout); p = p->next);
|
||||||
if (!strcmp(cfg.protocols[i].name, cfg.on_timeout)) return &cfg.protocols[i];
|
if (p) return p;
|
||||||
}
|
return get_first_protocol();
|
||||||
return &cfg.protocols[0];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* returns the first protocol (caller can then follow the *next pointers) */
|
||||||
|
struct proto* get_first_protocol(void)
|
||||||
|
{
|
||||||
|
return protocols;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_protocol_list(struct proto* prots)
|
||||||
|
{
|
||||||
|
protocols = prots;
|
||||||
|
}
|
||||||
|
|
||||||
/* From http://grapsus.net/blog/post/Hexadecimal-dump-in-C */
|
/* From http://grapsus.net/blog/post/Hexadecimal-dump-in-C */
|
||||||
#define HEXDUMP_COLS 16
|
#define HEXDUMP_COLS 16
|
||||||
void hexdump(msg_info msg_info, const char *mem, unsigned int len)
|
void hexdump(const char *mem, unsigned int len)
|
||||||
{
|
{
|
||||||
unsigned int i, j;
|
unsigned int i, j;
|
||||||
char str[10 + HEXDUMP_COLS * 4 + 2];
|
|
||||||
int c = 0; /* index in str */
|
|
||||||
|
|
||||||
for(i = 0; i < len + ((len % HEXDUMP_COLS) ? (HEXDUMP_COLS - len % HEXDUMP_COLS) : 0); i++)
|
for(i = 0; i < len + ((len % HEXDUMP_COLS) ? (HEXDUMP_COLS - len % HEXDUMP_COLS) : 0); i++)
|
||||||
{
|
{
|
||||||
/* print offset */
|
/* print offset */
|
||||||
if(i % HEXDUMP_COLS == 0)
|
if(i % HEXDUMP_COLS == 0)
|
||||||
c += sprintf(&str[c], "0x%06x: ", i);
|
printf("0x%06x: ", i);
|
||||||
|
|
||||||
/* print hex data */
|
/* print hex data */
|
||||||
if(i < len)
|
if(i < len)
|
||||||
c += sprintf(&str[c], "%02x ", 0xFF & mem[i]);
|
printf("%02x ", 0xFF & mem[i]);
|
||||||
else /* end of block, just aligning for ASCII dump */
|
else /* end of block, just aligning for ASCII dump */
|
||||||
c+= sprintf(&str[c], " ");
|
printf(" ");
|
||||||
|
|
||||||
/* print ASCII dump */
|
/* print ASCII dump */
|
||||||
if(i % HEXDUMP_COLS == (HEXDUMP_COLS - 1)) {
|
if(i % HEXDUMP_COLS == (HEXDUMP_COLS - 1)) {
|
||||||
for(j = i - (HEXDUMP_COLS - 1); j <= i; j++) {
|
for(j = i - (HEXDUMP_COLS - 1); j <= i; j++) {
|
||||||
if(j >= len) /* end of block, not really printing */
|
if(j >= len) /* end of block, not really printing */
|
||||||
str[c++] = ' ';
|
putchar(' ');
|
||||||
else if(isprint(mem[j])) /* printable char */
|
else if(isprint(mem[j])) /* printable char */
|
||||||
str[c++] = 0xFF & mem[j];
|
putchar(0xFF & mem[j]);
|
||||||
else /* other char */
|
else /* other char */
|
||||||
str[c++] = '.';
|
putchar('.');
|
||||||
}
|
}
|
||||||
str[c++] = '\n';
|
putchar('\n');
|
||||||
str[c++] = 0;
|
|
||||||
print_message(msg_info, "%s", str);
|
|
||||||
c = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Is the buffer the beginning of an SSH connection? */
|
/* Is the buffer the beginning of an SSH connection? */
|
||||||
static int is_ssh_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_item* proto)
|
static int is_ssh_protocol(const char *p, int len, struct proto *proto)
|
||||||
{
|
{
|
||||||
if (len < 4)
|
if (len < 4)
|
||||||
return PROBE_AGAIN;
|
return PROBE_AGAIN;
|
||||||
@ -143,73 +141,21 @@ static int is_ssh_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_
|
|||||||
* http://www.fengnet.com/book/vpns%20illustrated%20tunnels%20%20vpnsand%20ipsec/ch08lev1sec5.html
|
* http://www.fengnet.com/book/vpns%20illustrated%20tunnels%20%20vpnsand%20ipsec/ch08lev1sec5.html
|
||||||
* and OpenVPN ssl.c, ssl.h and options.c
|
* and OpenVPN ssl.c, ssl.h and options.c
|
||||||
*/
|
*/
|
||||||
#define OVPN_OPCODE_MASK 0xF8
|
static int is_openvpn_protocol (const char*p,int len, struct proto *proto)
|
||||||
#define OVPN_CONTROL_HARD_RESET_CLIENT_V1 (0x01 << 3)
|
|
||||||
#define OVPN_CONTROL_HARD_RESET_CLIENT_V2 (0x07 << 3)
|
|
||||||
#define OVPN_HMAC_128 16
|
|
||||||
#define OVPN_HMAC_160 20
|
|
||||||
#define OVPN_HARD_RESET_PACKET_ID_OFFSET(hmac_size) (9 + hmac_size)
|
|
||||||
static int is_openvpn_protocol (const char*p,ssize_t len, struct sslhcfg_protocols_item* proto)
|
|
||||||
{
|
{
|
||||||
int packet_len;
|
int packet_len;
|
||||||
|
|
||||||
if (proto->is_udp == 0)
|
if (len < 2)
|
||||||
{
|
return PROBE_AGAIN;
|
||||||
if (len < 2)
|
|
||||||
return PROBE_AGAIN;
|
|
||||||
|
|
||||||
packet_len = ntohs(*(uint16_t*)p);
|
packet_len = ntohs(*(uint16_t*)p);
|
||||||
return packet_len == len - 2;
|
return packet_len == len - 2;
|
||||||
} else {
|
|
||||||
if (len < 1)
|
|
||||||
return PROBE_NEXT;
|
|
||||||
|
|
||||||
if ((p[0] & OVPN_OPCODE_MASK) != OVPN_CONTROL_HARD_RESET_CLIENT_V1 &&
|
|
||||||
(p[0] & OVPN_OPCODE_MASK) != OVPN_CONTROL_HARD_RESET_CLIENT_V2)
|
|
||||||
return PROBE_NEXT;
|
|
||||||
|
|
||||||
/* The detection pattern above may not be reliable enough.
|
|
||||||
* Check the packet id: OpenVPN sents five initial packets
|
|
||||||
* whereas the packet id is increased with every transmitted datagram.
|
|
||||||
*/
|
|
||||||
|
|
||||||
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) + sizeof(uint32_t))
|
|
||||||
return PROBE_NEXT;
|
|
||||||
|
|
||||||
if (ntohl(*(uint32_t*)(p + OVPN_HARD_RESET_PACKET_ID_OFFSET(OVPN_HMAC_160))) <= 5u)
|
|
||||||
return PROBE_MATCH;
|
|
||||||
|
|
||||||
return PROBE_NEXT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int is_wireguard_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_item* proto)
|
|
||||||
{
|
|
||||||
if (proto->is_udp == 0)
|
|
||||||
return PROBE_NEXT;
|
|
||||||
|
|
||||||
// Handshake Init: 148 bytes
|
|
||||||
if (len != 148)
|
|
||||||
return PROBE_NEXT;
|
|
||||||
|
|
||||||
// Handshake Init: p[0] = 0x01, p[1..3] = 0x000000 (reserved)
|
|
||||||
if (ntohl(*(uint32_t*)p) != 0x01000000)
|
|
||||||
return PROBE_NEXT;
|
|
||||||
|
|
||||||
return PROBE_MATCH;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Is the buffer the beginning of a tinc connections?
|
/* Is the buffer the beginning of a tinc connections?
|
||||||
* Protocol is documented here: http://www.tinc-vpn.org/documentation/tinc.pdf
|
* (protocol is undocumented, but starts with "0 " in 1.0.15)
|
||||||
* First connection starts with "0 " in 1.0.15)
|
|
||||||
* */
|
* */
|
||||||
static int is_tinc_protocol( const char *p, ssize_t len, struct sslhcfg_protocols_item* proto)
|
static int is_tinc_protocol( const char *p, int len, struct proto *proto)
|
||||||
{
|
{
|
||||||
if (len < 2)
|
if (len < 2)
|
||||||
return PROBE_AGAIN;
|
return PROBE_AGAIN;
|
||||||
@ -221,18 +167,12 @@ static int is_tinc_protocol( const char *p, ssize_t len, struct sslhcfg_protocol
|
|||||||
* (Protocol is documented (http://tools.ietf.org/html/rfc6120) but for lazy
|
* (Protocol is documented (http://tools.ietf.org/html/rfc6120) but for lazy
|
||||||
* clients, just checking first frame containing "jabber" in xml entity)
|
* clients, just checking first frame containing "jabber" in xml entity)
|
||||||
* */
|
* */
|
||||||
static int is_xmpp_protocol( const char *p, ssize_t len, struct sslhcfg_protocols_item* proto)
|
static int is_xmpp_protocol( const char *p, int len, struct proto *proto)
|
||||||
{
|
{
|
||||||
if (memmem(p, len, "jabber", 6))
|
if (len < 6)
|
||||||
return PROBE_MATCH;
|
|
||||||
|
|
||||||
/* sometimes the word 'jabber' shows up late in the initial string,
|
|
||||||
sometimes after a newline. this makes sure we snarf the entire preamble
|
|
||||||
and detect it. (fixed for adium/pidgin) */
|
|
||||||
if (len < 50)
|
|
||||||
return PROBE_AGAIN;
|
return PROBE_AGAIN;
|
||||||
|
|
||||||
return PROBE_NEXT;
|
return memmem(p, len, "jabber", 6) ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int probe_http_method(const char *p, int len, const char *opt)
|
static int probe_http_method(const char *p, int len, const char *opt)
|
||||||
@ -240,11 +180,11 @@ static int probe_http_method(const char *p, int len, const char *opt)
|
|||||||
if (len < strlen(opt))
|
if (len < strlen(opt))
|
||||||
return PROBE_AGAIN;
|
return PROBE_AGAIN;
|
||||||
|
|
||||||
return !strncmp(p, opt, strlen(opt));
|
return !strncmp(p, opt, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Is the buffer the beginning of an HTTP connection? */
|
/* Is the buffer the beginning of an HTTP connection? */
|
||||||
static int is_http_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_item* proto)
|
static int is_http_protocol(const char *p, int len, struct proto *proto)
|
||||||
{
|
{
|
||||||
int res;
|
int res;
|
||||||
/* If it's got HTTP in the request (HTTP/1.1) then it's HTTP */
|
/* If it's got HTTP in the request (HTTP/1.1) then it's HTTP */
|
||||||
@ -269,232 +209,95 @@ static int is_http_protocol(const char *p, ssize_t len, struct sslhcfg_protocols
|
|||||||
return PROBE_NEXT;
|
return PROBE_NEXT;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Says if it's TLS, optionally with SNI and ALPN lists in proto->data */
|
static int is_tls_protocol(const char *p, int len, struct proto *proto)
|
||||||
static int is_tls_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_item* proto)
|
|
||||||
{
|
{
|
||||||
switch (parse_tls_header(proto->data, p, len)) {
|
if (len < 3)
|
||||||
case TLS_MATCH: return PROBE_MATCH;
|
return PROBE_AGAIN;
|
||||||
case TLS_NOMATCH: return PROBE_NEXT;
|
|
||||||
case TLS_ELENGTH: return PROBE_AGAIN;
|
/* TLS packet starts with a record "Hello" (0x16), followed by version
|
||||||
default: return PROBE_NEXT;
|
* (0x03 0x00-0x03) (RFC6101 A.1)
|
||||||
|
* This means we reject SSLv2 and lower, which is actually a good thing (RFC6176)
|
||||||
|
*/
|
||||||
|
return p[0] == 0x16 && p[1] == 0x03 && ( p[2] >= 0 && p[2] <= 0x03);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int regex_probe(const char *p, int len, struct proto *proto)
|
||||||
|
{
|
||||||
|
regex_t **probe = proto->data;
|
||||||
|
regmatch_t pos = { 0, len };
|
||||||
|
|
||||||
|
for (; *probe && regexec(*probe, p, 0, &pos, REG_STARTEND); probe++)
|
||||||
|
/* try them all */;
|
||||||
|
|
||||||
|
return (probe != NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read the beginning of data coming from the client connection and check if
|
||||||
|
* it's a known protocol.
|
||||||
|
* Return PROBE_AGAIN if not enough data, or PROBE_MATCH if it succeeded in
|
||||||
|
* which case cnx->proto is set to the appropriate protocol.
|
||||||
|
*/
|
||||||
|
int probe_client_protocol(struct connection *cnx)
|
||||||
|
{
|
||||||
|
char buffer[BUFSIZ];
|
||||||
|
struct proto *p;
|
||||||
|
int n;
|
||||||
|
|
||||||
|
n = read(cnx->q[0].fd, buffer, sizeof(buffer));
|
||||||
|
/* It's possible that read() returns an error, e.g. if the client
|
||||||
|
* disconnected between the previous call to select() and now. If that
|
||||||
|
* happens, we just connect to the default protocol so the caller of this
|
||||||
|
* function does not have to deal with a specific failure condition (the
|
||||||
|
* connection will just fail later normally). */
|
||||||
|
if (n > 0) {
|
||||||
|
int res = PROBE_NEXT;
|
||||||
|
|
||||||
|
defer_write(&cnx->q[1], buffer, n);
|
||||||
|
|
||||||
|
for (p = cnx->proto; p && res == PROBE_NEXT; p = p->next) {
|
||||||
|
if (! p->probe) continue;
|
||||||
|
if (verbose) fprintf(stderr, "probing for %s\n", p->description);
|
||||||
|
|
||||||
|
cnx->proto = p;
|
||||||
|
res = p->probe(cnx->q[1].begin_deferred_data, cnx->q[1].deferred_data_size, p);
|
||||||
|
}
|
||||||
|
if (res != PROBE_NEXT)
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (verbose)
|
||||||
|
fprintf(stderr,
|
||||||
|
"all probes failed, connecting to first protocol: %s\n",
|
||||||
|
protocols->description);
|
||||||
|
|
||||||
|
/* If none worked, return the first one affected (that's completely
|
||||||
|
* arbitrary) */
|
||||||
|
cnx->proto = protocols;
|
||||||
|
return PROBE_MATCH;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int probe_adb_cnxn_message(const char *p)
|
/* Returns the structure for specified protocol or NULL if not found */
|
||||||
|
static struct proto* get_protocol(const char* description)
|
||||||
{
|
{
|
||||||
/* The initial ADB host->device packet has a command type of CNXN, and a
|
|
||||||
* data payload starting with "host:". Note that current versions of the
|
|
||||||
* client hardcode "host::" (with empty serialno and banner fields) but
|
|
||||||
* other clients may populate those fields.
|
|
||||||
*/
|
|
||||||
return !memcmp(&p[0], "CNXN", 4) && !memcmp(&p[24], "host:", 5);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int is_adb_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_item* proto)
|
|
||||||
{
|
|
||||||
/* amessage.data_length is not being checked, under the assumption that
|
|
||||||
* a packet >= 30 bytes will have "something" in the payload field.
|
|
||||||
*
|
|
||||||
* 24 bytes for the message header and 5 bytes for the "host:" tag.
|
|
||||||
*
|
|
||||||
* ADB protocol:
|
|
||||||
* https://android.googlesource.com/platform/system/adb/+/master/protocol.txt
|
|
||||||
*/
|
|
||||||
static const unsigned int min_data_packet_size = 30;
|
|
||||||
|
|
||||||
if (len < min_data_packet_size)
|
|
||||||
return PROBE_AGAIN;
|
|
||||||
|
|
||||||
if (probe_adb_cnxn_message(&p[0]) == PROBE_MATCH)
|
|
||||||
return PROBE_MATCH;
|
|
||||||
|
|
||||||
/* In ADB v26.0.0 rc1-4321094, the initial host->device packet sends an
|
|
||||||
* empty message before sending the CNXN command type. This was an
|
|
||||||
* unintended side effect introduced in
|
|
||||||
* https://android-review.googlesource.com/c/342653, and will be reverted for
|
|
||||||
* a future release.
|
|
||||||
*/
|
|
||||||
static const unsigned char empty_message[] = {
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff
|
|
||||||
};
|
|
||||||
|
|
||||||
if (len < min_data_packet_size + sizeof(empty_message))
|
|
||||||
return PROBE_AGAIN;
|
|
||||||
|
|
||||||
if (memcmp(&p[0], empty_message, sizeof(empty_message)) != 0)
|
|
||||||
return PROBE_NEXT;
|
|
||||||
|
|
||||||
return probe_adb_cnxn_message(&p[sizeof(empty_message)]);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int is_socks5_protocol(const char *p_in, ssize_t len, struct sslhcfg_protocols_item* proto)
|
|
||||||
{
|
|
||||||
unsigned char* p = (unsigned char*)p_in;
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (len < 2)
|
for (i = 0; i < ARRAY_SIZE(builtins); i++) {
|
||||||
return PROBE_AGAIN;
|
if (!strcmp(builtins[i].description, description)) {
|
||||||
|
return &builtins[i];
|
||||||
/* First byte should be socks protocol version */
|
|
||||||
if (p[0] != 5)
|
|
||||||
return PROBE_NEXT;
|
|
||||||
|
|
||||||
/* Second byte should be number of supported
|
|
||||||
* authentication methods, assuming maximum of 10,
|
|
||||||
* as defined in https://www.iana.org/assignments/socks-methods/socks-methods.xhtml
|
|
||||||
*/
|
|
||||||
char m_count = p[1];
|
|
||||||
if (m_count < 1 || m_count > 10)
|
|
||||||
return PROBE_NEXT;
|
|
||||||
|
|
||||||
if (len < 2 + m_count)
|
|
||||||
return PROBE_AGAIN;
|
|
||||||
|
|
||||||
/* Each authentication method number should be in range 0..9
|
|
||||||
* (https://www.iana.org/assignments/socks-methods/socks-methods.xhtml)
|
|
||||||
*/
|
|
||||||
for (i = 0; i < m_count; i++) {
|
|
||||||
if (p[2 + i] > 9)
|
|
||||||
return PROBE_NEXT;
|
|
||||||
}
|
|
||||||
return PROBE_MATCH;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int is_syslog_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_item* proto)
|
|
||||||
{
|
|
||||||
int res, i, j;
|
|
||||||
|
|
||||||
res = sscanf(p, "<%d>", &i);
|
|
||||||
if (res == 1) return 1;
|
|
||||||
|
|
||||||
res = sscanf(p, "%d <%d>", &i, &j);
|
|
||||||
if (res == 2) return 1;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int is_teamspeak_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_item* proto)
|
|
||||||
{
|
|
||||||
if (len < 8)
|
|
||||||
return PROBE_NEXT;
|
|
||||||
|
|
||||||
return !strncmp(p, "TS3INIT1", len);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int is_msrdp_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_item* proto)
|
|
||||||
{
|
|
||||||
char version;
|
|
||||||
char packet_len;
|
|
||||||
if (len < 7)
|
|
||||||
return PROBE_NEXT;
|
|
||||||
version=*p;
|
|
||||||
if (version!=0x03)
|
|
||||||
return 0;
|
|
||||||
packet_len = ntohs(*(uint16_t*)(p+2));
|
|
||||||
return packet_len == len;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int regex_probe(const char *p, ssize_t len, struct sslhcfg_protocols_item* proto)
|
|
||||||
{
|
|
||||||
#ifdef ENABLE_REGEX
|
|
||||||
pcre2_code**probe = (pcre2_code**)proto->data;
|
|
||||||
pcre2_match_data* matches;
|
|
||||||
|
|
||||||
matches = pcre2_match_data_create(1, NULL);
|
|
||||||
|
|
||||||
for (; *probe; probe++) {
|
|
||||||
int res = pcre2_match(*probe, (PCRE2_SPTR8)p, len, 0, 0, matches, NULL);
|
|
||||||
if (res >= 0) return 1;
|
|
||||||
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
#else
|
|
||||||
/* Should never happen as we check when loading config file */
|
|
||||||
print_message(msg_int_error, "FATAL: regex probe called but not built in\n");
|
|
||||||
exit(5);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Run all the probes on a buffer
|
|
||||||
* buf, len: buffer to test on
|
|
||||||
* proto_in, proto_len: array of protocols to try
|
|
||||||
* proto_out: protocol that matched
|
|
||||||
*
|
|
||||||
* Returns
|
|
||||||
* PROBE_AGAIN if not enough data, and set *proto to NULL
|
|
||||||
* PROBE_MATCH if protocol is identified, in which case *proto is set to
|
|
||||||
* point to the appropriate protocol
|
|
||||||
* */
|
|
||||||
int probe_buffer(char* buf, int len,
|
|
||||||
struct sslhcfg_protocols_item** proto_in,
|
|
||||||
int proto_len,
|
|
||||||
struct sslhcfg_protocols_item** proto_out
|
|
||||||
)
|
|
||||||
{
|
|
||||||
struct sslhcfg_protocols_item* p;
|
|
||||||
int i, res, again = 0;
|
|
||||||
|
|
||||||
print_message(msg_packets, "hexdump of incoming packet:\n");
|
|
||||||
hexdump(msg_packets, buf, len);
|
|
||||||
|
|
||||||
*proto_out = NULL;
|
|
||||||
for (i = 0; i < proto_len; i++) {
|
|
||||||
char* probe_str[3] = {"PROBE_NEXT", "PROBE_MATCH", "PROBE_AGAIN"};
|
|
||||||
p = proto_in[i];
|
|
||||||
|
|
||||||
if (! p->probe) continue;
|
|
||||||
|
|
||||||
print_message(msg_probe_info, "probing for %s\n", p->name);
|
|
||||||
|
|
||||||
/* Don't probe last protocol if it is anyprot (and store last protocol) */
|
|
||||||
if ((i == proto_len - 1) && (!strcmp(p->name, "anyprot")))
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (p->minlength_is_present && (len < p->minlength )) {
|
|
||||||
print_message(msg_probe_info, "input too short, %d bytes but need %d\n",
|
|
||||||
len , p->minlength);
|
|
||||||
again++;
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
res = p->probe(buf, len, p);
|
|
||||||
print_message(msg_probe_info, "probed for %s: %s\n", p->name, probe_str[res]);
|
|
||||||
|
|
||||||
if (res == PROBE_MATCH) {
|
|
||||||
*proto_out = p;
|
|
||||||
return PROBE_MATCH;
|
|
||||||
}
|
|
||||||
if (res == PROBE_AGAIN)
|
|
||||||
again++;
|
|
||||||
}
|
}
|
||||||
if (again)
|
return NULL;
|
||||||
return PROBE_AGAIN;
|
|
||||||
|
|
||||||
/* Everything failed: match the last one */
|
|
||||||
|
|
||||||
if (proto_len == 0) {
|
|
||||||
/* This should be caught by configuration sanity checks, but just in
|
|
||||||
* case, die gracefully rather than segfaulting */
|
|
||||||
print_message(msg_int_error, "Received traffic on transport that has no target\n");
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
*proto_out = proto_in[proto_len-1];
|
|
||||||
return PROBE_MATCH;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Returns the probe for specified protocol:
|
/* Returns the probe for specified protocol:
|
||||||
* parameter is the description in builtins[], or "regex"
|
* parameter is the description in builtins[], or "regex"
|
||||||
* */
|
* */
|
||||||
T_PROBE* get_probe(const char* description) {
|
T_PROBE* get_probe(const char* description) {
|
||||||
int i;
|
struct proto* p = get_protocol(description);
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(builtins); i++) {
|
if (p)
|
||||||
if (!strcmp(builtins[i].name, description)) {
|
return p->probe;
|
||||||
return builtins[i].probe;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Special case of "regex" probe (we don't want to set it in builtins
|
/* Special case of "regex" probe (we don't want to set it in builtins
|
||||||
* because builtins is also used to build the command-line options and
|
* because builtins is also used to build the command-line options and
|
||||||
@ -502,11 +305,6 @@ T_PROBE* get_probe(const char* description) {
|
|||||||
if (!strcmp(description, "regex"))
|
if (!strcmp(description, "regex"))
|
||||||
return regex_probe;
|
return regex_probe;
|
||||||
|
|
||||||
/* Special case of "timeout" is allowed as a probe name in the
|
|
||||||
* configuration file even though it's not really a probe */
|
|
||||||
if (!strcmp(description, "timeout"))
|
|
||||||
return is_true;
|
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
42
probe.h
42
probe.h
@ -1,11 +1,9 @@
|
|||||||
/* API for probe.c */
|
/* API for probe.c */
|
||||||
|
|
||||||
#ifndef PROBE_H
|
#ifndef __PROBE_H_
|
||||||
#define PROBE_H
|
#define __PROBE_H_
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "tls.h"
|
|
||||||
#include "log.h"
|
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
PROBE_NEXT, /* Enough data, probe failed -- it's some other protocol */
|
PROBE_NEXT, /* Enough data, probe failed -- it's some other protocol */
|
||||||
@ -13,19 +11,24 @@ typedef enum {
|
|||||||
PROBE_AGAIN, /* Not enough data for this probe, try again with more data */
|
PROBE_AGAIN, /* Not enough data for this probe, try again with more data */
|
||||||
} probe_result;
|
} probe_result;
|
||||||
|
|
||||||
struct sslhcfg_protocols_item;
|
struct proto;
|
||||||
typedef int T_PROBE(const char*, ssize_t, struct sslhcfg_protocols_item*);
|
typedef int T_PROBE(const char*, int, struct proto*);
|
||||||
|
|
||||||
struct protocol_probe_desc {
|
/* For each protocol we need: */
|
||||||
const char* name;
|
struct proto {
|
||||||
|
const char* description; /* a string that says what it is (for logging and command-line parsing) */
|
||||||
|
const char* service; /* service name to do libwrap checks */
|
||||||
|
struct addrinfo *saddr; /* list of addresses to try and switch that protocol */
|
||||||
|
|
||||||
|
/* function to probe that protocol; parameters are buffer and length
|
||||||
|
* containing the data to probe, and a pointer to the protocol structure */
|
||||||
T_PROBE* probe;
|
T_PROBE* probe;
|
||||||
|
void* data; /* opaque pointer ; used to pass list of regex to regex probe */
|
||||||
|
struct proto *next; /* pointer to next protocol in list, NULL if last */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#include "sslh-conf.h"
|
|
||||||
|
|
||||||
/* Returns a pointer to the array of builtin protocols */
|
/* Returns a pointer to the array of builtin protocols */
|
||||||
struct protocol_probe_desc* get_builtins(void);
|
struct proto * get_builtins(void);
|
||||||
|
|
||||||
/* Returns the number of builtin protocols */
|
/* Returns the number of builtin protocols */
|
||||||
int get_num_builtins(void);
|
int get_num_builtins(void);
|
||||||
@ -34,10 +37,10 @@ int get_num_builtins(void);
|
|||||||
T_PROBE* get_probe(const char* description);
|
T_PROBE* get_probe(const char* description);
|
||||||
|
|
||||||
/* Returns the head of the configured protocols */
|
/* Returns the head of the configured protocols */
|
||||||
struct sslhcfg_protocols_item* get_first_protocol(void);
|
struct proto* get_first_protocol(void);
|
||||||
|
|
||||||
/* Set the list of configured protocols */
|
/* Set the list of configured protocols */
|
||||||
void set_protocol_list(struct sslhcfg_protocols_item*);
|
void set_protocol_list(struct proto*);
|
||||||
|
|
||||||
/* probe_client_protocol
|
/* probe_client_protocol
|
||||||
*
|
*
|
||||||
@ -48,13 +51,6 @@ void set_protocol_list(struct sslhcfg_protocols_item*);
|
|||||||
*/
|
*/
|
||||||
int probe_client_protocol(struct connection *cnx);
|
int probe_client_protocol(struct connection *cnx);
|
||||||
|
|
||||||
/* Probe on a buffer */
|
|
||||||
int probe_buffer(char* buf, int len,
|
|
||||||
struct sslhcfg_protocols_item** proto_in,
|
|
||||||
int proto_len,
|
|
||||||
struct sslhcfg_protocols_item** proto_out
|
|
||||||
);
|
|
||||||
|
|
||||||
/* set the protocol to connect to in case of timeout */
|
/* set the protocol to connect to in case of timeout */
|
||||||
void set_ontimeout(const char* name);
|
void set_ontimeout(const char* name);
|
||||||
|
|
||||||
@ -62,8 +58,8 @@ void set_ontimeout(const char* name);
|
|||||||
*
|
*
|
||||||
* Returns the protocol to connect to in case of timeout
|
* Returns the protocol to connect to in case of timeout
|
||||||
*/
|
*/
|
||||||
struct sslhcfg_protocols_item* timeout_protocol(void);
|
struct proto* timeout_protocol(void);
|
||||||
|
|
||||||
void hexdump(msg_info, const char*, unsigned int);
|
void hexdump(const char*, unsigned int);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
115
processes.c
115
processes.c
@ -1,115 +0,0 @@
|
|||||||
/*
|
|
||||||
Processes that are common to sslh-ev and sslh-select
|
|
||||||
|
|
||||||
# Copyright (C) 2021 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 "udp-listener.h"
|
|
||||||
#include "tcp-listener.h"
|
|
||||||
#include "processes.h"
|
|
||||||
#include "probe.h"
|
|
||||||
#include "log.h"
|
|
||||||
|
|
||||||
|
|
||||||
int tidy_connection(struct connection *cnx, struct loop_info* fd_info)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < 2; i++) {
|
|
||||||
if (cnx->q[i].fd != -1) {
|
|
||||||
print_message(msg_fd, "closing fd %d\n", cnx->q[i].fd);
|
|
||||||
|
|
||||||
watchers_del_read(fd_info->watchers, cnx->q[i].fd);
|
|
||||||
watchers_del_write(fd_info->watchers, cnx->q[i].fd);
|
|
||||||
close(cnx->q[i].fd);
|
|
||||||
if (cnx->q[i].deferred_data)
|
|
||||||
free(cnx->q[i].deferred_data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cnx->type == SOCK_DGRAM)
|
|
||||||
udp_tidy(cnx, fd_info);
|
|
||||||
|
|
||||||
if (gap_remove_ptr(fd_info->probing_list, cnx, fd_info->num_probing) != -1)
|
|
||||||
fd_info->num_probing--;
|
|
||||||
|
|
||||||
collection_remove_cnx(fd_info->collection, cnx);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Process a connection that is active in read */
|
|
||||||
|
|
||||||
void cnx_read_process(struct loop_info* fd_info, int fd)
|
|
||||||
{
|
|
||||||
cnx_collection* collection = fd_info->collection;
|
|
||||||
struct connection* cnx = collection_get_cnx_from_fd(collection, fd);
|
|
||||||
switch (cnx->type) {
|
|
||||||
case SOCK_STREAM:
|
|
||||||
tcp_read_process(fd_info, fd);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SOCK_DGRAM:
|
|
||||||
udp_s2c_forward(cnx);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
print_message(msg_int_error, "cnx_read_process: Illegal connection type %d\n", cnx->type);
|
|
||||||
dump_connection(cnx);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Process a connection that accepts a socket
|
|
||||||
* (For UDP, this means all traffic coming from remote clients)
|
|
||||||
* Returns new connection object, or NULL
|
|
||||||
* */
|
|
||||||
struct connection* cnx_accept_process(struct loop_info* fd_info, struct listen_endpoint* listen_socket)
|
|
||||||
{
|
|
||||||
int fd = listen_socket->socketfd;
|
|
||||||
int type = listen_socket->type;
|
|
||||||
struct connection* cnx;
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case SOCK_STREAM:
|
|
||||||
cnx = accept_new_connection(fd, fd_info);
|
|
||||||
if (!cnx) return NULL;
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SOCK_DGRAM:
|
|
||||||
cnx = udp_c2s_forward(fd, fd_info);
|
|
||||||
if (!cnx) return NULL;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
print_message(msg_int_error, "Inconsistent cnx type: %d\n", type);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
int new_fd = cnx->q[0].fd;
|
|
||||||
watchers_add_read(fd_info->watchers, new_fd);
|
|
||||||
return cnx;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
41
processes.h
41
processes.h
@ -1,41 +0,0 @@
|
|||||||
#ifndef PROCESSES_H
|
|
||||||
#define PROCESSES_H
|
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
#include "collection.h"
|
|
||||||
#include "gap.h"
|
|
||||||
|
|
||||||
typedef struct connection* hash_item;
|
|
||||||
#include "hash.h"
|
|
||||||
|
|
||||||
/* Provided by event loop, sslh-ev or sslh-select, for implementation-dependant
|
|
||||||
* data */
|
|
||||||
typedef struct watchers watchers;
|
|
||||||
|
|
||||||
/* Global state for a loop */
|
|
||||||
struct loop_info {
|
|
||||||
int num_probing; /* Number of connections currently probing
|
|
||||||
* We use this to know if we need to time out of
|
|
||||||
* select() */
|
|
||||||
gap_array* probing_list; /* Pointers to cnx that are in probing mode */
|
|
||||||
|
|
||||||
hash* hash_sources; /* UDP remote sources previously encountered */
|
|
||||||
|
|
||||||
watchers* watchers;
|
|
||||||
|
|
||||||
cnx_collection* collection; /* Collection of connections linked to this loop */
|
|
||||||
};
|
|
||||||
|
|
||||||
void cnx_read_process(struct loop_info* fd_info, int fd);
|
|
||||||
struct connection* cnx_accept_process(struct loop_info* fd_info, struct listen_endpoint* listen_socket);
|
|
||||||
|
|
||||||
int tidy_connection(struct connection *cnx, struct loop_info* fd_info);
|
|
||||||
|
|
||||||
|
|
||||||
/* These must be declared in the loop handler, sslh-ev or sslh-select */
|
|
||||||
void watchers_add_read(watchers* w, int fd);
|
|
||||||
void watchers_del_read(watchers* w, int fd);
|
|
||||||
void watchers_add_write(watchers* w, int fd);
|
|
||||||
void watchers_del_write(watchers* w, int fd);
|
|
||||||
|
|
||||||
#endif
|
|
120
proxyprotocol.c
120
proxyprotocol.c
@ -1,120 +0,0 @@
|
|||||||
/*
|
|
||||||
# proxyprotocol: Support for HAProxy's proxyprotocol
|
|
||||||
#
|
|
||||||
# Copyright (C) 2025 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"
|
|
||||||
|
|
||||||
#ifdef HAVE_PROXYPROTOCOL
|
|
||||||
|
|
||||||
#include <proxy_protocol.h>
|
|
||||||
#include "common.h"
|
|
||||||
#include "log.h"
|
|
||||||
|
|
||||||
|
|
||||||
/* Converts socket family to libproxyprotocol family */
|
|
||||||
static int family_to_pp(int af_family)
|
|
||||||
{
|
|
||||||
switch (af_family) {
|
|
||||||
case AF_INET:
|
|
||||||
return ADDR_FAMILY_INET;
|
|
||||||
|
|
||||||
case AF_INET6:
|
|
||||||
return ADDR_FAMILY_INET6;
|
|
||||||
|
|
||||||
case AF_UNIX:
|
|
||||||
return ADDR_FAMILY_UNIX;
|
|
||||||
|
|
||||||
default:
|
|
||||||
print_message(msg_int_error, "Unknown internal socket family %d\n", af_family);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef char libpp_addr[108]; /* This is hardcoded in libproxyprotocol/proxy_protocol.h */
|
|
||||||
|
|
||||||
/* Fills *addr, *host and *serv with the connection information corresponding
|
|
||||||
* to fd. *host is the IP address as string and *serv is the service (port)
|
|
||||||
* */
|
|
||||||
static int get_info(int fd, struct addrinfo* addr, libpp_addr* host, uint16_t* serv)
|
|
||||||
{
|
|
||||||
char serv_str[NI_MAXSERV];
|
|
||||||
int res;
|
|
||||||
|
|
||||||
res = getpeername(fd, addr->ai_addr, &addr->ai_addrlen);
|
|
||||||
CHECK_RES_RETURN(res, "getpeername", -1);
|
|
||||||
|
|
||||||
res = getnameinfo(addr->ai_addr, addr->ai_addrlen,
|
|
||||||
(char*)host, sizeof(*host),
|
|
||||||
serv_str, sizeof(serv_str),
|
|
||||||
NI_NUMERICHOST | NI_NUMERICSERV );
|
|
||||||
CHECK_RES_RETURN(res, "getnameinfo", -1);
|
|
||||||
|
|
||||||
*serv = atoi(serv_str);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int pp_write_header(int pp_version, struct connection* cnx)
|
|
||||||
{
|
|
||||||
pp_info_t pp_info_in_v1 = {
|
|
||||||
.transport_protocol = TRANSPORT_PROTOCOL_STREAM,
|
|
||||||
};
|
|
||||||
uint16_t pp1_hdr_len;
|
|
||||||
int32_t error;
|
|
||||||
|
|
||||||
struct sockaddr_storage ss;
|
|
||||||
struct addrinfo addr;
|
|
||||||
int res;
|
|
||||||
|
|
||||||
addr.ai_addr = (struct sockaddr*)&ss;
|
|
||||||
addr.ai_addrlen = sizeof(ss);
|
|
||||||
|
|
||||||
res = get_info(cnx->q[0].fd,
|
|
||||||
&addr,
|
|
||||||
&pp_info_in_v1.src_addr,
|
|
||||||
&pp_info_in_v1.src_port);
|
|
||||||
if (res == -1) return -1;
|
|
||||||
pp_info_in_v1.address_family = family_to_pp(addr.ai_addr->sa_family);
|
|
||||||
|
|
||||||
res = get_info(cnx->q[1].fd,
|
|
||||||
&addr,
|
|
||||||
&pp_info_in_v1.dst_addr,
|
|
||||||
&pp_info_in_v1.dst_port
|
|
||||||
);
|
|
||||||
if (res == -1) return -1;
|
|
||||||
|
|
||||||
uint8_t *pp1_hdr = pp_create_hdr(pp_version, &pp_info_in_v1, &pp1_hdr_len, &error);
|
|
||||||
|
|
||||||
if (!pp1_hdr) {
|
|
||||||
print_message(msg_system_error, "pp_create_hrd:%d:%s\n", error, pp_strerror(error));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
defer_write_before(&cnx->q[1], pp1_hdr, pp1_hdr_len);
|
|
||||||
|
|
||||||
pp_info_clear(&pp_info_in_v1);
|
|
||||||
free(pp1_hdr);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* HAVE_PROXYPROTOCOL */
|
|
@ -1,15 +0,0 @@
|
|||||||
#ifndef PROXYPROTOCOL_H
|
|
||||||
#define PROXYPROTOCOL_H
|
|
||||||
|
|
||||||
|
|
||||||
#if HAVE_PROXYPROTOCOL
|
|
||||||
int pp_write_header(int pp_version, struct connection* cnx);
|
|
||||||
|
|
||||||
|
|
||||||
#else /* HAVE_PROXYPROTOCOL */
|
|
||||||
|
|
||||||
static inline int pp_write_header(int pp_version, struct connection* cnx) { return 0; }
|
|
||||||
|
|
||||||
#endif /* HAVE_PROXYPROTOCOL */
|
|
||||||
|
|
||||||
#endif /* PROXYPROTOCOL_H */
|
|
@ -1,186 +0,0 @@
|
|||||||
#! /bin/sh
|
|
||||||
### BEGIN INIT INFO
|
|
||||||
# Provides: sslh
|
|
||||||
# Required-Start: $remote_fs $syslog $network
|
|
||||||
# Required-Stop: $remote_fs $syslog $network
|
|
||||||
# Default-Start: 2 3 4 5
|
|
||||||
# Default-Stop: 0 1 6
|
|
||||||
# Short-Description: ssl/ssh multiplexer
|
|
||||||
# Description: sslh lets one accept both HTTPS and SSH connections on the
|
|
||||||
# same port. It makes it possible to connect to an SSH server
|
|
||||||
# on port 443 (e.g. from inside a corporate firewall) while
|
|
||||||
# still serving HTTPS on that port.
|
|
||||||
### END INIT INFO
|
|
||||||
|
|
||||||
# Original Author: Guillaume Delacour <gui@iroqwa.org>
|
|
||||||
# modified and optimized for current sslh-fork
|
|
||||||
|
|
||||||
# Do NOT "set -e"
|
|
||||||
|
|
||||||
# PATH should only include /usr/* if it runs after the mountnfs.sh script
|
|
||||||
PATH=/sbin:/usr/sbin:/bin:/usr/bin
|
|
||||||
DESC="ssl/ssh multiplexer"
|
|
||||||
NAME=sslh
|
|
||||||
DAEMON=/usr/sbin/$NAME
|
|
||||||
DAEMON_OPTS=""
|
|
||||||
PIDFILE=/var/run/sslh/$NAME.pid
|
|
||||||
SCRIPTNAME=/etc/init.d/$NAME
|
|
||||||
RUN=yes
|
|
||||||
|
|
||||||
|
|
||||||
# If you want to use a configuration file, put -F/path/to/sslh.cfg
|
|
||||||
# into /etc/default/sslh DAEMON_OPTS
|
|
||||||
# Read configuration variable file if it is present
|
|
||||||
[ -r /etc/default/$NAME ] && . /etc/default/$NAME
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Load the VERBOSE setting and other rcS variables
|
|
||||||
. /lib/init/vars.sh
|
|
||||||
|
|
||||||
# Define LSB log_* functions.
|
|
||||||
# Depend on lsb-base (>= 3.2-14) to ensure that this file is present
|
|
||||||
# and status_of_proc is working.
|
|
||||||
. /lib/lsb/init-functions
|
|
||||||
|
|
||||||
# Exit if the package is not installed
|
|
||||||
if [ -x "$DAEMON" ]
|
|
||||||
then
|
|
||||||
echo "Can not start \"$DAEMON\", path not available"
|
|
||||||
log_failure_msg "Can not start \"$DAEMON\", path not available"
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# Function that starts the daemon/service
|
|
||||||
#
|
|
||||||
do_start()
|
|
||||||
{
|
|
||||||
# Return
|
|
||||||
# 0 if daemon has been started
|
|
||||||
# 1 if daemon was already running
|
|
||||||
# 2 if daemon could not be started
|
|
||||||
|
|
||||||
# Use this if you want the user to explicitly set 'RUN' in
|
|
||||||
# /etc/default/
|
|
||||||
if [ "$RUN" != "yes" ]
|
|
||||||
then
|
|
||||||
echo "$NAME disabled, please adjust the configuration to your needs "
|
|
||||||
log_failure_msg "and then set RUN to 'yes' in /etc/default/$NAME to enable it."
|
|
||||||
return 2
|
|
||||||
fi
|
|
||||||
|
|
||||||
# sslh write the pid as sslh user
|
|
||||||
if [ ! -d /var/run/sslh/ ]
|
|
||||||
then
|
|
||||||
mkdir -p /var/run/sslh
|
|
||||||
chown sslh:sslh /var/run/sslh
|
|
||||||
fi
|
|
||||||
|
|
||||||
start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \
|
|
||||||
|| return 1
|
|
||||||
start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \
|
|
||||||
$DAEMON_OPTS \
|
|
||||||
|| return 2
|
|
||||||
# Add code here, if necessary, that waits for the process to be ready
|
|
||||||
# to handle requests from services started subsequently which depend
|
|
||||||
# on this one. As a last resort, sleep for some time.
|
|
||||||
}
|
|
||||||
|
|
||||||
#
|
|
||||||
# Function that stops the daemon/service
|
|
||||||
#
|
|
||||||
do_stop()
|
|
||||||
{
|
|
||||||
# Return
|
|
||||||
# 0 if daemon has been stopped
|
|
||||||
# 1 if daemon was already stopped
|
|
||||||
# 2 if daemon could not be stopped
|
|
||||||
# other if a failure occurred
|
|
||||||
start-stop-daemon --stop --quiet --retry=TERM/45/KILL/5 --pidfile $PIDFILE --name $NAME
|
|
||||||
RETVAL="$?"
|
|
||||||
[ "$RETVAL" = 2 ] && return 2
|
|
||||||
# As long, as the started sslh is sslh-fork, don't kill the still existing
|
|
||||||
# connections. You may need the following construct for sslh-ev and sslh-select,
|
|
||||||
# as sslh has currently no function reloading its configuration.
|
|
||||||
|
|
||||||
# Wait for children to finish too if this is a daemon that forks
|
|
||||||
# and if the daemon is only ever run from this initscript.
|
|
||||||
# If the above conditions are not satisfied then add some other code
|
|
||||||
# that waits for the process to drop all resources that could be
|
|
||||||
# needed by services started subsequently. A last resort is to
|
|
||||||
# sleep for some time.
|
|
||||||
#start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON
|
|
||||||
#[ "$?" = 2 ] && return 2
|
|
||||||
# Many daemons don't delete their pidfiles when they exit.
|
|
||||||
rm -f $PIDFILE
|
|
||||||
return "$RETVAL"
|
|
||||||
}
|
|
||||||
|
|
||||||
#
|
|
||||||
# Function that sends a SIGHUP to the daemon/service
|
|
||||||
# don't activate this, as this kills only the leading process
|
|
||||||
# of sslh-fork, and the spawned worker stays connected listening.
|
|
||||||
# After that, the Owner of the PID from PIDFILE is gone, the
|
|
||||||
# listening connection is still blocked
|
|
||||||
# sslh can't reload its configuration as of Aug 2024
|
|
||||||
#do_reload() {
|
|
||||||
#
|
|
||||||
# If the daemon can reload its configuration without
|
|
||||||
# restarting (for example, when it is sent a SIGHUP),
|
|
||||||
# then implement that here.
|
|
||||||
#
|
|
||||||
# start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME
|
|
||||||
# return 0
|
|
||||||
#}
|
|
||||||
|
|
||||||
case "$1" in
|
|
||||||
start)
|
|
||||||
# check if sslh is launched via inetd
|
|
||||||
if [ -f /etc/inetd.conf ] && [ $(egrep -q "^https.*/usr/sbin/sslh" /etc/inetd.conf|wc -l) -ne 0 ]
|
|
||||||
then
|
|
||||||
echo "sslh is started from inetd."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
log_daemon_msg "Starting $DESC" "$NAME"
|
|
||||||
do_start
|
|
||||||
case "$?" in
|
|
||||||
0|1) log_end_msg 0 ;;
|
|
||||||
2) log_end_msg 1 ;;
|
|
||||||
esac
|
|
||||||
;;
|
|
||||||
stop)
|
|
||||||
log_daemon_msg "Stopping $DESC" "$NAME"
|
|
||||||
do_stop
|
|
||||||
case "$?" in
|
|
||||||
0|1) log_end_msg 0 ;;
|
|
||||||
2) log_end_msg 1 ;;
|
|
||||||
esac
|
|
||||||
;;
|
|
||||||
status)
|
|
||||||
status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $?
|
|
||||||
;;
|
|
||||||
restart|force-reload)
|
|
||||||
log_daemon_msg "Restarting $DESC" "$NAME"
|
|
||||||
do_stop
|
|
||||||
case "$?" in
|
|
||||||
0|1)
|
|
||||||
do_start
|
|
||||||
case "$?" in
|
|
||||||
0) log_end_msg 0 ;;
|
|
||||||
1) log_end_msg 1 ;; # Old process is still running
|
|
||||||
*) log_end_msg 1 ;; # Failed to start
|
|
||||||
esac
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
# Failed to stop
|
|
||||||
log_end_msg 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo "Usage: $SCRIPTNAME {start|stop|status|restart}" >&2
|
|
||||||
exit 3
|
|
||||||
;;
|
|
||||||
esac
|
|
5
scripts/etc.default.sslh
Executable file
5
scripts/etc.default.sslh
Executable file
@ -0,0 +1,5 @@
|
|||||||
|
LISTEN=ifname:443
|
||||||
|
SSH=localhost:22
|
||||||
|
SSL=localhost:443
|
||||||
|
USER=nobody
|
||||||
|
PID=/var/run/sslh.pid
|
@ -27,7 +27,7 @@ DAEMON=$PREFIX/sbin/sslh
|
|||||||
start()
|
start()
|
||||||
{
|
{
|
||||||
echo "Start services: sslh"
|
echo "Start services: sslh"
|
||||||
$DAEMON -F/etc/sslh.cfg
|
$DAEMON --user ${USER} --pidfile ${PID} --listen ${LISTEN} --ssh ${SSH} --ssl ${SSL}
|
||||||
logger -t ${tag} -p ${facility} -i 'Started sslh'
|
logger -t ${tag} -p ${facility} -i 'Started sslh'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
# is needed in order to run as sslh user
|
# is needed in order to run as sslh user
|
||||||
#
|
#
|
||||||
#SSLH_USER=sslh
|
#SSLH_USER=sslh
|
||||||
#setcap cap_net_bind_service,cap_net_raw=+ep $SSLH
|
#setcap cap_net_bind_service,cap_net_admin=+ep $SSLH
|
||||||
|
|
||||||
#
|
#
|
||||||
# Configuration file for sslh
|
# Configuration file for sslh
|
||||||
@ -18,7 +18,7 @@
|
|||||||
#CONFIG=/etc/sslh.cfg
|
#CONFIG=/etc/sslh.cfg
|
||||||
|
|
||||||
#
|
#
|
||||||
# Extra option to pass on command line
|
# Extra option to pass on comand line
|
||||||
# Those can supersede configuration file settings
|
# Those can supersede configuration file settings
|
||||||
#
|
#
|
||||||
#OPTIONS=
|
#OPTIONS=
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
# Add the following to your fail2ban jail.conf
|
|
||||||
# In Debian you'd append it to /etc/fail2ban/jail.local
|
|
||||||
|
|
||||||
[sslh-ssh]
|
|
||||||
enabled = true
|
|
||||||
filter = sslh-ssh
|
|
||||||
action = iptables-multiport[name=sslh,port="443"]
|
|
||||||
logpath = /var/log/messages
|
|
||||||
maxretry = 5
|
|
@ -1,25 +0,0 @@
|
|||||||
# Add the following to you fail2ban configuration file
|
|
||||||
# In Debian it'd go in /etc/fail2ban/filter.d/sslh-ssh.conf
|
|
||||||
|
|
||||||
|
|
||||||
# Fail2Ban filter for sslh demultiplexed ssh
|
|
||||||
#
|
|
||||||
# Doesn't (and cannot) detect auth errors,
|
|
||||||
# but many connection attempts from the same
|
|
||||||
# origin is reason enough to block.
|
|
||||||
#
|
|
||||||
# Version: 2014-03-28
|
|
||||||
|
|
||||||
[INCLUDES]
|
|
||||||
|
|
||||||
# no includes
|
|
||||||
|
|
||||||
[Definition]
|
|
||||||
|
|
||||||
failregex = ^.+ sslh\[.+\]: ssh:connection from <HOST>:.+ to .+ forwarded
|
|
||||||
from .+ to .+:ssh\s*$
|
|
||||||
|
|
||||||
ignoreregex =
|
|
||||||
|
|
||||||
# Author: Evert Mouw <post@evert.net>
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
|||||||
[Unit]
|
|
||||||
Description=SSL/SSH multiplexer (select mode) for %I
|
|
||||||
After=network.target
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
EnvironmentFile=/etc/default/sslh
|
|
||||||
ExecStart=/usr/sbin/sslh-select -F/etc/sslh/%I.cfg -f $DAEMON_OPTS
|
|
||||||
KillMode=process
|
|
||||||
#Hardening
|
|
||||||
PrivateTmp=true
|
|
||||||
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
|
|
||||||
AmbientCapabilities=CAP_NET_BIND_SERVICE
|
|
||||||
SecureBits=noroot-locked
|
|
||||||
ProtectSystem=strict
|
|
||||||
ProtectHome=true
|
|
||||||
ProtectKernelModules=true
|
|
||||||
ProtectKernelTunables=true
|
|
||||||
ProtectControlGroups=true
|
|
||||||
MountFlags=private
|
|
||||||
NoNewPrivileges=true
|
|
||||||
PrivateDevices=true
|
|
||||||
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
|
|
||||||
MemoryDenyWriteExecute=true
|
|
||||||
DynamicUser=true
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
11
scripts/systemd.sslh.service
Normal file
11
scripts/systemd.sslh.service
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=SSL/SSH multiplexer
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
EnvironmentFile=/etc/conf.d/sslh
|
||||||
|
ExecStart=/usr/bin/sslh --foreground $DAEMON_OPTS
|
||||||
|
KillMode=process
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
@ -1,27 +0,0 @@
|
|||||||
[Unit]
|
|
||||||
Description=SSL/SSH multiplexer (fork mode) for %I
|
|
||||||
After=network.target
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
EnvironmentFile=/etc/default/sslh
|
|
||||||
ExecStart=/usr/sbin/sslh -F/etc/sslh/%I.cfg -f $DAEMON_OPTS
|
|
||||||
KillMode=process
|
|
||||||
#Hardening
|
|
||||||
PrivateTmp=true
|
|
||||||
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
|
|
||||||
AmbientCapabilities=CAP_NET_BIND_SERVICE
|
|
||||||
SecureBits=noroot-locked
|
|
||||||
ProtectSystem=strict
|
|
||||||
ProtectHome=true
|
|
||||||
ProtectKernelModules=true
|
|
||||||
ProtectKernelTunables=true
|
|
||||||
ProtectControlGroups=true
|
|
||||||
MountFlags=private
|
|
||||||
NoNewPrivileges=true
|
|
||||||
PrivateDevices=true
|
|
||||||
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
|
|
||||||
MemoryDenyWriteExecute=true
|
|
||||||
DynamicUser=true
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user