Caution
Buildbot no longer supports Python 2.7 on the Buildbot master.
2.5.16. Web Server
Note
As of Buildbot 0.9.0, the built-in web server replaces the old WebStatus
plugin.
Buildbot contains a built-in web server.
This server is configured with the www
configuration key, which specifies a dictionary with the following keys:
port
The TCP port on which to serve requests. It might be an integer or any string accepted by serverFromString (ex: “tcp:8010:interface=127.0.0.1” to listen on another interface). Note that using twisted’s SSL endpoint is discouraged. Use a reverse proxy that offers proper SSL hardening instead (see Reverse Proxy Configuration). If this is
None
(the default), then the master will not implement a web server.json_cache_seconds
The number of seconds into the future at which an HTTP API response should expire.
rest_minimum_version
The minimum supported REST API version. Any versions less than this value will not be available. This can be used to ensure that no clients are depending on API versions that will soon be removed from Buildbot.
plugins
This key gives a dictionary of additional UI plugins to load, along with configuration for those plugins. These plugins must be separately installed in the Python environment, e.g.,
pip install buildbot-waterfall-view
. See UI plugins. For example:c['www'] = { 'plugins': {'waterfall_view': True} }
default_page
Configure the default landing page of the web server, for example, to forward directly to another plugin. For example:
c['www']['default_page'] = 'console'
debug
If true, then debugging information will be output to the browser. This is best set to false (the default) on production systems, to avoid the possibility of information leakage.
allowed_origins
This gives a list of origins which are allowed to access the Buildbot API (including control via JSONRPC 2.0). It implements cross-origin request sharing (CORS), allowing pages at origins other than the Buildbot UI to use the API. Each origin is interpreted as filename match expression, with
?
matching one character and*
matching anything. Thus['*']
will match all origins, and['https://*.buildbot.net']
will match secure sites underbuildbot.net
. The Buildbot UI will operate correctly without this parameter; it is only useful for allowing access from other web applications.auth
Authentication module to use for the web server. See Authentication plugins.
avatar_methods
List of methods that can be used to get avatar pictures to use for the web server. By default, Buildbot uses Gravatar to get images associated with each users, if you want to disable this you can just specify empty list:
c['www'] = { 'avatar_methods': [] }
You could also use the GitHub user avatar if GitHub authentication is enabled:
c['www'] = { 'avatar_methods': [util.AvatarGitHub()] }
- class AvatarGitHub(github_api_endpoint=None, token=None, debug=False, verify=False)
- Parameters:
github_api_endpoint (string) – specify the github api endpoint if you work with GitHub Enterprise
token (string) – a GitHub API token to execute all requests to the API authenticated. It is strongly recommended to use a API token since it increases GitHub API rate limits significantly
client_id (string) – a GitHub OAuth client ID to use with client secret to execute all requests to the API authenticated in place of token
client_secret (string) – a GitHub OAuth client secret to use with client ID above
debug (boolean) – logs every requests and their response
verify (boolean) – disable ssl verification for the case you use temporary self signed certificates on a GitHub Enterprise installation
This class requires txrequests package to allow interaction with GitHub REST API.
For use of corporate pictures, you can use LdapUserInfo, which can also act as an avatar provider. See Authentication plugins.
logfileName
Filename used for HTTP access logs, relative to the master directory. If set to
None
or the empty string, the content of the logs will land in the maintwisted.log
log file. (Defaults tohttp.log
)logRotateLength
The amount of bytes after which the
http.log
file will be rotated. (Defaults to the same value as for thetwisted.log
file, set inbuildbot.tac
)maxRotatedFiles
The amount of log files that will be kept when rotating (Defaults to the same value as for the
twisted.log
file, set inbuildbot.tac
)versions
Custom component versions that you’d like to display on the About page. Buildbot will automatically prepend the versions of Python, twisted and Buildbot itself to the list.
versions
should be a list of tuples. For example:c['www'] = { # ... 'versions': [ ('master.cfg', '0.1'), ('OS', 'Ubuntu 14.04'), ] }
The first element of a tuple stands for the name of the component, the second stands for the corresponding version.
custom_templates_dir
This directory will be parsed for custom angularJS templates to replace the one of the original website templates. You can use this to slightly customize buildbot look for your project, but to add any logic, you will need to create a full-blown plugin. If the directory string is relative, it will be joined to the master’s basedir. Buildbot uses the jade file format natively (which has been renamed to ‘pug’ in the nodejs ecosystem), but you can also use HTML format if you prefer.
Either
*.jade
files or*.html
files can be used to override templates with the same name in the UI. On the regular nodejs UI build system, we use nodejs’s pug module to compile jade into html. For custom_templates, we use the pypugjs interpreter to parse the jade templates, before sending them to the UI.pip install pypugjs
is required to use jade templates. You can also override plugin’s directives, but they have to be in another directory, corresponding to the plugin’s name in itspackage.json
. For example:# replace the template whose source is in: # www/base/src/app/builders/build/build.tpl.jade build.jade # here we use a jade (aka pug) file # replace the template whose source is in # www/console_view/src/module/view/builders-header/console.tpl.jade console_view/console.html # here we use html format
Known differences between nodejs’s pug and pyjade:
quotes in attributes are not quoted (https://github.com/syrusakbary/pyjade/issues/132). This means you should use double quotes for attributes, e.g.:
tr(ng-repeat="br in buildrequests | orderBy:'-submitted_at'")
pypugjs may have some differences but it is a maintained fork of pyjade. https://github.com/kakulukia/pypugjs
change_hook_dialects
See Change Hooks.
cookie_expiration_time
This allows to define the timeout of the session cookie. Should be a datetime.timedelta. Default is one week.
import datetime c['www'] = { # ... 'cookie_expiration_time': datetime.timedelta(weeks=2) }
ui_default_config
Settings in the settings page are stored per browser. This configuration parameter allows to override the default settings for all your users. If a user already has changed a value from the default, this will have no effect to them. The settings page in the UI will tell you what to insert in your master.cfg to reproduce the configuration you have in your own browser. For example:
c['www']['ui_default_config'] = { 'Builders.buildFetchLimit': 500, 'Workers.showWorkerBuilders': True, }
ws_ping_interval
Send websocket pings every
ws_ping_interval
seconds. This is useful to avoid websocket timeouts when using reverse proxies or CDNs. If the value is 0 (the default), pings are disabled.
Note
The buildbotURL
configuration value gives the base URL that all masters will use to generate links.
The www
configuration gives the settings for the webserver.
In simple cases, the buildbotURL
contains the hostname and port of the master, e.g., http://master.example.com:8010/
.
In more complex cases, with multiple masters, web proxies, or load balancers, the correspondence may be less obvious.
2.5.16.1. UI plugins
Waterfall View
Waterfall shows the whole Buildbot activity in a vertical time line. Builds are represented with boxes whose height vary according to their duration. Builds are sorted by builders in the horizontal axes, which allows you to see how builders are scheduled together.
pip install buildbot-waterfall-viewc['www'] = { 'plugins': {'waterfall_view': True} }
Note
Waterfall is the emblematic view of Buildbot Eight. It allowed to see the whole Buildbot activity very quickly. Waterfall however had big scalability issues, and larger installs had to disable the page in order to avoid tens of seconds master hang because of a big waterfall page rendering. The whole Buildbot Eight internal status API has been tailored in order to make Waterfall possible. This is not the case anymore with Buildbot Nine, which has a more generic and scalable Data API and REST API. This is the reason why Waterfall does not display the steps details anymore. However nothing is impossible. We could make a specific REST api available to generate all the data needed for waterfall on the server. Please step-in if you want to help improve the Waterfall view.
Console View
Console view shows the whole Buildbot activity arranged by changes as discovered by Change Sources and Changes vertically and builders horizontally.
If a builder has no build in the current time range, it will not be displayed.
If no change is available for a build, then it will generate a fake change according to the got_revision
property.
Console view will also group the builders by tags. When there are several tags defined per builders, it will first group the builders by the tag that is defined for most builders. Then given those builders, it will group them again in another tag cluster. In order to keep the UI usable, you have to keep your tags short!
pip install buildbot-console-viewc['www'] = { 'plugins': {'console_view': True} }
Note
Nine’s Console View is the equivalent of Buildbot Eight’s Console and tgrid views. Unlike Waterfall, we think it is now feature equivalent and even better, with its live update capabilities. Please submit an issue if you think there is an issue displaying your data, with screen shots of what happen and suggestion on what to improve.
Grid View
Grid view shows the whole Buildbot activity arranged by builders vertically and changes horizontally. It is equivalent to Buildbot Eight’s grid view.
By default, changes on all branches are displayed but only one branch may be filtered by the user. Builders can also be filtered by tags. This feature is similar to the one in the builder list.
pip install buildbot-grid-viewc['www'] = { 'plugins': {'grid_view': True} }
Badges
Buildbot badges plugin produces an image in SVG or PNG format with information about the last build for the given builder name. PNG generation is based on the CAIRO SVG engine, it requires a bit more CPU to generate.
pip install buildbot-badgesc['www'] = { 'plugins': {'badges': {}} }
You can the access your builder’s badges using urls like http://<buildbotURL>/badges/<buildername>.svg
.
The default templates are very much configurable via the following options:
{
"left_pad" : 5,
"left_text": "Build Status", # text on the left part of the image
"left_color": "#555", # color of the left part of the image
"right_pad" : 5,
"border_radius" : 5, # Border Radius on flat and plastic badges
# style of the template availables are "flat", "flat-square", "plastic"
"style": "plastic",
"template_name": "{style}.svg.j2", # name of the template
"font_face": "DejaVu Sans",
"font_size": 11,
"color_scheme": { # color to be used for right part of the image
"exception": "#007ec6", # blue
"failure": "#e05d44", # red
"retry": "#007ec6", # blue
"running": "#007ec6", # blue
"skipped": "a4a61d", # yellowgreen
"success": "#4c1", # brightgreen
"unknown": "#9f9f9f", # lightgrey
"warnings": "#dfb317" # yellow
}
}
Those options can be configured either using the plugin configuration:
c['www'] = {
'plugins': {'badges': {"left_color": "#222"}}
}
or via the URL arguments like http://<buildbotURL>/badges/<buildername>.svg?left_color=222
.
Custom templates can also be specified in a template
directory nearby the master.cfg
.
The badgeio template
A badges template was developed to standardize upon a consistent “look and feel” across the usage of multiple CI/CD solutions, e.g.: use of Buildbot, Codecov.io, and Travis-CI. An example is shown below.
To ensure the correct “look and feel”, the following Buildbot configuration is needed:
c['www'] = {
'plugins': {
'badges': {
"left_pad": 0,
"right_pad": 0,
"border_radius": 3,
"style": "badgeio"
}
}
}
Note
It is highly recommended to use only with SVG.
2.5.16.2. Authentication plugins
By default, Buildbot does not require people to authenticate in order to access control features in the web UI. To secure Buildbot, you will need to configure an authentication plugin.
Note
To secure the Buildbot web interface, authorization rules must be provided via the ‘authz’ configuration. If you simply wish to lock down a Buildbot instance so that only read only access is permitted, you can restrict access to control endpoints to an unpopulated ‘admin’ role. For example:
c['www']['authz'] = util.Authz(allowRules=[util.AnyControlEndpointMatcher(role="admins")],
roleMatchers=[])
Note
As of Buildbot 0.9.4, user session is managed via a JWT token, using HS256 algorithm.
The session secret is stored in the database in the object_state
table with name
column being session_secret
.
Please make sure appropriate access restriction is made to this database table.
Authentication plugins are implemented as classes, and passed as the auth
parameter to www
.
The available classes are described here:
- class buildbot.www.auth.NoAuth
This class is the default authentication plugin, which disables authentication.
- class buildbot.www.auth.UserPasswordAuth(users)
- Parameters:
users – list of
("user","password")
tuples, or a dictionary of{"user": "password", ..}
Simple username/password authentication using a list of user/password tuples provided in the configuration file.
from buildbot.plugins import util c['www'] = { # ... 'auth': util.UserPasswordAuth({"homer": "doh!"}), }
- class buildbot.www.auth.CustomAuth
This authentication class means to be overridden with a custom
check_credentials
method that gets username and password as arguments and check if the user can login. You may use it e.g. to check the credentials against an external database or file.from buildbot.plugins import util class MyAuth(util.CustomAuth): def check_credentials(self, user, password): if user == 'snow' and password == 'white': return True else: return False from buildbot.plugins import util c['www']['auth'] = MyAuth()
- class buildbot.www.auth.HTPasswdAuth(passwdFile)
- Parameters:
passwdFile – An
.htpasswd
file to read
This class implements simple username/password authentication against a standard
.htpasswd
file.from buildbot.plugins import util c['www'] = { # ... 'auth': util.HTPasswdAuth("my_htpasswd"), }
- class buildbot.www.oauth2.GoogleAuth(clientId, clientSecret)
- Parameters:
clientId – The client ID of your buildbot application
clientSecret – The client secret of your buildbot application
This class implements an authentication with Google single sign-on. You can look at the Google oauth2 documentation on how to register your Buildbot instance to the Google systems. The developer console will give you the two parameters you have to give to
GoogleAuth
.Register your Buildbot instance with the
BUILDBOT_URL/auth/login
URL as the allowed redirect URI.Example:
from buildbot.plugins import util c['www'] = { # ... 'auth': util.GoogleAuth("clientid", "clientsecret"), }
In order to use this module, you need to install the Python
requests
module:pip install requests
- class buildbot.www.oauth2.GitHubAuth(clientId, clientSecret)
- param clientId:
The client ID of your buildbot application
- param clientSecret:
The client secret of your buildbot application
- param serverURL:
The server URL if this is a GitHub Enterprise server
- param apiVersion:
The GitHub API version to use. One of
3
or4
(V3/REST or V4/GraphQL). Defaults to 3.- param getTeamsMembership:
When
True
fetch all team memberships for each of the organizations the user belongs to. The teams will be included in the user’s groups asorg-name/team-name
.- param debug:
When
True
and usingapiVersion=4
show some additional log calls with the GraphQL queries and responses for debugging purposes.
This class implements an authentication with GitHub single sign-on. It functions almost identically to the
GoogleAuth
class.Register your Buildbot instance with the
BUILDBOT_URL/auth/login
url as the allowed redirect URI.The user’s email-address (for e.g. authorization) is set to the “primary” address set by the user in GitHub. When using group-based authorization, the user’s groups are equal to the names of the GitHub organizations the user is a member of.
Example:
from buildbot.plugins import util c['www'] = { # ... 'auth': util.GitHubAuth("clientid", "clientsecret"), }
Example for Enterprise GitHub:
from buildbot.plugins import util c['www'] = { # ... 'auth': util.GitHubAuth("clientid", "clientsecret", "https://git.corp.mycompany.com"), }
An example on fetching team membership could be:
from buildbot.plugins import util c['www'] = { # ... 'auth': util.GitHubAuth("clientid", "clientsecret", apiVersion=4, getTeamsMembership=True), 'authz': util.Authz( allowRules=[ util.AnyControlEndpointMatcher(role="core-developers"), ], roleMatchers=[ util.RolesFromGroups(groupPrefix='buildbot/') ] ) }
If the
buildbot
organization had two teams, for example, ‘core-developers’ and ‘contributors’, with the above example, any user belonging to those teams would be granted the roles matching those team names.In order to use this module, you need to install the Python
requests
module:pip install requests
- class buildbot.www.oauth2.GitLabAuth(instanceUri, clientId, clientSecret)
- Parameters:
instanceUri – The URI of your GitLab instance
clientId – The client ID of your buildbot application
clientSecret – The client secret of your buildbot application
This class implements an authentication with GitLab single sign-on. It functions almost identically to the
GoogleAuth
class.Register your Buildbot instance with the
BUILDBOT_URL/auth/login
URL as the allowed redirect URI.Example:
from buildbot.plugins import util c['www'] = { # ... 'auth': util.GitLabAuth("https://gitlab.com", "clientid", "clientsecret"), }
In order to use this module, you need to install the Python
requests
module:pip install requests
- class buildbot.www.oauth2.BitbucketAuth(clientId, clientSecret)
- Parameters:
clientId – The client ID of your buildbot application
clientSecret – The client secret of your buildbot application
This class implements an authentication with Bitbucket single sign-on. It functions almost identically to the
GoogleAuth
class.Register your Buildbot instance with the
BUILDBOT_URL/auth/login
URL as the allowed redirect URI.Example:
from buildbot.plugins import util c['www'] = { # ... 'auth': util.BitbucketAuth("clientid", "clientsecret"), }
In order to use this module, you need to install the Python
requests
module:pip install requests
- class buildbot.www.auth.RemoteUserAuth
- Parameters:
header – header to use to get the username (defaults to
REMOTE_USER
)headerRegex – regular expression to get the username from header value (defaults to
"(?P<username>[^ @]+)@(?P<realm>[^ @]+)")
. Note that you need at least to specify a?P<username>
regular expression named group.userInfoProvider – user info provider; see User Information
If the Buildbot UI is served through a reverse proxy that supports HTTP-based authentication (like apache or lighttpd), it’s possible to tell Buildbot to trust the web server and get the username from the request headers.
The administrator must make sure that it’s impossible to get access to Buildbot in any way other than through the frontend. Usually this means that Buildbot should listen for incoming connections only on localhost (or on some firewall-protected port). The reverse proxy must require HTTP authentication to access Buildbot pages (using any source for credentials, such as htpasswd, PAM, LDAP, Kerberos).
Example:
from buildbot.plugins import util c['www'] = { # ... 'auth': util.RemoteUserAuth(), }
A corresponding Apache configuration example:
<Location "/"> AuthType Kerberos AuthName "Buildbot login via Kerberos" KrbMethodNegotiate On KrbMethodK5Passwd On KrbAuthRealms <<YOUR CORP REALMS>> KrbVerifyKDC off KrbServiceName Any Krb5KeyTab /etc/krb5/krb5.keytab KrbSaveCredentials Off require valid-user Order allow,deny Satisfy Any #] SSO RewriteEngine On RewriteCond %{LA-U:REMOTE_USER} (.+)$ RewriteRule . - [E=RU:%1,NS] RequestHeader set REMOTE_USER %{RU}e </Location>
The advantage of this sort of authentication is that it is uses a proven and fast implementation for authentication. The problem is that the only information that is passed to Buildbot is the username, and there is no way to pass any other information like user email, user groups, etc. That information can be very useful to the mailstatus plugin, or for authorization processes. See User Information for a mechanism to supply that information.
2.5.16.3. User Information
For authentication mechanisms which cannot provide complete information about a user, Buildbot needs another way to get user data. This is useful both for authentication (to fetch more data about the logged-in user) and for avatars (to fetch data about other users).
This extra information is provided, appropriately enough, by user info providers.
These can be passed to RemoteUserAuth
and as an element of avatar_methods
.
This can also be passed to oauth2 authentication plugins. In this case the username provided by oauth2 will be used, and all other information will be taken from ldap (Full Name, email, and groups):
Currently only one provider is available:
- class buildbot.ldapuserinfo.LdapUserInfo(uri, bindUser, bindPw, accountBase, accountPattern, groupBase=None, groupMemberPattern=None, groupName=None, accountFullName, accountEmail, avatarPattern=None, avatarData=None, accountExtraFields=None, tls=None)
- Parameters:
uri – uri of the ldap server
bindUser – username of the ldap account that is used to get the infos for other users (usually a “faceless” account)
bindPw – password of the
bindUser
accountBase – the base dn (distinguished name)of the user database
accountPattern – the pattern for searching in the account database. This must contain the
%(username)s
string, which is replaced by the searched usernameaccountFullName – the name of the field in account ldap database where the full user name is to be found.
accountEmail – the name of the field in account ldap database where the user email is to be found.
groupBase – the base dn of the groups database
groupMemberPattern – the pattern for searching in the group database. This must contain the
%(dn)s
string, which is replaced by the searched username’s dngroupName – the name of the field in groups ldap database where the group name is to be found.
avatarPattern – the pattern for searching avatars from emails in the account database. This must contain the
%(email)s
string, which is replaced by the searched emailavatarData – the name of the field in groups ldap database where the avatar picture is to be found. This field is supposed to contain the raw picture, format is automatically detected from jpeg, png or git.
accountExtraFields – extra fields to extracts for use with the authorization policies
tls – an instance of
ldap.Tls
that specifies TLS settings.
If one of the three optional groups parameters is supplied, then all of them become mandatory. If none is supplied, the retrieved user info has an empty list of groups.
Example:
from buildbot.plugins import util
# this configuration works for MS Active Directory ldap implementation
# we use it for user info, and avatars
userInfoProvider = util.LdapUserInfo(
uri='ldap://ldap.mycompany.com:3268',
bindUser='ldap_user',
bindPw='p4$$wd',
accountBase='dc=corp,dc=mycompany,dc=com',
groupBase='dc=corp,dc=mycompany,dc=com',
accountPattern='(&(objectClass=person)(sAMAccountName=%(username)s))',
accountFullName='displayName',
accountEmail='mail',
groupMemberPattern='(&(objectClass=group)(member=%(dn)s))',
groupName='cn',
avatarPattern='(&(objectClass=person)(mail=%(email)s))',
avatarData='thumbnailPhoto',
)
c['www'] = dict(port=PORT, allowed_origins=["*"],
url=c['buildbotURL'],
auth=util.RemoteUserAuth(userInfoProvider=userInfoProvider),
avatar_methods=[userInfoProvider,
util.AvatarGravatar()])
Note
In order to use this module, you need to install the ldap3
module:
pip install ldap3
In the case of oauth2 authentications, you have to pass the userInfoProvider as keyword argument:
from buildbot.plugins import util
userInfoProvider = util.LdapUserInfo(...)
c['www'] = {
# ...
'auth': util.GoogleAuth("clientid", "clientsecret", userInfoProvider=userInfoProvider),
}
2.5.16.4. Reverse Proxy Configuration
It is usually better to put Buildbot behind a reverse proxy in production.
Provides automatic gzip compression
Provides SSL support with a widely used implementation
Provides support for http/2 or spdy for fast parallel REST api access from the browser
Reverse proxy however might be problematic for websocket, you have to configure it specifically to pass web socket requests. Here is an nginx configuration that is known to work (nginx 1.6.2):
server {
# Enable SSL and http2
listen 443 ssl http2 default_server;
server_name yourdomain.com;
root html;
index index.html index.htm;
ssl on;
ssl_certificate /etc/nginx/ssl/server.cer;
ssl_certificate_key /etc/nginx/ssl/server.key;
# put a one day session timeout for websockets to stay longer
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1440m;
# please consult latest nginx documentation for current secure encryption settings
ssl_protocols ..
ssl_ciphers ..
ssl_prefer_server_ciphers on;
#
# force https
add_header Strict-Transport-Security "max-age=31536000; includeSubdomains;";
spdy_headers_comp 5;
proxy_set_header HOST $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-Host $host;
# you could use / if you use domain based proxy instead of path based proxy
location /buildbot/ {
proxy_pass http://127.0.0.1:5000/;
}
location /buildbot/sse/ {
# proxy buffering will prevent sse to work
proxy_buffering off;
proxy_pass http://127.0.0.1:5000/sse/;
}
# required for websocket
location /buildbot/ws {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_pass http://127.0.0.1:5000/ws;
# raise the proxy timeout for the websocket
proxy_read_timeout 6000s;
}
}
To run with Apache2, you’ll need mod_proxy_wstunnel in addition to mod_proxy_http. Serving HTTPS (mod_ssl) is advised to prevent issues with enterprise proxies (see Server Sent Events), even if you don’t need the encryption itself.
Here is a configuration that is known to work (Apache 2.4.10 / Debian 8, Apache 2.4.25 / Debian 9, Apache 2.4.6 / CentOS 7), directly at the top of the domain.
If you want to add access control directives, just put them in a
<Location />
.
<VirtualHost *:443>
ServerName buildbot.example
ServerAdmin webmaster@buildbot.example
# replace with actual port of your Buildbot master
ProxyPass /ws ws://127.0.0.1:8020/ws
ProxyPassReverse /ws ws://127.0.0.1:8020/ws
ProxyPass / http://127.0.0.1:8020/
ProxyPassReverse / http://127.0.0.1:8020/
SetEnvIf X-Url-Scheme https HTTPS=1
ProxyPreserveHost On
SSLEngine on
SSLCertificateFile /path/to/cert.pem
SSLCertificateKeyFile /path/to/cert.key
# check Apache2 documentation for current safe SSL settings
# This is actually the Debian 8 default at the time of this writing:
SSLProtocol all -SSLv3
</VirtualHost>