diff --git a/config.ini.sample b/config.ini.sample index 1d678ad..0c2bef0 100644 --- a/config.ini.sample +++ b/config.ini.sample @@ -1,130 +1,135 @@ ; SylkServer configuration file [Server] ; The following settings are the default used by the software, uncomment ; them only if you want to make changes ; default_application = conference ; Statically map a Request URI to a specific application. In the example ; below, 123 is matched 1st against the domain part, than the username part ; of the Request URI This static mapping can be overwritten by adding ; X-Sylk-App header set to the value of a valid SylkServer application name ; application_map = echo:echo,123:conference,test:ircconference,gmail.com:xmppgateway ; Disable the specified applications ; disabled_applications = ; Directory where extra applications are stored ; extra_applications_dir = trace_dir = /var/log/sylkserver ; trace_core = False ; trace_sip = False ; trace_msrp = False ; trace_notifications = False ; TLS is used by default for SIP signaling and MSRP media using a ; self-signed certificate. You may want to use a properly signed X.509 ; certificate and configure it below ; The X.509 Certificate Authorities file ca_file = /etc/sylkserver/tls/ca.crt ; The file containing X.509 certificate and private key in unencrypted format certificate = /etc/sylkserver/tls/default.crt ; verify_server = False ; Enable Bonjour capabilities for applications ; enable_bonjour = False ; Base directory for files created by the server, excluding log files ; spool_dir = /var/spool/sylkserver [SIP] ; SIP transport settings ; IP address used for SIP signaling and RTP media; an empty string or 'any' means listening on ; the interface used by the default route ; local_ip = ; IP address to be advertised in the SDP, useful in 1-to-1 NAT scenarios such as Amazon EC2 ; advertised_ip = ; Ports used for SIP transports, if not set to any value the transport will be disabled ; local_udp_port = 5060 ; local_tcp_port = 5060 ; local_tls_port = 5061 ; If set, all outbound SIP requests will be sent through this SIP proxy ; The proxy address format is: proxy.example.com:5061;transport=tls ; Transport can be udp, tcp or tls, if skipped it is considered udp ; If only the hostname is set, RFC3263 lookups are performed to lookup ; the outbound proxy server address ; outbound_proxy = ; A comma-separated list of hosts or networks to trust. ; The elements can be an IP address in CIDR format, a ; hostname or an IP address (in the latter 2 a mask of 32 ; is assumed), or the special keywords 'any' and 'none' ; (being equivalent to 0.0.0.0/0 and 0.0.0.0/32 ; respectively). It defaults to 'any'. ; trusted_peers = ; Toggle ICE support (RFC 5245) ; enable_ice = False [MSRP] ; MSRP transport settings ; A valid X.509 certificate is required for MSRP to work over TLS. ; TLS is enabled by default, a default TLS certificate is provided with SylkServer. ; use_tls = True [RTP] ; RTP transport settings ; Allowed codec list, valid values: opus, G722, speex, PCMU, PCMA, iLBC, GSM ; audio_codecs = opus,speex,G722,PCMU,PCMA ; Port range used for RTP ; port_range = 50000:50500 ; SRTP valid values: disabled, sdes, zrtp, opportunistic ; srtp_encryption = opportunistic ; RTP stream timeout, session will be disconnected after this value ; timeout = 30 ; Audio sampling rate ; sample_rate = 32000 [WebServer] ; Global web server settings ; IP address used for serving HTTP(S) requests, empty string ; means listen on interface used by the default route ; local_ip = ; Port where the web server will listen on, set to 0 for random ; local_port = 10888 ; X.509 server certificate for HTTPS connections, the certificate private ; key must be concatenated inside the same file, set to empty in order to ; disable HTTPS ; certificate = /etc/sylkserver/tls/default.crt +; Certificat chain file containing all the certificates that the server +; should present to the client. If specified, it must also contain the +; certificate in the file specified by the 'certificate' option. +; certificate_chain + ; Hostname used when publishing the server URL. Must match the common name ; of server X.509 certificate set above, otherwise clients will raise ; warning. If not set the listening IP address will be used ; hostname = diff --git a/sylk/configuration/__init__.py b/sylk/configuration/__init__.py index 2c0514e..b3a1460 100644 --- a/sylk/configuration/__init__.py +++ b/sylk/configuration/__init__.py @@ -1,86 +1,87 @@ from application.configuration import ConfigSection, ConfigSetting from application.configuration.datatypes import NetworkRangeList, StringList from application.system import host from sipsimple.configuration.datatypes import NonNegativeInteger, SampleRate from sylk import configuration_filename from sylk.configuration.datatypes import AudioCodecs, IPAddress, Path, Port, PortRange, SIPProxyAddress, SRTPEncryption from sylk.resources import Resources, VarResources from sylk.tls import Certificate, PrivateKey class ServerConfig(ConfigSection): __cfgfile__ = configuration_filename __section__ = 'Server' ca_file = ConfigSetting(type=Path, value=Path(Resources.get('tls/ca.crt'))) certificate = ConfigSetting(type=Path, value=Path(Resources.get('tls/default.crt'))) verify_server = False enable_bonjour = False default_application = 'conference' application_map = ConfigSetting(type=StringList, value=['echo:echo']) disabled_applications = ConfigSetting(type=StringList, value='') extra_applications_dir = ConfigSetting(type=Path, value=None) trace_dir = ConfigSetting(type=Path, value=Path(VarResources.get('log/sylkserver'))) trace_core = False trace_sip = False trace_msrp = False trace_notifications = False spool_dir = ConfigSetting(type=Path, value=Path(VarResources.get('spool/sylkserver'))) class SIPConfig(ConfigSection): __cfgfile__ = configuration_filename __section__ = 'SIP' local_ip = ConfigSetting(type=IPAddress, value=IPAddress(host.default_ip)) local_udp_port = ConfigSetting(type=Port, value=5060) local_tcp_port = ConfigSetting(type=Port, value=5060) local_tls_port = ConfigSetting(type=Port, value=5061) advertised_ip = ConfigSetting(type=IPAddress, value=None) outbound_proxy = ConfigSetting(type=SIPProxyAddress, value=None) trusted_peers = ConfigSetting(type=NetworkRangeList, value=NetworkRangeList('any')) enable_ice = False class MSRPConfig(ConfigSection): __cfgfile__ = configuration_filename __section__ = 'MSRP' use_tls = True class RTPConfig(ConfigSection): __cfgfile__ = configuration_filename __section__ = 'RTP' audio_codecs = ConfigSetting(type=AudioCodecs, value=['opus', 'speex', 'G722', 'PCMA', 'PCMU']) port_range = ConfigSetting(type=PortRange, value=PortRange('50000:50500')) srtp_encryption = ConfigSetting(type=SRTPEncryption, value='opportunistic') timeout = ConfigSetting(type=NonNegativeInteger, value=30) sample_rate = ConfigSetting(type=SampleRate, value=32000) class WebServerConfig(ConfigSection): __cfgfile__ = configuration_filename __section__ = 'WebServer' local_ip = ConfigSetting(type=IPAddress, value=IPAddress(host.default_ip)) local_port = ConfigSetting(type=Port, value=10888) hostname = '' certificate = ConfigSetting(type=Path, value=Path(Resources.get('tls/default.crt'))) + certificate_chain = ConfigSetting(type=Path, value=None) class ThorNodeConfig(ConfigSection): __cfgfile__ = configuration_filename __section__ = 'ThorNetwork' enabled = False domain = "sipthor.net" multiply = 1000 certificate = ConfigSetting(type=Certificate, value=None) private_key = ConfigSetting(type=PrivateKey, value=None) ca = ConfigSetting(type=Certificate, value=None) diff --git a/sylk/web.py b/sylk/web.py index 4ddff03..80b3492 100644 --- a/sylk/web.py +++ b/sylk/web.py @@ -1,81 +1,93 @@ __all__ = ['Klein', 'StaticFileResource', 'WebServer', 'server'] import os from application import log from application.python.types import Singleton from klein import Klein from twisted.internet import reactor from twisted.internet.ssl import DefaultOpenSSLContextFactory from twisted.web.resource import Resource, NoResource from twisted.web.server import Site from twisted.web.static import File from sylk import __version__ from sylk.configuration import WebServerConfig # Set the 'Server' header string which Twisted Web will use import twisted.web.server twisted.web.server.version = b'SylkServer/%s' % __version__ class StaticFileResource(File): def directoryListing(self): return NoResource('Directory listing not available') class RootResource(Resource): isLeaf = True def render_GET(self, request): request.setHeader('Content-Type', 'text/plain') return 'Welcome to SylkServer!' class WebServer(object): __metaclass__ = Singleton def __init__(self): self.base = Resource() self.base.putChild('', RootResource()) self.site = Site(self.base, logPath=os.devnull) self.site.noisy = False self.listener = None @property def url(self): return self.__dict__.get('url', '') def register_resource(self, path, resource): self.base.putChild(path, resource) def start(self): interface = WebServerConfig.local_ip port = WebServerConfig.local_port cert_path = WebServerConfig.certificate.normalized if WebServerConfig.certificate else None + cert_chain_path = WebServerConfig.certificate_chain.normalized if WebServerConfig.certificate_chain else None if cert_path is not None: if not os.path.isfile(cert_path): log.error('Certificate file %s could not be found' % cert_path) return try: - ssl_context = DefaultOpenSSLContextFactory(cert_path, cert_path) + ssl_ctx_factory = DefaultOpenSSLContextFactory(cert_path, cert_path) except Exception: - log.exception('Creating SSL context') + log.exception('Creating TLS context') log.err() return - self.listener = reactor.listenSSL(port, self.site, ssl_context, backlog=511, interface=interface) + if cert_chain_path is not None: + if not os.path.isfile(cert_chain_path): + log.error('Certificate chain file %s could not be found' % cert_chain_path) + return + ssl_ctx = ssl_ctx_factory.getContext() + try: + ssl_ctx.use_certificate_chain_file(cert_chain_path) + except Exception: + log.exception('Setting TLS certificate chain file') + log.err() + return + self.listener = reactor.listenSSL(port, self.site, ssl_ctx_factory, backlog=511, interface=interface) scheme = 'https' else: self.listener = reactor.listenTCP(port, self.site, backlog=511, interface=interface) scheme = 'http' port = self.listener.getHost().port self.__dict__['url'] = '%s://%s:%d' % (scheme, WebServerConfig.hostname or interface.normalized, port) log.msg('Web server listening for requests on: %s' % self.url) def stop(self): if self.listener is not None: self.listener.stopListening() server = WebServer()