mirror of
https://github.com/asterisk/asterisk.git
synced 2025-10-26 22:30:28 +00:00
Merge "Core: Add support for systemd socket activation." into 15
This commit is contained in:
119
contrib/systemd/README.txt
Normal file
119
contrib/systemd/README.txt
Normal file
@@ -0,0 +1,119 @@
|
||||
SystemD Socket Activation for Asterisk
|
||||
======================================
|
||||
|
||||
This folder contains sample unit files which can be used as the basis of a
|
||||
socket activated Asterisk deployment. Socket activation support currently
|
||||
extends to the following listeners:
|
||||
|
||||
* Asterisk Command-line Interface
|
||||
* Asterisk Manager Interface (clear text and TLS)
|
||||
* Builtin HTTP / HTTPS server
|
||||
|
||||
The primary use case of this feature is to allow Asterisk to be started by
|
||||
other services through use of AMI, CLI or REST API.
|
||||
|
||||
|
||||
Security
|
||||
========
|
||||
|
||||
Care must be take if enabling socket activation on any IP:PORT that is not
|
||||
protected by a firewall. Any user that can reach any socket activation
|
||||
port can start Asterisk, even if they do not have valid credentials to sign
|
||||
into the service in question. Enabling HTTP socket activation on a system
|
||||
which provides SIP over websockets would allow remote users to start Asterisk
|
||||
any time the HTTP socket is running.
|
||||
|
||||
This functionality bypasses the normal restriction where only 'root' can start
|
||||
a service. Enabling AMI socket activation allows any user on the local server
|
||||
to start Asterisk by running 'telnet localhost 5038'.
|
||||
|
||||
CLI activation is secured by the combination of SocketUser, SocketGroup and
|
||||
SocketMode settings in the systemd socket. Only local users with access will
|
||||
be able to start asterisk by using CLI.
|
||||
|
||||
|
||||
Separate .socket units or a single unit
|
||||
=======================================
|
||||
|
||||
Asterisk is a complex system with many components which can be enabled or
|
||||
disabled individually. Using socket activation requires deciding to use
|
||||
a single socket file or multiple separate socket files.
|
||||
|
||||
The remainder of this README assumes separate socket units are used for each
|
||||
listener.
|
||||
|
||||
|
||||
Service and Socket files
|
||||
========================
|
||||
|
||||
All .socket and .service examples in this folder use "reasonable" default
|
||||
paths for Linux. Depending on your distribution and ./configure options
|
||||
you may need to modify these before installing. The files are meant to
|
||||
be examples rather than files to be blindly installed.
|
||||
|
||||
|
||||
Installing and enabling socket units
|
||||
====================================
|
||||
|
||||
Modify socket files as desired. Install them to a location where systemd
|
||||
will find them. pkg-config can be used to determine an appropriate location.
|
||||
|
||||
For socket files to be managed directly by the local administrator:
|
||||
pkg-config systemd --variable systemdsystemconfdir
|
||||
|
||||
For socket files to be deployed by package manager:
|
||||
pkg-config systemd --variable systemdsystemunitdir
|
||||
|
||||
|
||||
After installing socket files you must run 'systemctl daemon-reload' for
|
||||
systemd to read the added/modified units. After this you can enable the
|
||||
desired sockets, for example to enable AMI:
|
||||
systemctl enable asterisk-ami.socket
|
||||
|
||||
|
||||
Socket Selection
|
||||
================
|
||||
|
||||
Asterisk configuration is unchanged by use of socket activation. When a
|
||||
component that supports socket activation starts a listener in Asterisk,
|
||||
any sockets provided by systemd are iterated. The systemd socket is used
|
||||
when the bound address configured by Asterisk is an exact match with the
|
||||
address given by the ListenStream setting in the systemd socket.
|
||||
|
||||
|
||||
Command-line Interface
|
||||
======================
|
||||
|
||||
Symbolic links do not appear to be resolved when checking the CLI listener.
|
||||
This may be of concern since /var/run is often a symbolic link to /run. Both
|
||||
Asterisk and systemd must use /var/run, or both must use /run. Mismatching
|
||||
will result in service startup failure.
|
||||
|
||||
When socket activation is used for Asterisk CLI some asterisk.conf options
|
||||
are ignored. The following options from the [files] section are ignored
|
||||
and must instead be set by the systemd socket file.
|
||||
* astctlowner - use SocketUser
|
||||
* astctlgroup - use SocketGroup
|
||||
* astctlpermissions - use SocketMode
|
||||
|
||||
See asterisk-cli.socket for an example of these settings.
|
||||
|
||||
|
||||
Stopping Asterisk
|
||||
=================
|
||||
|
||||
Some existing asterisk.service files use CLI 'core stop now' for the ExecStop
|
||||
command. It is not recommended to use CLI to stop Asterisk on systems where
|
||||
CLI socket activation is enabled. If Asterisk fails to start systemd still
|
||||
tries running the ExecStop command. This can result in an loop where ExecStop
|
||||
causes CLI socket activation to start Asterisk again. A better way to deal
|
||||
with shutdown is to use Type=notify and do not specify an ExecStop command.
|
||||
See the example asterisk.service.
|
||||
|
||||
|
||||
Unused Sockets
|
||||
==============
|
||||
|
||||
Asterisk makes no attempt to check for sockets provided by systemd that are not
|
||||
used. It is the users responsibility to only provide sockets which Asterisk is
|
||||
configured to use.
|
||||
10
contrib/systemd/asterisk-ami.socket
Normal file
10
contrib/systemd/asterisk-ami.socket
Normal file
@@ -0,0 +1,10 @@
|
||||
[Unit]
|
||||
Description=Asterisk Manager Interface Socket
|
||||
|
||||
[Socket]
|
||||
Service=asterisk.service
|
||||
ListenStream=0.0.0.0:5038
|
||||
|
||||
[Install]
|
||||
WantedBy=sockets.target
|
||||
RequiredBy=asterisk.service
|
||||
10
contrib/systemd/asterisk-amis.socket
Normal file
10
contrib/systemd/asterisk-amis.socket
Normal file
@@ -0,0 +1,10 @@
|
||||
[Unit]
|
||||
Description=Asterisk Manager Interface TLS Socket
|
||||
|
||||
[Socket]
|
||||
Service=asterisk.service
|
||||
ListenStream=0.0.0.0:5039
|
||||
|
||||
[Install]
|
||||
WantedBy=sockets.target
|
||||
RequiredBy=asterisk.service
|
||||
13
contrib/systemd/asterisk-cli.socket
Normal file
13
contrib/systemd/asterisk-cli.socket
Normal file
@@ -0,0 +1,13 @@
|
||||
[Unit]
|
||||
Description=Asterisk Command-line Interface Socket
|
||||
|
||||
[Socket]
|
||||
Service=asterisk.service
|
||||
ListenStream=/var/run/asterisk/asterisk.ctl
|
||||
SocketUser=asterisk
|
||||
SocketGroup=asterisk
|
||||
SocketMode=0660
|
||||
|
||||
[Install]
|
||||
WantedBy=sockets.target
|
||||
RequiredBy=asterisk.service
|
||||
11
contrib/systemd/asterisk-http.socket
Normal file
11
contrib/systemd/asterisk-http.socket
Normal file
@@ -0,0 +1,11 @@
|
||||
[Unit]
|
||||
Description=Asterisk HTTP Socket
|
||||
|
||||
[Socket]
|
||||
Service=asterisk.service
|
||||
FreeBind=true
|
||||
ListenStream=127.0.0.1:8088
|
||||
|
||||
[Install]
|
||||
WantedBy=sockets.target
|
||||
RequiredBy=asterisk.service
|
||||
11
contrib/systemd/asterisk-https.socket
Normal file
11
contrib/systemd/asterisk-https.socket
Normal file
@@ -0,0 +1,11 @@
|
||||
[Unit]
|
||||
Description=Asterisk HTTPS Socket
|
||||
|
||||
[Socket]
|
||||
Service=asterisk.service
|
||||
FreeBind=true
|
||||
ListenStream=127.0.0.1:8089
|
||||
|
||||
[Install]
|
||||
WantedBy=sockets.target
|
||||
RequiredBy=asterisk.service
|
||||
27
contrib/systemd/asterisk.service
Normal file
27
contrib/systemd/asterisk.service
Normal file
@@ -0,0 +1,27 @@
|
||||
[Unit]
|
||||
Description=Asterisk PBX and telephony daemon.
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=notify
|
||||
Environment=HOME=/var/lib/asterisk
|
||||
WorkingDirectory=/var/lib/asterisk
|
||||
User=asterisk
|
||||
Group=asterisk
|
||||
ExecStart=/usr/sbin/asterisk -mqf -C /etc/asterisk/asterisk.conf
|
||||
ExecReload=/usr/sbin/asterisk -rx 'core reload'
|
||||
|
||||
#Nice=0
|
||||
#UMask=0002
|
||||
LimitCORE=infinity
|
||||
#LimitNOFILE=
|
||||
Restart=always
|
||||
RestartSec=4
|
||||
|
||||
# Prevent duplication of logs with color codes to /var/log/messages
|
||||
StandardOutput=null
|
||||
|
||||
PrivateTmp=true
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
26
contrib/systemd/asterisk.socket
Normal file
26
contrib/systemd/asterisk.socket
Normal file
@@ -0,0 +1,26 @@
|
||||
[Unit]
|
||||
Description=Asterisk Sockets
|
||||
|
||||
[Socket]
|
||||
FreeBind=true
|
||||
SocketUser=asterisk
|
||||
SocketGroup=asterisk
|
||||
SocketMode=0660
|
||||
|
||||
# CLI
|
||||
ListenStream=/var/run/asterisk/asterisk.ctl
|
||||
# AMI
|
||||
ListenStream=0.0.0.0:5038
|
||||
# AMIS
|
||||
ListenStream=0.0.0.0:5039
|
||||
# HTTP
|
||||
ListenStream=127.0.0.1:8088
|
||||
# HTTPS
|
||||
ListenStream=127.0.0.1:8089
|
||||
# chan_sip TCP
|
||||
ListenStream=0.0.0.0:5060
|
||||
# chan_sip TLS
|
||||
ListenStream=0.0.0.0:5061
|
||||
|
||||
[Install]
|
||||
WantedBy=sockets.target
|
||||
@@ -24,6 +24,7 @@
|
||||
#define _ASTERISK_IO_H
|
||||
|
||||
#include "asterisk/poll-compat.h"
|
||||
#include "asterisk/netsock2.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
@@ -148,6 +149,29 @@ int ast_get_termcols(int fd);
|
||||
*/
|
||||
int ast_sd_notify(const char *state);
|
||||
|
||||
/*!
|
||||
* \brief Find a listening file descriptor provided by socket activation.
|
||||
* \param type SOCK_STREAM or SOCK_DGRAM
|
||||
* \param addr The socket address of the bound listener.
|
||||
* \retval <0 No match.
|
||||
* \retval >0 File Descriptor matching sockaddr.
|
||||
*
|
||||
* \note This function returns -1 if systemd's development headers were not
|
||||
* detected on the system.
|
||||
*/
|
||||
int ast_sd_get_fd(int type, const struct ast_sockaddr *addr);
|
||||
|
||||
/*!
|
||||
* \brief Find a listening AF_LOCAL file descriptor provided by socket activation.
|
||||
* \param type SOCK_STREAM or SOCK_DGRAM
|
||||
* \param path The path of the listener.
|
||||
* \retval <0 No match.
|
||||
* \retval >0 File Descriptor matching path.
|
||||
*
|
||||
* \note This function returns -1 if systemd's development headers were not
|
||||
* detected on the system.
|
||||
*/
|
||||
int ast_sd_get_fd_un(int type, const char *path);
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
}
|
||||
|
||||
@@ -128,6 +128,22 @@ static inline void ast_sockaddr_setnull(struct ast_sockaddr *addr)
|
||||
addr->len = 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief
|
||||
* Copies the data from a sockaddr to an ast_sockaddr
|
||||
*
|
||||
* \param dst The destination ast_sockaddr
|
||||
* \param src The source sockaddr
|
||||
* \param len Length of the value stored in sockaddr
|
||||
* \retval void
|
||||
*/
|
||||
static inline void ast_sockaddr_copy_sockaddr(struct ast_sockaddr *dst,
|
||||
struct sockaddr *src, socklen_t len)
|
||||
{
|
||||
memcpy(dst, src, len);
|
||||
dst->len = len;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \since 1.8
|
||||
*
|
||||
|
||||
@@ -350,6 +350,7 @@ struct ast_eid ast_eid_default;
|
||||
char record_cache_dir[AST_CACHE_DIR_LEN] = DEFAULT_TMP_DIR;
|
||||
|
||||
static int ast_socket = -1; /*!< UNIX Socket for allowing remote control */
|
||||
static int ast_socket_is_sd = 0; /*!< Is socket activation responsible for ast_socket? */
|
||||
static int ast_consock = -1; /*!< UNIX Socket for controlling another asterisk */
|
||||
pid_t ast_mainpid;
|
||||
struct console {
|
||||
@@ -1576,8 +1577,16 @@ static int ast_makesocket(void)
|
||||
uid_t uid = -1;
|
||||
gid_t gid = -1;
|
||||
|
||||
for (x = 0; x < AST_MAX_CONNECTS; x++)
|
||||
for (x = 0; x < AST_MAX_CONNECTS; x++) {
|
||||
consoles[x].fd = -1;
|
||||
}
|
||||
|
||||
if (ast_socket_is_sd) {
|
||||
ast_socket = ast_sd_get_fd_un(SOCK_STREAM, ast_config_AST_SOCKET);
|
||||
|
||||
goto start_lthread;
|
||||
}
|
||||
|
||||
unlink(ast_config_AST_SOCKET);
|
||||
ast_socket = socket(PF_LOCAL, SOCK_STREAM, 0);
|
||||
if (ast_socket < 0) {
|
||||
@@ -1602,12 +1611,19 @@ static int ast_makesocket(void)
|
||||
return -1;
|
||||
}
|
||||
|
||||
start_lthread:
|
||||
if (ast_pthread_create_background(<hread, NULL, listener, NULL)) {
|
||||
ast_log(LOG_WARNING, "Unable to create listener thread.\n");
|
||||
close(ast_socket);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ast_socket_is_sd) {
|
||||
/* owner/group/permissions are set by systemd, we might not even have access
|
||||
* to socket file so leave it alone */
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!ast_strlen_zero(ast_config_AST_CTL_OWNER)) {
|
||||
struct passwd *pw;
|
||||
if ((pw = getpwnam(ast_config_AST_CTL_OWNER)) == NULL)
|
||||
@@ -2075,7 +2091,9 @@ static void really_quit(int num, shutdown_nice_t niceness, int restart)
|
||||
pthread_cancel(lthread);
|
||||
close(ast_socket);
|
||||
ast_socket = -1;
|
||||
if (!ast_socket_is_sd) {
|
||||
unlink(ast_config_AST_SOCKET);
|
||||
}
|
||||
pthread_kill(lthread, SIGURG);
|
||||
pthread_join(lthread, NULL);
|
||||
}
|
||||
@@ -4319,7 +4337,12 @@ int main(int argc, char *argv[])
|
||||
/* Initial value of the maximum active system verbosity level. */
|
||||
ast_verb_sys_level = option_verbose;
|
||||
|
||||
if (ast_tryconnect()) {
|
||||
if (ast_sd_get_fd_un(SOCK_STREAM, ast_config_AST_SOCKET) > 0) {
|
||||
ast_socket_is_sd = 1;
|
||||
}
|
||||
|
||||
/* DO NOT perform check for existing daemon if systemd has CLI socket activation */
|
||||
if (!ast_socket_is_sd && ast_tryconnect()) {
|
||||
/* One is already running */
|
||||
if (ast_opt_remote) {
|
||||
multi_thread_safe = 1;
|
||||
|
||||
74
main/io.c
74
main/io.c
@@ -36,6 +36,10 @@
|
||||
#include "asterisk/utils.h"
|
||||
#ifdef HAVE_SYSTEMD
|
||||
#include <systemd/sd-daemon.h>
|
||||
|
||||
#ifndef SD_LISTEN_FDS_START
|
||||
#define SD_LISTEN_FDS_START 3
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_IO
|
||||
@@ -392,3 +396,73 @@ int ast_sd_notify(const char *state) {
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal \brief Check the type and sockaddr of a file descriptor.
|
||||
* \param fd File Descriptor to check.
|
||||
* \param type SOCK_STREAM or SOCK_DGRAM
|
||||
* \param addr The socket address to match.
|
||||
* \retval 0 if matching
|
||||
* \retval -1 if not matching
|
||||
*/
|
||||
#ifdef HAVE_SYSTEMD
|
||||
static int ast_sd_is_socket_sockaddr(int fd, int type, const struct ast_sockaddr* addr)
|
||||
{
|
||||
int canretry = 1;
|
||||
struct ast_sockaddr fd_addr;
|
||||
struct sockaddr ss;
|
||||
socklen_t ss_len;
|
||||
|
||||
if (sd_is_socket(fd, AF_UNSPEC, type, 1) <= 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
doretry:
|
||||
if (getsockname(fd, &ss, &ss_len) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ss.sa_family == AF_UNSPEC && canretry) {
|
||||
/* An unknown bug can cause silent failure from
|
||||
* the first call to getsockname. */
|
||||
canretry = 0;
|
||||
goto doretry;
|
||||
}
|
||||
|
||||
ast_sockaddr_copy_sockaddr(&fd_addr, &ss, ss_len);
|
||||
|
||||
return ast_sockaddr_cmp(addr, &fd_addr);
|
||||
}
|
||||
#endif
|
||||
|
||||
int ast_sd_get_fd(int type, const struct ast_sockaddr *addr)
|
||||
{
|
||||
#ifdef HAVE_SYSTEMD
|
||||
int count = sd_listen_fds(0);
|
||||
int idx;
|
||||
|
||||
for (idx = 0; idx < count; idx++) {
|
||||
if (!ast_sd_is_socket_sockaddr(idx + SD_LISTEN_FDS_START, type, addr)) {
|
||||
return idx + SD_LISTEN_FDS_START;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ast_sd_get_fd_un(int type, const char *path)
|
||||
{
|
||||
#ifdef HAVE_SYSTEMD
|
||||
int count = sd_listen_fds(0);
|
||||
int idx;
|
||||
|
||||
for (idx = 0; idx < count; idx++) {
|
||||
if (sd_is_socket_unix(idx + SD_LISTEN_FDS_START, type, 1, path, 0) > 0) {
|
||||
return idx + SD_LISTEN_FDS_START;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
|
||||
#include "asterisk/compat.h"
|
||||
#include "asterisk/tcptls.h"
|
||||
#include "asterisk/io.h"
|
||||
#include "asterisk/http.h"
|
||||
#include "asterisk/utils.h"
|
||||
#include "asterisk/strings.h"
|
||||
@@ -618,6 +619,7 @@ void ast_tcptls_server_start(struct ast_tcptls_session_args *desc)
|
||||
int flags;
|
||||
int x = 1;
|
||||
int tls_changed = 0;
|
||||
int sd_socket;
|
||||
|
||||
if (desc->tls_cfg) {
|
||||
char hash[41];
|
||||
@@ -689,6 +691,19 @@ void ast_tcptls_server_start(struct ast_tcptls_session_args *desc)
|
||||
pthread_join(desc->master, NULL);
|
||||
}
|
||||
|
||||
sd_socket = ast_sd_get_fd(SOCK_STREAM, &desc->local_address);
|
||||
|
||||
if (sd_socket != -1) {
|
||||
if (desc->accept_fd != sd_socket) {
|
||||
if (desc->accept_fd != -1) {
|
||||
close(desc->accept_fd);
|
||||
}
|
||||
desc->accept_fd = sd_socket;
|
||||
}
|
||||
|
||||
goto systemd_socket_activation;
|
||||
}
|
||||
|
||||
if (desc->accept_fd != -1) {
|
||||
close(desc->accept_fd);
|
||||
}
|
||||
@@ -718,6 +733,8 @@ void ast_tcptls_server_start(struct ast_tcptls_session_args *desc)
|
||||
ast_log(LOG_ERROR, "Unable to listen for %s!\n", desc->name);
|
||||
goto error;
|
||||
}
|
||||
|
||||
systemd_socket_activation:
|
||||
flags = fcntl(desc->accept_fd, F_GETFL);
|
||||
fcntl(desc->accept_fd, F_SETFL, flags | O_NONBLOCK);
|
||||
if (ast_pthread_create_background(&desc->master, NULL, desc->accept_fn, desc)) {
|
||||
|
||||
Reference in New Issue
Block a user