mirror of
https://github.com/yrutschle/sslh.git
synced 2025-04-13 15:47:15 +03:00
Merge systemd unit multiple configurations
This commit is contained in:
parent
1799a81079
commit
4b921be69d
1
.gitignore
vendored
1
.gitignore
vendored
@ -7,6 +7,7 @@ libsslh.a
|
|||||||
sslh-fork
|
sslh-fork
|
||||||
sslh-select
|
sslh-select
|
||||||
sslh-ev
|
sslh-ev
|
||||||
|
systemd-sslh-generator
|
||||||
sslh.8.gz
|
sslh.8.gz
|
||||||
tags
|
tags
|
||||||
version.h
|
version.h
|
||||||
|
27
scripts/systemd.sslh-select@.service
Normal file
27
scripts/systemd.sslh-select@.service
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=SSL/SSH multiplexer (select mode) for %I
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
EnvironmentFile=/etc/conf.d/sslh
|
||||||
|
ExecStart=/usr/sbin/sslh-select -F/etc/sslh/%I.cfg -f $DAEMON_OPTS
|
||||||
|
KillMode=process
|
||||||
|
#Hardening
|
||||||
|
PrivateTmp=true
|
||||||
|
CapabilityBoundingSet=CAP_SETGID CAP_SETUID 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
|
@ -1,10 +1,10 @@
|
|||||||
[Unit]
|
[Unit]
|
||||||
Description=SSL/SSH multiplexer
|
Description=SSL/SSH multiplexer (fork mode) for %I
|
||||||
After=network.target
|
After=network.target
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
EnvironmentFile=/etc/conf.d/sslh
|
EnvironmentFile=/etc/conf.d/sslh
|
||||||
ExecStart=/usr/sbin/sslh --foreground $DAEMON_OPTS
|
ExecStart=/usr/sbin/sslh -F/etc/sslh/%I.cfg -f $DAEMON_OPTS
|
||||||
KillMode=process
|
KillMode=process
|
||||||
#Hardening
|
#Hardening
|
||||||
PrivateTmp=true
|
PrivateTmp=true
|
@ -1,39 +1,72 @@
|
|||||||
#include <libconfig.h>
|
#include <libconfig.h>
|
||||||
|
#include <dirent.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <errno.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
|
|
||||||
#define print_message(sink, format, file, line) fprintf(stderr, format, file, line)
|
#define print_message(sink, format, file, line) fprintf(stderr, format, file, line)
|
||||||
|
|
||||||
static char* resolve_listen(const char *hostname, const char *port) {
|
typedef struct FileList FileList;
|
||||||
/* Need room in the strcat for \0 and :
|
|
||||||
* the format in the socket unit file is hostname:port */
|
struct FileList {
|
||||||
char *conn = malloc(strlen(hostname)+strlen(port)+2);
|
char *name;
|
||||||
CHECK_ALLOC(conn, "malloc");
|
struct FileList *prev;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void free_file_list(FileList *fl) {
|
||||||
|
while (fl != NULL) {
|
||||||
|
FileList *prev = fl->prev;
|
||||||
|
free(fl->name);
|
||||||
|
free(fl);
|
||||||
|
fl = prev;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static FILE *err_log;
|
||||||
|
static bool systemd_invoked = false;
|
||||||
|
static const char
|
||||||
|
*socket_ext = ".socket",
|
||||||
|
*dropin_ext = ".conf",
|
||||||
|
*service_ext = ".service.d/",
|
||||||
|
*fork_unit_file = "/sslh@",
|
||||||
|
*select_unit_file = "/sslh-select@",
|
||||||
|
*fork_unit = "# Automatically generated by systemd-sslh-generator\n\n"
|
||||||
|
"[Unit]\n"
|
||||||
|
"Requires=sslh@%s.socket\n"
|
||||||
|
"Conflicts=sslh-select@%s.service\n"
|
||||||
|
"PartOf=sslh@%s.socket\n",
|
||||||
|
*select_unit = "# Automatically generated by systemd-sslh-generator\n\n"
|
||||||
|
"[Unit]\n"
|
||||||
|
"Requires=sslh@%s.socket\n"
|
||||||
|
"Conflicts=sslh@%s.service\n"
|
||||||
|
"PartOf=sslh@%s.socket\n";
|
||||||
|
|
||||||
|
static char *resolve_listen(const char *hostname, const char *port) {
|
||||||
|
char *conn = malloc(strlen(hostname) + strlen(port) + 2);
|
||||||
|
CHECK_ALLOC(conn, "malloc")
|
||||||
strcpy(conn, hostname);
|
strcpy(conn, hostname);
|
||||||
strcat(conn, ":");
|
strcat(conn, ":");
|
||||||
strcat(conn, port);
|
strcat(conn, port);
|
||||||
|
|
||||||
return conn;
|
return conn;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int get_listen_from_conf(const char *filename, char **listen[]) {
|
static int get_listen_from_conf(const char *filename, char **listen[]) {
|
||||||
config_t config;
|
config_t config;
|
||||||
config_setting_t *setting, *addr;
|
config_setting_t *setting, *addr;
|
||||||
const char *hostname, *port;
|
const char *hostname, *port;
|
||||||
int len = 0;
|
int len = 0;
|
||||||
|
|
||||||
/* look up the listen stanzas in the config file so these
|
|
||||||
* can be used in the socket file generated */
|
|
||||||
config_init(&config);
|
config_init(&config);
|
||||||
if (config_read_file(&config, filename) == CONFIG_FALSE) {
|
if (config_read_file(&config, filename) == CONFIG_FALSE) {
|
||||||
/* we don't care if file is missing, skip it */
|
|
||||||
if (config_error_line(&config) != 0) {
|
if (config_error_line(&config) != 0) {
|
||||||
fprintf(stderr, "%s:%d:%s\n",
|
fprintf(err_log,
|
||||||
|
"systemd-sslh-generator: %s%d%s\n",
|
||||||
filename,
|
filename,
|
||||||
config_error_line(&config),
|
config_error_line(&config),
|
||||||
config_error_text(&config));
|
config_error_text(&config));
|
||||||
@ -42,16 +75,14 @@ static int get_listen_from_conf(const char *filename, char **listen[]) {
|
|||||||
} else {
|
} else {
|
||||||
setting = config_lookup(&config, "listen");
|
setting = config_lookup(&config, "listen");
|
||||||
if (setting) {
|
if (setting) {
|
||||||
int i;
|
|
||||||
len = config_setting_length(setting);
|
len = config_setting_length(setting);
|
||||||
*listen = malloc(len * sizeof(**listen));
|
*listen = malloc(len * sizeof(**listen));
|
||||||
CHECK_ALLOC(*listen, "malloc");
|
CHECK_ALLOC(*listen, "malloc")
|
||||||
for (i = 0; i < len; i++) {
|
for (int i = 0; i < len; i++) {
|
||||||
addr = config_setting_get_elem(setting, i);
|
addr = config_setting_get_elem(setting, i);
|
||||||
if (! (config_setting_lookup_string(addr, "host", &hostname) &&
|
if (!(config_setting_lookup_string(addr, "host", &hostname) && config_setting_lookup_string(addr, "port", &port))) {
|
||||||
config_setting_lookup_string(addr, "port", &port))) {
|
fprintf(err_log,
|
||||||
fprintf(stderr,
|
"systemd-sslh-generator: line %d:Incomplete specification (hostname and port required)\n",
|
||||||
"line %d:Incomplete specification (hostname and port required)\n",
|
|
||||||
config_setting_source_line(addr));
|
config_setting_source_line(addr));
|
||||||
return -1;
|
return -1;
|
||||||
} else {
|
} else {
|
||||||
@ -65,95 +96,231 @@ static int get_listen_from_conf(const char *filename, char **listen[]) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return len;
|
return len;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int write_socket_unit(FILE *socket, char *listen[], int num_addr, const char *source) {
|
static void write_socket_unit(FILE *socket, char *listen[], int num_addr, const char *cfg, const char *source) {
|
||||||
int i;
|
|
||||||
|
|
||||||
fprintf(socket,
|
fprintf(socket,
|
||||||
"# Automatically generated by systemd-sslh-generator\n\n"
|
"# Automatically generated by systemd-sslh-generator\n\n"
|
||||||
"[Unit]\n"
|
"[Unit]\n"
|
||||||
"Before=sslh.service\n"
|
"Before=sslh@%s.service\n"
|
||||||
"SourcePath=%s\n"
|
"SourcePath=%s\n"
|
||||||
|
"PartOf=sslh@%s.service\n"
|
||||||
"Documentation=man:sslh(8) man:systemd-sslh-generator(8)\n\n"
|
"Documentation=man:sslh(8) man:systemd-sslh-generator(8)\n\n"
|
||||||
"[Socket]\n"
|
"[Socket]\n"
|
||||||
"FreeBind=true\n",
|
"FreeBind=true\n",
|
||||||
source);
|
cfg,
|
||||||
|
source,
|
||||||
for (i = 0; i < num_addr; i++) {
|
cfg);
|
||||||
|
for (int i = 0; i < num_addr; i++) {
|
||||||
fprintf(socket, "ListenStream=%s\n", listen[i]);
|
fprintf(socket, "ListenStream=%s\n", listen[i]);
|
||||||
}
|
}
|
||||||
|
fprintf(socket,
|
||||||
|
"\n[Install]\n"
|
||||||
|
"WantedBy=sockets.target\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int write_unit_dropin(const char *runtime_unit_dir, const bool is_fork, const char *cfg) {
|
||||||
|
const char
|
||||||
|
*unit_file = is_fork ? fork_unit_file : select_unit_file,
|
||||||
|
*unit_string = is_fork ? fork_unit : select_unit;
|
||||||
|
FILE *dropin_fd = stdout;
|
||||||
|
if (systemd_invoked) {
|
||||||
|
//Systemd drop-in configuration for the base select service sslh@%I.service
|
||||||
|
const size_t runtime_len = strlen(runtime_unit_dir);
|
||||||
|
size_t len = strlen(unit_file) + strlen(cfg) + strlen(service_ext) + strlen(dropin_ext);
|
||||||
|
char dropin_dir[runtime_len + len + 1];
|
||||||
|
strcpy(dropin_dir, runtime_unit_dir);
|
||||||
|
strcat(dropin_dir, unit_file);
|
||||||
|
strcat(dropin_dir, cfg);
|
||||||
|
strcat(dropin_dir, service_ext);
|
||||||
|
|
||||||
|
if (mkdir(dropin_dir, S_IRWXU | S_IRWXG | S_IROTH)) {
|
||||||
|
fprintf(err_log,
|
||||||
|
"systemd-sslh-generator: Could not create directory '%s' to generate drop-in configuration: %s\n",
|
||||||
|
dropin_dir,
|
||||||
|
strerror(errno));
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
len = len + strlen(cfg) + strlen(dropin_ext);
|
||||||
|
char dropin_path[len + 1];
|
||||||
|
strcpy(dropin_path, dropin_dir);
|
||||||
|
strcat(dropin_path, cfg);
|
||||||
|
strcat(dropin_path, dropin_ext);
|
||||||
|
dropin_fd = fopen(dropin_path, "w");
|
||||||
|
if (!dropin_fd) {
|
||||||
|
fprintf(err_log,
|
||||||
|
"systemd-sslh-generator: Could not open '%s' to generate drop-in configuration: %s\n",
|
||||||
|
dropin_path,
|
||||||
|
strerror(errno));
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fprintf(dropin_fd, unit_string, cfg, cfg, cfg);
|
||||||
|
if (systemd_invoked) {
|
||||||
|
fclose(dropin_fd);
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int gen_sslh_config(char *runtime_unit_dir) {
|
static int gen_sslh_config(char *runtime_unit_dir) {
|
||||||
char *sslh_conf;
|
int status = 0;
|
||||||
int num_addr;
|
const char *config_dir = "/etc/sslh/";
|
||||||
FILE *config;
|
|
||||||
char **listen;
|
char **listen;
|
||||||
FILE *runtime_conf_fd = stdout;
|
DIR *d = opendir(config_dir);
|
||||||
const char *unit_file;
|
FileList *fa = NULL;
|
||||||
|
|
||||||
/* There are two default locations so check both with first given preference */
|
if (d) {
|
||||||
sslh_conf = "/etc/sslh.cfg";
|
struct dirent *dir;
|
||||||
|
while ((dir = readdir(d)) != NULL) {
|
||||||
config = fopen(sslh_conf, "r");
|
if ((strcmp(dir->d_name, ".") == 0) || (strcmp(dir->d_name, "..") == 0)) {
|
||||||
if (config == NULL) {
|
continue;
|
||||||
sslh_conf="/etc/sslh/sslh.cfg";
|
}
|
||||||
config = fopen(sslh_conf, "r");
|
FileList *lfa = malloc(sizeof(FileList));
|
||||||
if (config == NULL) {
|
CHECK_ALLOC(lfa, "malloc")
|
||||||
return -1;
|
lfa->name = malloc(strlen(dir->d_name) + 1);
|
||||||
|
CHECK_ALLOC(lfa->name, "malloc")
|
||||||
|
strcpy(lfa->name, dir->d_name);
|
||||||
|
lfa->prev = NULL;
|
||||||
|
if (fa) {
|
||||||
|
lfa->prev = fa;
|
||||||
|
}
|
||||||
|
fa = lfa;
|
||||||
}
|
}
|
||||||
|
closedir(d);
|
||||||
|
} else {
|
||||||
|
fprintf(err_log,
|
||||||
|
"systemd-sslh-generator: Configuration directory '/etc/sslh/' does not exist! No units generated.\n");
|
||||||
|
//Config directory /etc/sslh/ does not exist
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
if (!fa) {
|
||||||
fclose(config);
|
fprintf(err_log,
|
||||||
|
"systemd-sslh-generator: Configuration directory '/etc/sslh/' is empty! No units generated.\n");
|
||||||
num_addr = get_listen_from_conf(sslh_conf, &listen);
|
//No configuration files in /etc/sslh/
|
||||||
if (num_addr < 0)
|
return 0;
|
||||||
return -1;
|
|
||||||
|
|
||||||
/* If this is run by systemd directly write to the location told to
|
|
||||||
* otherwise write to standard out so that it's trivial to check what
|
|
||||||
* will be written */
|
|
||||||
if (runtime_unit_dir && *runtime_unit_dir) {
|
|
||||||
unit_file = "/sslh.socket";
|
|
||||||
size_t uf_len = strlen(unit_file);
|
|
||||||
size_t runtime_len = strlen(runtime_unit_dir) + uf_len + 1;
|
|
||||||
char *runtime_conf = malloc(runtime_len);
|
|
||||||
CHECK_ALLOC(runtime_conf, "malloc");
|
|
||||||
strcpy(runtime_conf, runtime_unit_dir);
|
|
||||||
strcat(runtime_conf, unit_file);
|
|
||||||
runtime_conf_fd = fopen(runtime_conf, "w");
|
|
||||||
free(runtime_conf);
|
|
||||||
}
|
}
|
||||||
|
FileList *fa_ref = fa;
|
||||||
|
// size_t num_listen_addresses = 0;
|
||||||
|
// char **listen_addresses = NULL;
|
||||||
|
//Process all config files
|
||||||
|
do {
|
||||||
|
char *end = strstr(fa->name, ".cfg");
|
||||||
|
if (!end) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
//Current sslh config name
|
||||||
|
const size_t end_count = end - fa->name;
|
||||||
|
char config_name[end_count + 1];
|
||||||
|
memcpy(config_name, fa->name, end_count);
|
||||||
|
config_name[end_count] = '\0';
|
||||||
|
|
||||||
|
//Full path to current sslh config
|
||||||
return write_socket_unit(runtime_conf_fd, listen, num_addr, sslh_conf);
|
char full_path[strlen(config_dir) + strlen(fa->name) + 1];
|
||||||
|
strcpy(full_path, config_dir);
|
||||||
|
strcat(full_path, fa->name);
|
||||||
|
FILE *config = fopen(full_path, "r");
|
||||||
|
if (!config) {
|
||||||
|
fprintf(err_log,
|
||||||
|
"systemd-sslh-generator: Could not open config file '%s': %s\n",
|
||||||
|
full_path,
|
||||||
|
strerror(errno));
|
||||||
|
return errno;
|
||||||
|
} else {
|
||||||
|
fclose(config);
|
||||||
|
int num_addr = get_listen_from_conf(full_path, &listen);
|
||||||
|
if (num_addr <= 0) {
|
||||||
|
fprintf(err_log,
|
||||||
|
"systemd-sslh-generator: sslh config '%s' contains no valid listen configurations!\n",
|
||||||
|
fa->name);
|
||||||
|
status |= -1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
FILE *socket_fd = stdout;
|
||||||
|
if (systemd_invoked) {
|
||||||
|
//Systemd socket for the current sslh config
|
||||||
|
const size_t len = strlen(runtime_unit_dir) + strlen(fork_unit_file) + strlen(config_name) + strlen(socket_ext);
|
||||||
|
char socket_path[len + 1];
|
||||||
|
strcpy(socket_path, runtime_unit_dir);
|
||||||
|
strcat(socket_path, fork_unit_file);
|
||||||
|
strcat(socket_path, config_name);
|
||||||
|
strcat(socket_path, socket_ext);
|
||||||
|
socket_fd = fopen(socket_path, "w");
|
||||||
|
if (!socket_fd) {
|
||||||
|
fprintf(err_log,
|
||||||
|
"systemd-sslh-generator: Could not open '%s' to generate socket configuration: %s\n",
|
||||||
|
socket_path,
|
||||||
|
strerror(errno));
|
||||||
|
status |= errno;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//Write socket unit
|
||||||
|
write_socket_unit(socket_fd, listen, num_addr, config_name, full_path);
|
||||||
|
//Write forking drop-in config
|
||||||
|
status |= write_unit_dropin(runtime_unit_dir, false, config_name);
|
||||||
|
//Write select drop-in config
|
||||||
|
status |= write_unit_dropin(runtime_unit_dir, true, config_name);
|
||||||
|
if (systemd_invoked) {
|
||||||
|
fclose(socket_fd);
|
||||||
|
}
|
||||||
|
// if (listen_addresses) {
|
||||||
|
// //Check for overlapping addresses
|
||||||
|
// for (size_t i = 0; i < num_listen_addresses; i++) {
|
||||||
|
// for (size_t j = 0; j < num_addr; j++) {
|
||||||
|
// if (strcmp(*(listen_addresses + i), *(listen + j)) == 0) {
|
||||||
|
// fprintf(err_log, "systemd-sslh-generator: Overlapping listen addresses across sslh configurations!");
|
||||||
|
// return -1;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// char *listen_addresses_copy[num_listen_addresses + num_addr];
|
||||||
|
// if (listen_addresses) {
|
||||||
|
// //Copy previous configurations' listen addresses into temp
|
||||||
|
// memcpy(listen_addresses_copy, listen_addresses, num_listen_addresses);
|
||||||
|
// //Free global list of listen addresses
|
||||||
|
// free(listen_addresses);
|
||||||
|
// }
|
||||||
|
// //Copy listen addresses from current configuration into temp
|
||||||
|
// memcpy(listen_addresses_copy + num_listen_addresses, listen, num_addr);
|
||||||
|
// num_listen_addresses += num_addr;
|
||||||
|
// listen_addresses = malloc(num_listen_addresses * sizeof(char *));
|
||||||
|
// CHECK_ALLOC(listen_addresses, "malloc")
|
||||||
|
// //Append all addresses to global list
|
||||||
|
// memcpy(listen_addresses, listen_addresses_copy, num_listen_addresses);
|
||||||
|
//Free all allocated listen strings
|
||||||
|
for (size_t i = 0; i < num_addr; i++) {
|
||||||
|
free(*(listen + i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while((fa = fa->prev));
|
||||||
|
free_file_list(fa_ref);
|
||||||
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
int main(int argc, char *argv[]){
|
if (argc == 1 || argc == 4) {
|
||||||
int r = 0;
|
systemd_invoked = argc == 4;
|
||||||
int k;
|
if (systemd_invoked) {
|
||||||
char *runtime_unit_dest = "";
|
err_log = fopen("/dev/kmsg", "w");
|
||||||
|
if (!err_log) {
|
||||||
if (argc > 1 && (argc != 4) ) {
|
return -1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err_log = stderr;
|
||||||
|
}
|
||||||
|
const int r = gen_sslh_config(systemd_invoked ? argv[1] : "");
|
||||||
|
if (systemd_invoked) {
|
||||||
|
fclose(err_log);
|
||||||
|
}
|
||||||
|
if (!r) {
|
||||||
|
fprintf(err_log, "systemd-sslh-generator: Successfully generated all targets.\n");
|
||||||
|
}
|
||||||
|
return r < 0 ? -1 : 0;
|
||||||
|
} else {
|
||||||
printf("This program takes three or no arguments.\n");
|
printf("This program takes three or no arguments.\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argc > 1)
|
|
||||||
runtime_unit_dest = argv[1];
|
|
||||||
|
|
||||||
k = gen_sslh_config(runtime_unit_dest);
|
|
||||||
if (k < 0)
|
|
||||||
r = k;
|
|
||||||
|
|
||||||
return r < 0 ? -1 : 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user