initial commit
commit
961498e32b
|
@ -0,0 +1 @@
|
||||||
|
.vscode
|
|
@ -0,0 +1,45 @@
|
||||||
|
openssh_port: "12322"
|
||||||
|
|
||||||
|
smtp_accounts:
|
||||||
|
- name: ahoy
|
||||||
|
host: mail.infomaniak.ch
|
||||||
|
port: 587
|
||||||
|
from: ahoy@pirateparty.be
|
||||||
|
password: !vault |
|
||||||
|
$ANSIBLE_VAULT;1.1;AES256
|
||||||
|
62633164383764376333643063363263356461613164663066623836613931306437633033633134
|
||||||
|
3632326164663564653962613437376265333234313032360a313935303230393938356632356231
|
||||||
|
34613661383736313232613131313262616261323464653936393634653464323631333839353030
|
||||||
|
3230396536663635650a633537633633623365346563323334616338333436633537623831313165
|
||||||
|
38343766346437626332313230346537663735313937643765353465356236633134
|
||||||
|
smtp_default_account: ahoy
|
||||||
|
smtp_default_contact: it@pirateparty.be
|
||||||
|
|
||||||
|
node_exporter_password: !vault |
|
||||||
|
$ANSIBLE_VAULT;1.1;AES256
|
||||||
|
35333237666635633862336433303264613133376230346461333332636231643563636466356630
|
||||||
|
3235623237303366626562393065353436663632306438370a333132653432636632643134326130
|
||||||
|
66396666626631373637373065613137393232383361346438633763396266636264663364663238
|
||||||
|
3363666332633562360a323532666664333266333761343136306133336138623137316234653939
|
||||||
|
37643239613631383165656138633134663736393238343939336135303732333838336538373531
|
||||||
|
3635396265643061356339333035393836313936316633623662
|
||||||
|
|
||||||
|
backup_targets:
|
||||||
|
- host: storage.pirateparty.be
|
||||||
|
ssh:
|
||||||
|
port: 23
|
||||||
|
username: "{{ storage_box_username }}"
|
||||||
|
key_file: storage-box
|
||||||
|
- host: batato.be
|
||||||
|
ssh:
|
||||||
|
key_file: batato
|
||||||
|
|
||||||
|
users:
|
||||||
|
- name: hgo
|
||||||
|
ssh_keys:
|
||||||
|
- ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOOK8Y3OEq1j3rR8EOLpVPYZeA5qC0PTsctza9c2qhbU hadrien@terry
|
||||||
|
- name: tierce
|
||||||
|
ssh_keys:
|
||||||
|
- ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC75IeAMEg6RwvbO6oLEQOpYSASGx9A3JD15gtA7D3NJFz+pZ7hBjBSjTxZrHDQLg1OFs0XRGS5DATRMnj6jSRAc25C71DewbNY9fWOH1/dAuo45zBllO3/pol17uYVqUbaPVjnqQFfikLCf7HjBbjt7JEVffJ3nkalE2q0TqjGK0JrltrL9dePE/R3ZNzVSDXvkgMsu18Wov9if6ftsKYgNTW+oOc9xoN1GSHYEnzv68+YNt3zKGTiwhU87cLyHJBu9o/wFDNOLdQcOtKa3IUPZvOgDlLrAm8a4Z9/A9DCJS/8kFmyNOazF1rupPAojn7k9mIBvVPxc5zqg+qrKbxR tierce@q
|
||||||
|
|
||||||
|
acme_email: it@pirateparty.be
|
|
@ -0,0 +1,41 @@
|
||||||
|
alertmanager_smtp:
|
||||||
|
from: ahoy@pirateparty.be
|
||||||
|
smarthost: mail.infomaniak.ch:587
|
||||||
|
auth_username: ahoy@pirateparty.be
|
||||||
|
auth_password: !vault |
|
||||||
|
$ANSIBLE_VAULT;1.1;AES256
|
||||||
|
61643536623562333434653364623535633331653539356132653863313965313030333163313637
|
||||||
|
6161333463653839383265323937376630336134633531650a313132326536346530353764656465
|
||||||
|
63323737643034353532333363303363616261363335333365663133626537653961323133626433
|
||||||
|
6566656236383864610a323262393562663836343162326131336630363939356333313934326436
|
||||||
|
6261
|
||||||
|
|
||||||
|
alertmanager_route:
|
||||||
|
receiver: 'default-receiver'
|
||||||
|
group_wait: 30s
|
||||||
|
group_interval: 5m
|
||||||
|
repeat_interval: 3h
|
||||||
|
|
||||||
|
alertmanager_receivers:
|
||||||
|
- name: default-receiver
|
||||||
|
email_configs:
|
||||||
|
- to: hadrien@pirateparty.be
|
||||||
|
|
||||||
|
alertmanager_password: !vault |
|
||||||
|
$ANSIBLE_VAULT;1.1;AES256
|
||||||
|
63313035633034643636326230383162666666626539623934303631366236656432616238356362
|
||||||
|
6665626364643666343737623532616133303539356133300a396530643865323334313564363762
|
||||||
|
31646562306232356437636537383732626664663166656331303630303537383064663565323235
|
||||||
|
3962313936613039320a656337356131363636643366393233613462313361323639373363643134
|
||||||
|
32383436313035323032656266376664383166633631663438316165313930373937636436633962
|
||||||
|
6131336262343531643264346362343433373165386266323439
|
||||||
|
prometheus_password: "{{ alertmanager_password }}"
|
||||||
|
grafana_admin_user: ppbe
|
||||||
|
grafana_admin_password: !vault |
|
||||||
|
$ANSIBLE_VAULT;1.1;AES256
|
||||||
|
39626461326636633230343536613564643537376464336537353661636638303238303966383030
|
||||||
|
3133663938623334396435333761306265373064353462610a356531326130396566386638653533
|
||||||
|
36323833663030663466356538353237376137313135656534383439613935623234373065376530
|
||||||
|
3864366438626135300a333664313339343964306538343366306639393631666366323537313734
|
||||||
|
36613731626439646537653565646436323839383930363131653431306431396638613665616464
|
||||||
|
3435313137313964636139366439336365663564326639303234
|
|
@ -0,0 +1,46 @@
|
||||||
|
umurmur_version: 0.2.17
|
||||||
|
umurmur_domain: mumble.parley.be
|
||||||
|
|
||||||
|
umurmur_welcome_text:
|
||||||
|
- Welcome to Parley Talk!
|
||||||
|
- You can talk to the people in the room you joined.
|
||||||
|
- You start in the Welcome room, to join another channel right click the room name and choose "Join Channel"
|
||||||
|
umurmur_admin_password: wC7yZ4vV2ocb7AkBfQ2RwuhRqYVyiwY42Rjpw3pfJ
|
||||||
|
umurmur_max_users: 100
|
||||||
|
|
||||||
|
umurmur_channels:
|
||||||
|
- name: Parley
|
||||||
|
description: Main Parley Talk channel. No entry.
|
||||||
|
noenter: yes
|
||||||
|
- name: Welcome
|
||||||
|
parent: Parley
|
||||||
|
description: Welcome channel
|
||||||
|
position: 0
|
||||||
|
- name: Silent
|
||||||
|
parent: Parley
|
||||||
|
description: Silent channel
|
||||||
|
silent: yes
|
||||||
|
position: 1
|
||||||
|
- name: Mary Read's territory
|
||||||
|
description: The channel dedicated to Mary Read. She was a Caribbean pirate. Dressed as a man, Mary went to sea and later joined the British army, fighting in the War Of The Spanish Succession. She married and settled down as a woman, but dressed back as a man following the death of her husband, later boarding a ship bound for the West Indies.
|
||||||
|
parent: Welcome
|
||||||
|
- name: Anne Bonny crew
|
||||||
|
description: The channel dedicated to Anne Bonny. She was one the most famous female pirates. She operated in the Caribbean. She discovered that one of her crew companion, Mark Read, was secretly a woman (Mary Read) and the two became very close.
|
||||||
|
parent: Welcome
|
||||||
|
- name: Mary Cricket's ship
|
||||||
|
description: The channel dedicated to Mary Cricket. Toghether with 5 other prisoners Mary Crickett escaped and overpowered the two-man crew of the sloop John and Elizabeth on 12 May 1729. She held the prisoners in the ship's hold, sitting on the hatch to prevent their escape. The pair was released a few days later. The pirates sailed into Chesapeake Bay but before they could raid any other ships, they were captured by HMS Shoreham. Returned to Virginia, they were tried in August 1729, convicted of piracy, and sentenced to hang.
|
||||||
|
parent: Welcome
|
||||||
|
- name: Flora Burn's island
|
||||||
|
description: The channel dedicated to Flora Burn. She began her pirate career in 1741 and operated mainly on the East Coast of North America.
|
||||||
|
parent: Welcome
|
||||||
|
- name: Sayyida al Hurra
|
||||||
|
description: She was a Moroccan pirate from the 16th century, and controlled the Mediterranean Sea together with Barbarossa. Her name means "noble lady who is free and independent"
|
||||||
|
parent: Welcome
|
||||||
|
- name: Ching Shih
|
||||||
|
description: She was a Chinese pirate from the 19th century. She is considered to be the pirate with the largest crew ever assembled (between 20.000 and 40.000 pirates) and died peacefully as a free woman
|
||||||
|
parent: Welcome
|
||||||
|
|
||||||
|
umurmur_default_channel: Welcome
|
||||||
|
|
||||||
|
mumble_web_domain: talk.parley.be
|
||||||
|
mumble_web_version: c03b78d096eae69e1cec82e148f65e7a8541bd68
|
|
@ -0,0 +1 @@
|
||||||
|
ansible_user: admin
|
|
@ -0,0 +1,8 @@
|
||||||
|
storage_box_username: u212275-sub5
|
||||||
|
storage_box_password: !vault |
|
||||||
|
$ANSIBLE_VAULT;1.1;AES256
|
||||||
|
36313662333062323531613966386365373339663566303133653562663838316632613830613264
|
||||||
|
6564333736343830623061313534313630313534316231390a666662633861383563333562356561
|
||||||
|
64616534313266323833383331313334333761323965383634666635663430366461353437616465
|
||||||
|
6337363536643738310a656530663837386537336434633037376463336165613239323265366234
|
||||||
|
64663863333763356430616635323061396663373264343666323831646664646430
|
|
@ -0,0 +1,11 @@
|
||||||
|
[web]
|
||||||
|
mastodon.pirateparty.be
|
||||||
|
pirateparty.be
|
||||||
|
wiki.pirateparty.be
|
||||||
|
talk.parley.be
|
||||||
|
|
||||||
|
[monitoring]
|
||||||
|
status.pirateparty.be
|
||||||
|
|
||||||
|
[mumble]
|
||||||
|
talk.parley.be
|
|
@ -0,0 +1,12 @@
|
||||||
|
---
|
||||||
|
- hosts: all
|
||||||
|
become: yes
|
||||||
|
|
||||||
|
pre_tasks:
|
||||||
|
- name: Update apt cache
|
||||||
|
apt:
|
||||||
|
update_cache: yes
|
||||||
|
cache_valid_time: 3600
|
||||||
|
|
||||||
|
roles:
|
||||||
|
- common
|
|
@ -0,0 +1,4 @@
|
||||||
|
- hosts: monitoring
|
||||||
|
roles:
|
||||||
|
- common
|
||||||
|
- monitoring
|
|
@ -0,0 +1,10 @@
|
||||||
|
- hosts: mumble
|
||||||
|
become: yes
|
||||||
|
|
||||||
|
vars:
|
||||||
|
acme_domains:
|
||||||
|
- "{{ umurmur_domain }}"
|
||||||
|
- "{{ mumble_web_domain }}"
|
||||||
|
roles:
|
||||||
|
- acme
|
||||||
|
- mumble
|
|
@ -0,0 +1,9 @@
|
||||||
|
acme_directory: https://acme-v02.api.letsencrypt.org/directory
|
||||||
|
acme_config_dir: /etc/ssl
|
||||||
|
acme_keys_dir: "{{ acme_config_dir }}/private"
|
||||||
|
acme_csr_dir: "{{ acme_config_dir }}/csr"
|
||||||
|
acme_certs_dir: "{{ acme_config_dir }}/certs"
|
||||||
|
acme_accounts_dir: "{{ acme_config_dir }}/accounts"
|
||||||
|
acme_account_key: "acme_account.key"
|
||||||
|
acme_ssl_group: ssl-cert
|
||||||
|
acme_challenge_dir: /var/www/acme
|
|
@ -0,0 +1,184 @@
|
||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
import josepy as jose
|
||||||
|
from OpenSSL import crypto
|
||||||
|
from acme import client, messages, challenges, crypto_util
|
||||||
|
|
||||||
|
SSL_CONFIG_DIR="/etc/ssl"
|
||||||
|
OPENSSL_DATE_FORMAT = '%Y%m%d%H%M%SZ'
|
||||||
|
USER_AGENT="python-acme"
|
||||||
|
|
||||||
|
def select_http01_challenge(order):
|
||||||
|
for auth in order.authorizations:
|
||||||
|
for challenge in auth.body.challenges:
|
||||||
|
if isinstance(challenge.chall, challenges.HTTP01):
|
||||||
|
return challenge
|
||||||
|
raise Exception("HTTP-01 challenge was not offered by the CA server.")
|
||||||
|
|
||||||
|
def perform_http01_challenge(client_acme, challenge, order, challenge_dir):
|
||||||
|
response, validation = challenge.response_and_validation(client_acme.net.key)
|
||||||
|
validation_filename, validation_content = validation.split('.')
|
||||||
|
|
||||||
|
challenge_path = os.path.join(challenge_dir, validation_filename)
|
||||||
|
with open(challenge_path, 'w') as f_out:
|
||||||
|
f_out.write(validation_content)
|
||||||
|
|
||||||
|
client_acme.answer_challenge(challenge, response)
|
||||||
|
|
||||||
|
fullchain_pem = client_acme.poll_and_finalize(order).fullchain_pem
|
||||||
|
|
||||||
|
os.remove(challenge_path)
|
||||||
|
|
||||||
|
return fullchain_pem
|
||||||
|
|
||||||
|
def renew_cert(domain,
|
||||||
|
account_key_path,
|
||||||
|
privkey_path,
|
||||||
|
csr_path,
|
||||||
|
certs_dir,
|
||||||
|
challenge_dir,
|
||||||
|
directory_url,
|
||||||
|
days_before_renewal,
|
||||||
|
force = False):
|
||||||
|
logging.info(f"Checking {domain} certificate's expiration date…")
|
||||||
|
now = datetime.now()
|
||||||
|
cert_expiration_date = now
|
||||||
|
|
||||||
|
fullchain_path = os.path.join(certs_dir, "fullchain.pem")
|
||||||
|
try:
|
||||||
|
with open(fullchain_path, 'rb') as pem_in:
|
||||||
|
fullchain_pem = crypto.load_certificate(crypto.FILETYPE_PEM, pem_in.read())
|
||||||
|
cert_expiration_date = datetime.strptime(fullchain_pem.get_notAfter().decode(), OPENSSL_DATE_FORMAT)
|
||||||
|
except IOError as e:
|
||||||
|
logging.warning(f"Unable to load {domain} certificate: {e}")
|
||||||
|
|
||||||
|
cert_expiration_days = (cert_expiration_date - now).days
|
||||||
|
if not force and cert_expiration_days >= days_before_renewal:
|
||||||
|
logging.info(f"Certificate expires in {cert_expiration_days} days. Nothing to do.")
|
||||||
|
return
|
||||||
|
|
||||||
|
logging.info(f"Certificate expires in {cert_expiration_days} days! Renewing certificate…")
|
||||||
|
|
||||||
|
logging.info(f"Loading account key from {account_key_path}…")
|
||||||
|
with open(account_key_path, 'rb') as pem_in:
|
||||||
|
account_pem = crypto.load_privatekey(crypto.FILETYPE_PEM, pem_in.read())
|
||||||
|
account_jwk = jose.JWKRSA(key=account_pem.to_cryptography_key())
|
||||||
|
account_jwk_pub = account_jwk.public_key()
|
||||||
|
|
||||||
|
logging.info(f"Loading {domain} CSR file from {csr_path}…")
|
||||||
|
try:
|
||||||
|
with open(csr_path, 'r') as pem_in:
|
||||||
|
csr_pem = pem_in.read()
|
||||||
|
except IOError as e:
|
||||||
|
logging.warning(f"Unable to load CSR file: {e}")
|
||||||
|
logging.info(f"Loading {domain} private key from {privkey_path}…")
|
||||||
|
with open(privkey_path, 'r') as pem_in:
|
||||||
|
privkey_pem = pem_in.read()
|
||||||
|
|
||||||
|
csr_pem = crypto_util.make_csr(privkey_pem, [domain])
|
||||||
|
|
||||||
|
client_network = client.ClientNetwork(account_jwk, user_agent=USER_AGENT)
|
||||||
|
directory = messages.Directory.from_json(client_network.get(directory_url).json())
|
||||||
|
client_acme = client.ClientV2(directory, net=client_network)
|
||||||
|
|
||||||
|
logging.info("Registering with ACME account…")
|
||||||
|
# Here we assume we already have an account
|
||||||
|
registration_message = messages.NewRegistration(
|
||||||
|
key=account_jwk_pub,
|
||||||
|
only_return_existing=True)
|
||||||
|
registration_response = client_acme._post(directory["newAccount"], registration_message)
|
||||||
|
account = client_acme._regr_from_response(registration_response)
|
||||||
|
client_acme.net.account = account
|
||||||
|
|
||||||
|
logging.info("Ordering ACME challenge…")
|
||||||
|
order = client_acme.new_order(csr_pem)
|
||||||
|
|
||||||
|
logging.info("Selecting HTTP-01 ACME challenge…")
|
||||||
|
challenge = select_http01_challenge(order)
|
||||||
|
|
||||||
|
logging.info("Performing HTTP-01 ACME challenge…")
|
||||||
|
fullchain_pem = perform_http01_challenge(client_acme, challenge, order, challenge_dir)
|
||||||
|
|
||||||
|
logging.info(f"Writing {domain} certificates into {certs_dir}…")
|
||||||
|
certs_pem = []
|
||||||
|
for line in fullchain_pem.split('\n'):
|
||||||
|
if 'BEGIN CERTIFICATE' in line:
|
||||||
|
cert_pem = ''
|
||||||
|
|
||||||
|
cert_pem += line + '\n'
|
||||||
|
|
||||||
|
if 'END CERTIFICATE' in line:
|
||||||
|
certs_pem.append(cert_pem)
|
||||||
|
|
||||||
|
cert_path = os.path.join(certs_dir, "cert.pem")
|
||||||
|
with open(cert_path, 'w') as pem_out:
|
||||||
|
pem_out.write(certs_pem[0])
|
||||||
|
|
||||||
|
chain_path = os.path.join(certs_dir, "chain.pem")
|
||||||
|
with open(chain_path, 'w') as pem_out:
|
||||||
|
pem_out.write(''.join(certs_pem[1:]))
|
||||||
|
|
||||||
|
with open(fullchain_path, 'w') as pem_out:
|
||||||
|
pem_out.write(fullchain_pem)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="Renew ACME certificates for a list of domains. This script assumes you already have an account on the CA server and a private key for each certificate.",
|
||||||
|
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
||||||
|
parser.add_argument("domains", nargs='+',
|
||||||
|
help="List of domain names for which to renew the certificate.")
|
||||||
|
parser.add_argument("--account_key", "-a",
|
||||||
|
default=os.path.join(SSL_CONFIG_DIR, "accounts", "acme_account.key"),
|
||||||
|
help="Path to the account key.")
|
||||||
|
parser.add_argument("--privkey", "-p",
|
||||||
|
default=os.path.join(SSL_CONFIG_DIR, "private", "{domain}.pem"),
|
||||||
|
help="Path to the private certificate.")
|
||||||
|
parser.add_argument("--csr", "-r",
|
||||||
|
default=os.path.join(SSL_CONFIG_DIR, "csr", "{domain}.pem"),
|
||||||
|
help="Path to the CSR file. If the file doesn't exist, it will be generated from the private key and the domain name.")
|
||||||
|
parser.add_argument("--certs", "-o",
|
||||||
|
default=os.path.join(SSL_CONFIG_DIR, "certs", "{domain}.d"),
|
||||||
|
help="Path to the certificates directory.")
|
||||||
|
parser.add_argument("--challenge", "-c",
|
||||||
|
default="/var/www/html/.well-known/acme-challenge",
|
||||||
|
help="Path to the challenge directory.")
|
||||||
|
parser.add_argument("--directory_url", "-u",
|
||||||
|
default="https://acme-v02.api.letsencrypt.org/directory",
|
||||||
|
help="Directory URL on which performing ACME challenges. Only ACME v2 is supported.")
|
||||||
|
parser.add_argument("--days", "-d", type=int,
|
||||||
|
default=30,
|
||||||
|
help="Days before attempting to renew the certificates.")
|
||||||
|
parser.add_argument("--quiet", "-q",
|
||||||
|
action="store_true",
|
||||||
|
help="Quiet mode.")
|
||||||
|
parser.add_argument("--force", "-f",
|
||||||
|
action="store_true",
|
||||||
|
help="Force certificates renewal without checking their expiration date.")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
log_level = logging.WARN if args.quiet else logging.INFO
|
||||||
|
logging.basicConfig(stream=sys.stdout, level=log_level, format="%(levelname)s:%(message)s")
|
||||||
|
|
||||||
|
for domain in args.domains:
|
||||||
|
account_key = args.account_key.format(domain=domain)
|
||||||
|
privkey = args.privkey.format(domain=domain)
|
||||||
|
csr = args.csr.format(domain=domain)
|
||||||
|
certs_dir = args.certs.format(domain=domain)
|
||||||
|
challenge_dir = args.challenge.format(domain=domain)
|
||||||
|
|
||||||
|
renew_cert(domain,
|
||||||
|
account_key_path=account_key,
|
||||||
|
privkey_path=privkey,
|
||||||
|
csr_path=csr,
|
||||||
|
certs_dir=certs_dir,
|
||||||
|
challenge_dir=challenge_dir,
|
||||||
|
directory_url=args.directory_url,
|
||||||
|
days_before_renewal=args.days,
|
||||||
|
force=args.force)
|
|
@ -0,0 +1,76 @@
|
||||||
|
- name: Install ACME dependencies
|
||||||
|
apt:
|
||||||
|
name: python3-acme
|
||||||
|
state: present
|
||||||
|
tags: acme_install
|
||||||
|
|
||||||
|
- name: Create Let's Encrypt config directories
|
||||||
|
file:
|
||||||
|
path: "{{ config_dir }}"
|
||||||
|
state: directory
|
||||||
|
owner: root
|
||||||
|
group: "{{ acme_ssl_group }}"
|
||||||
|
mode: "711"
|
||||||
|
loop:
|
||||||
|
- "{{ acme_config_dir }}"
|
||||||
|
- "{{ acme_keys_dir }}"
|
||||||
|
- "{{ acme_accounts_dir }}"
|
||||||
|
- "{{ acme_csr_dir }}"
|
||||||
|
loop_control:
|
||||||
|
loop_var: config_dir
|
||||||
|
tags: acme_install
|
||||||
|
|
||||||
|
- name: Create challenge directory
|
||||||
|
file:
|
||||||
|
path: "{{ acme_challenge_dir }}/.well-known/acme-challenge"
|
||||||
|
state: directory
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "755"
|
||||||
|
tags: acme_install
|
||||||
|
|
||||||
|
- name: Perform ACME challenge for each domain
|
||||||
|
include_tasks:
|
||||||
|
file: challenge.yml
|
||||||
|
apply:
|
||||||
|
tags: acme_challenge
|
||||||
|
loop: "{{ acme_domains | unique }}"
|
||||||
|
loop_control:
|
||||||
|
loop_var: domain_name
|
||||||
|
tags: acme_challenge
|
||||||
|
|
||||||
|
- name: Create directory for certificate renewal tool
|
||||||
|
file:
|
||||||
|
path: /opt/acme
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "755"
|
||||||
|
state: directory
|
||||||
|
tags: acme_renew
|
||||||
|
|
||||||
|
- name: Copy script to renew ACME certificates
|
||||||
|
copy:
|
||||||
|
src: acme_renew_cert.py
|
||||||
|
dest: /opt/acme/acme_renew_cert.py
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "755"
|
||||||
|
tags: acme_renew
|
||||||
|
|
||||||
|
- name: Setup cron job for ACME certificates renewal of {{ domain_name }}
|
||||||
|
cron:
|
||||||
|
name: acme renew {{ domain_name }} cert
|
||||||
|
job: >-
|
||||||
|
sleep $((RANDOM % 3600)) && /opt/acme/acme_renew_cert.py {{ domain_name }} -q
|
||||||
|
-a {{ (acme_accounts_dir + '/' + acme_account_key) | quote }}
|
||||||
|
-p {{ acme_keys_dir | quote }}/{domain}.pem
|
||||||
|
-r {{ acme_csr_dir | quote }}/{domain}.csr
|
||||||
|
-o {{ acme_certs_dir | quote }}/{domain}.d
|
||||||
|
-c {{ acme_challenge_dir | quote }}/.well-known/acme-challenge
|
||||||
|
minute: "30"
|
||||||
|
hour: "2"
|
||||||
|
state: present
|
||||||
|
loop: "{{ acme_domains | unique }}"
|
||||||
|
loop_control:
|
||||||
|
loop_var: domain_name
|
||||||
|
tags: acme_renew
|
|
@ -0,0 +1,91 @@
|
||||||
|
- name: Create {{ domain_name }} certificates directory
|
||||||
|
file:
|
||||||
|
path: "{{ acme_certs_dir }}/{{ domain_name }}.d"
|
||||||
|
state: directory
|
||||||
|
owner: root
|
||||||
|
group: "{{ acme_ssl_group }}"
|
||||||
|
mode: "755"
|
||||||
|
tags: acme_install
|
||||||
|
|
||||||
|
- name: Generate Let's Encrypt account key
|
||||||
|
openssl_privatekey:
|
||||||
|
path: "{{ acme_accounts_dir }}/{{ acme_account_key }}"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "600"
|
||||||
|
type: RSA
|
||||||
|
size: 4096
|
||||||
|
tags: acme_account
|
||||||
|
|
||||||
|
- name: Generate Let's Encrypt private key for {{ domain_name }}
|
||||||
|
openssl_privatekey:
|
||||||
|
path: "{{ acme_keys_dir }}/{{ domain_name }}.pem"
|
||||||
|
owner: root
|
||||||
|
group: "{{ acme_ssl_group }}"
|
||||||
|
mode: "640"
|
||||||
|
type: RSA
|
||||||
|
size: 4096
|
||||||
|
|
||||||
|
- name: Generate Let's Encrypt CSR for {{ domain_name }}
|
||||||
|
openssl_csr:
|
||||||
|
path: "{{ acme_csr_dir }}/{{ domain_name }}.csr"
|
||||||
|
owner: root
|
||||||
|
group: "{{ acme_ssl_group }}"
|
||||||
|
mode: "644"
|
||||||
|
privatekey_path: "{{ acme_keys_dir }}/{{ domain_name }}.pem"
|
||||||
|
common_name: "{{ domain_name }}"
|
||||||
|
|
||||||
|
# - name: Check if Let's Encrypt certificate already exists for {{ domain_name }}
|
||||||
|
# stat:
|
||||||
|
# path: "{{ acme_certs_dir }}/{{ domain_name }}.d/cert.pem"
|
||||||
|
# register: _acme_cert_file
|
||||||
|
|
||||||
|
# - name: Check Let's Encrypt certificate expiration date for {{ domain_name }}
|
||||||
|
# openssl_certificate_info:
|
||||||
|
# path: "{{ acme_certs_dir }}/{{ domain_name }}.d/cert.pem"
|
||||||
|
# valid_at:
|
||||||
|
# thirty_days: "+30d"
|
||||||
|
# register: _acme_cert_validity
|
||||||
|
# when: _acme_cert_file.stat.isreg is defined and _acme_cert_file.stat.isreg
|
||||||
|
|
||||||
|
- name: Begin Let's Encrypt challenges for {{ domain_name }}
|
||||||
|
acme_certificate:
|
||||||
|
acme_directory: "{{ acme_directory }}"
|
||||||
|
acme_version: "{{ acme_version }}"
|
||||||
|
account_key_src: "{{ acme_accounts_dir }}/{{ acme_account_key }}"
|
||||||
|
account_email: "{{ acme_email }}"
|
||||||
|
terms_agreed: yes
|
||||||
|
challenge: http-01
|
||||||
|
csr: "{{ acme_csr_dir }}/{{ domain_name }}.csr"
|
||||||
|
dest: "{{ acme_certs_dir }}/{{ domain_name }}.d/cert.pem"
|
||||||
|
fullchain_dest: "{{ acme_certs_dir }}/{{ domain_name }}.d/fullchain.pem"
|
||||||
|
remaining_days: 30
|
||||||
|
register: _acme_challenge
|
||||||
|
# when: _acme_cert_validity is skipped or not _acme_cert_validity.valid_at.thirty_days
|
||||||
|
|
||||||
|
- debug:
|
||||||
|
var: _acme_challenge
|
||||||
|
|
||||||
|
# - name: Implement and complete Let's Encrypt challenge for {{ domain_name }}
|
||||||
|
# when: _acme_challenge is not skipped
|
||||||
|
# block:
|
||||||
|
# - name: Implement http-01 challenge files for {{ domain_name }}
|
||||||
|
# copy:
|
||||||
|
# content: "{{ _acme_challenge.challenge_data[domain_name]['http-01'].resource_value }}"
|
||||||
|
# dest: "{{ acme_challenge_dir }}/{{ _acme_challenge.challenge_data[domain_name]['http-01'].resource }}"
|
||||||
|
# owner: root
|
||||||
|
# group: root
|
||||||
|
# mode: "644"
|
||||||
|
|
||||||
|
# - name: Complete Let's Encrypt challenges for {{ domain_name }}
|
||||||
|
# acme_certificate:
|
||||||
|
# acme_directory: "{{ acme_directory }}"
|
||||||
|
# acme_version: "{{ acme_version }}"
|
||||||
|
# account_key_src: "{{ acme_accounts_dir }}/{{ acme_account_key }}"
|
||||||
|
# account_email: "{{ acme_email }}"
|
||||||
|
# challenge: http-01
|
||||||
|
# csr: "{{ acme_csr_dir }}/{{ domain_name }}.csr"
|
||||||
|
# dest: "{{ acme_certs_dir }}/{{ domain_name }}.d/cert.pem"
|
||||||
|
# chain_dest: "{{ acme_certs_dir }}/{{ domain_name }}.d/chain.pem"
|
||||||
|
# fullchain_dest: "{{ acme_certs_dir }}/{{ domain_name }}.d/fullchain.pem"
|
||||||
|
# data: "{{ _acme_challenge }}"
|
|
@ -0,0 +1,2 @@
|
||||||
|
- import_tasks: acme.yml
|
||||||
|
tags: acme
|
|
@ -0,0 +1 @@
|
||||||
|
acme_version: 2
|
|
@ -0,0 +1,38 @@
|
||||||
|
Role Name
|
||||||
|
=========
|
||||||
|
|
||||||
|
A brief description of the role goes here.
|
||||||
|
|
||||||
|
Requirements
|
||||||
|
------------
|
||||||
|
|
||||||
|
Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required.
|
||||||
|
|
||||||
|
Role Variables
|
||||||
|
--------------
|
||||||
|
|
||||||
|
A description of the settable variables for this role should go here, including any variables that are in defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well.
|
||||||
|
|
||||||
|
Dependencies
|
||||||
|
------------
|
||||||
|
|
||||||
|
A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles.
|
||||||
|
|
||||||
|
Example Playbook
|
||||||
|
----------------
|
||||||
|
|
||||||
|
Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too:
|
||||||
|
|
||||||
|
- hosts: servers
|
||||||
|
roles:
|
||||||
|
- { role: username.rolename, x: 42 }
|
||||||
|
|
||||||
|
License
|
||||||
|
-------
|
||||||
|
|
||||||
|
BSD
|
||||||
|
|
||||||
|
Author Information
|
||||||
|
------------------
|
||||||
|
|
||||||
|
An optional section for the role authors to include contact information, or a website (HTML is not allowed).
|
|
@ -0,0 +1,11 @@
|
||||||
|
---
|
||||||
|
# defaults file for common
|
||||||
|
node_exporter_path: /
|
||||||
|
node_exporter_port: 9100
|
||||||
|
node_exporter_public_port: "9180"
|
||||||
|
|
||||||
|
nginx_config_dir: /etc/nginx/conf.d
|
||||||
|
nginx_ssl_dir: /etc/nginx/ssl
|
||||||
|
|
||||||
|
ssh_config_dir: ~/.ssh
|
||||||
|
backup_targets: []
|
|
@ -0,0 +1,8 @@
|
||||||
|
- name: restart openssh
|
||||||
|
service:
|
||||||
|
name: ssh
|
||||||
|
state: restarted
|
||||||
|
|
||||||
|
- name: reload nginx
|
||||||
|
include_tasks: ../handlers/nginx.yml
|
||||||
|
when: nginx_started is not changed
|
|
@ -0,0 +1,7 @@
|
||||||
|
- name: Validate Nginx config
|
||||||
|
command: nginx -t
|
||||||
|
|
||||||
|
- name: Reload Nginx server
|
||||||
|
service:
|
||||||
|
name: nginx
|
||||||
|
state: reloaded
|
|
@ -0,0 +1,16 @@
|
||||||
|
- import_tasks: repos.yml
|
||||||
|
tags: repos
|
||||||
|
- import_tasks: users.yml
|
||||||
|
tags: users
|
||||||
|
- import_tasks: openssh.yml
|
||||||
|
tags: openssh
|
||||||
|
- import_tasks: ufw.yml
|
||||||
|
tags: firewall
|
||||||
|
- import_tasks: msmtp.yml
|
||||||
|
tags: smtp
|
||||||
|
- import_tasks: nginx.yml
|
||||||
|
tags: nginx
|
||||||
|
- import_tasks: node_exporter.yml
|
||||||
|
tags: node_exporter
|
||||||
|
#- import_tasks: backup.yml
|
||||||
|
# tags: backup
|
|
@ -0,0 +1,18 @@
|
||||||
|
---
|
||||||
|
# Install and configure SMTP relay
|
||||||
|
- name: Install msmtp
|
||||||
|
apt:
|
||||||
|
name:
|
||||||
|
- msmtp
|
||||||
|
- msmtp-mta
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Copy msmtp configuration
|
||||||
|
template:
|
||||||
|
src: msmtp/msmtprc.j2
|
||||||
|
dest: /etc/msmtprc
|
||||||
|
|
||||||
|
- name: Copy aliases
|
||||||
|
template:
|
||||||
|
src: msmtp/aliases.j2
|
||||||
|
dest: /etc/aliases
|
|
@ -0,0 +1,101 @@
|
||||||
|
---
|
||||||
|
# Install and configure Nginx
|
||||||
|
- name: Install htpasswd dependencies
|
||||||
|
apt:
|
||||||
|
name: python-passlib
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Install SSL dependencies
|
||||||
|
apt:
|
||||||
|
name: ssl-cert
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Install Nginx
|
||||||
|
apt:
|
||||||
|
name: nginx-full
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Create Nginx configuration directories
|
||||||
|
file:
|
||||||
|
path: "{{ config_dir }}"
|
||||||
|
state: directory
|
||||||
|
owner: root
|
||||||
|
group: www-data
|
||||||
|
mode: "755"
|
||||||
|
loop:
|
||||||
|
- "{{ nginx_config_dir }}"
|
||||||
|
- "{{ nginx_ssl_dir }}"
|
||||||
|
loop_control:
|
||||||
|
loop_var: config_dir
|
||||||
|
|
||||||
|
- name: Generate Diffie-Hellman parameters
|
||||||
|
# This can take a long time... So we are doing it in async mode
|
||||||
|
openssl_dhparam:
|
||||||
|
path: "{{ nginx_ssl_dir }}/dhparam.pem"
|
||||||
|
size: 3072
|
||||||
|
owner: root
|
||||||
|
group: www-data
|
||||||
|
async: 3600
|
||||||
|
poll: 0
|
||||||
|
register: nginx_dh
|
||||||
|
|
||||||
|
- name: Use snakoil cert key as Nginx's default private key
|
||||||
|
file:
|
||||||
|
src: "/etc/ssl/private/ssl-cert-snakeoil.key"
|
||||||
|
path: "{{ nginx_ssl_dir }}/nginx.key"
|
||||||
|
state: link
|
||||||
|
owner: root
|
||||||
|
group: www-data
|
||||||
|
mode: "750"
|
||||||
|
force: yes
|
||||||
|
|
||||||
|
- name: Use snakoil cert as Nginx's default certificate
|
||||||
|
file:
|
||||||
|
src: "/etc/ssl/certs/ssl-cert-snakeoil.pem"
|
||||||
|
path: "{{ nginx_ssl_dir }}/nginx.crt"
|
||||||
|
state: link
|
||||||
|
owner: root
|
||||||
|
group: www-data
|
||||||
|
mode: "755"
|
||||||
|
force: yes
|
||||||
|
|
||||||
|
- name: Copy default Nginx config
|
||||||
|
template:
|
||||||
|
src: nginx/default.conf.j2
|
||||||
|
dest: /etc/nginx/sites-available/default
|
||||||
|
owner: root
|
||||||
|
group: www-data
|
||||||
|
mode: "755"
|
||||||
|
notify: reload nginx
|
||||||
|
|
||||||
|
- name: Enable default Nginx config
|
||||||
|
file:
|
||||||
|
src: /etc/nginx/sites-available/default
|
||||||
|
dest: /etc/nginx/sites-enabled/default
|
||||||
|
owner: root
|
||||||
|
group: www-data
|
||||||
|
mode: "755"
|
||||||
|
notify: reload nginx
|
||||||
|
|
||||||
|
- name: Allow default Nginx ports
|
||||||
|
ufw:
|
||||||
|
rule: allow
|
||||||
|
name: "Nginx Full"
|
||||||
|
|
||||||
|
- name: Waiting for Diffie-Hellman task to complete…
|
||||||
|
async_status:
|
||||||
|
jid: "{{ nginx_dh.ansible_job_id }}"
|
||||||
|
register: nginx_dh_job
|
||||||
|
retries: 60
|
||||||
|
delay: 30 # will retry every 30s for 30min (60 retries)
|
||||||
|
until: nginx_dh_job.finished
|
||||||
|
|
||||||
|
- name: Start Nginx server
|
||||||
|
service:
|
||||||
|
name: nginx
|
||||||
|
state: started
|
||||||
|
enabled: yes
|
||||||
|
register: nginx_started
|
||||||
|
|
||||||
|
- name: "Trigger Nginx handlers"
|
||||||
|
meta: flush_handlers
|
|
@ -0,0 +1,30 @@
|
||||||
|
---
|
||||||
|
# Install and configure node-exporter
|
||||||
|
- name: Include role for installing node-exporter
|
||||||
|
include_role:
|
||||||
|
name: cloudalchemy.node-exporter
|
||||||
|
public: yes
|
||||||
|
vars:
|
||||||
|
node_exporter_web_listen_address: "0.0.0.0:{{ node_exporter_port }}"
|
||||||
|
|
||||||
|
- name: Configure Nginx for node-exporter
|
||||||
|
import_role:
|
||||||
|
name: nginx
|
||||||
|
vars:
|
||||||
|
nginx_config_file: node-exporter.conf
|
||||||
|
nginx_server:
|
||||||
|
name: "{{ inventory_hostname }}"
|
||||||
|
port: "{{ node_exporter_public_port }}"
|
||||||
|
locations:
|
||||||
|
- path: "{{ node_exporter_path }}"
|
||||||
|
basic_auth:
|
||||||
|
file: .htpasswd.node-exporter
|
||||||
|
password: "{{ node_exporter_password }}"
|
||||||
|
proxy_pass:
|
||||||
|
port: "{{ node_exporter_port }}"
|
||||||
|
path: /metrics
|
||||||
|
|
||||||
|
- name: Allow node-exporter port {{ node_exporter_public_port }}
|
||||||
|
ufw:
|
||||||
|
rule: allow
|
||||||
|
port: "{{ node_exporter_public_port }}"
|
|
@ -0,0 +1,19 @@
|
||||||
|
---
|
||||||
|
# Configure OpenSSH server
|
||||||
|
- name: Configure OpenSSH server
|
||||||
|
template:
|
||||||
|
src: openssh/sshd_config.j2
|
||||||
|
dest: /etc/ssh/sshd_config
|
||||||
|
backup: yes
|
||||||
|
owner: "0"
|
||||||
|
group: "0"
|
||||||
|
mode: "0644"
|
||||||
|
validate: '/usr/sbin/sshd -T -f %s'
|
||||||
|
notify: restart openssh
|
||||||
|
|
||||||
|
- name: Trigger Ansible handlers
|
||||||
|
meta: flush_handlers
|
||||||
|
|
||||||
|
- name: Change Ansible SSH port to {{ openssh_port }}
|
||||||
|
set_fact:
|
||||||
|
ansible_port: "{{ openssh_port }}"
|
|
@ -0,0 +1,12 @@
|
||||||
|
---
|
||||||
|
# Configure APT repositories and automatic upgrades
|
||||||
|
- name: Safely upgrade the server
|
||||||
|
apt:
|
||||||
|
upgrade: safe
|
||||||
|
update_cache: yes
|
||||||
|
cache_valid_time: "3600"
|
||||||
|
|
||||||
|
- name: Install unattend-upgrades for automatic upgrades
|
||||||
|
apt:
|
||||||
|
name: unattended-upgrades
|
||||||
|
state: present
|
|
@ -0,0 +1,15 @@
|
||||||
|
---
|
||||||
|
# Install and configure UFW, the uncomplicated firewall
|
||||||
|
- name: Install UFW, the uncomplicated firewall
|
||||||
|
apt:
|
||||||
|
name: ufw
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Allow OpenSSH port {{ openssh_port }}
|
||||||
|
ufw:
|
||||||
|
rule: allow
|
||||||
|
port: "{{ openssh_port }}"
|
||||||
|
|
||||||
|
- name: Enable UFW config
|
||||||
|
ufw:
|
||||||
|
state: enabled
|
|
@ -0,0 +1,22 @@
|
||||||
|
---
|
||||||
|
# Create an user and add their SSH public keys
|
||||||
|
- name: Create user {{ user.name }} with no password
|
||||||
|
user:
|
||||||
|
name: "{{ user.name }}"
|
||||||
|
shell: /bin/bash
|
||||||
|
# See https://unix.stackexchange.com/questions/193066/how-to-unlock-account-for-public-key-ssh-authorization-but-not-for-password-aut/193131#193131
|
||||||
|
password: '*'
|
||||||
|
groups:
|
||||||
|
- sudo
|
||||||
|
append: yes
|
||||||
|
state: present
|
||||||
|
update_password: on_create
|
||||||
|
|
||||||
|
- name: Add SSH public keys for user {{ user.name }}
|
||||||
|
authorized_key:
|
||||||
|
user: "{{ user.name }}"
|
||||||
|
state: present
|
||||||
|
# we can pass multiple SSH keys, but they must be separated by newlines
|
||||||
|
key: "{{ user.ssh_keys | join('\n') }}"
|
||||||
|
# remove obsolete keys
|
||||||
|
exclusive: yes
|
|
@ -0,0 +1,28 @@
|
||||||
|
---
|
||||||
|
# Create users and add their SSH public keys
|
||||||
|
- name: Install sudo package
|
||||||
|
package:
|
||||||
|
name: sudo
|
||||||
|
state: present
|
||||||
|
tags: sudo
|
||||||
|
|
||||||
|
- name: Remove password to become root with sudo
|
||||||
|
lineinfile:
|
||||||
|
path: /etc/sudoers
|
||||||
|
state: present
|
||||||
|
regexp: '^%sudo'
|
||||||
|
line: '%sudo ALL=(ALL) NOPASSWD: ALL'
|
||||||
|
validate: 'visudo -cf %s'
|
||||||
|
tags: sudo
|
||||||
|
|
||||||
|
- name: Remove password for root user
|
||||||
|
user:
|
||||||
|
name: root
|
||||||
|
shell: /bin/bash
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Create users and add their SSH public keys
|
||||||
|
include_tasks: user.yml
|
||||||
|
loop: "{{ users }}"
|
||||||
|
loop_control:
|
||||||
|
loop_var: user
|
|
@ -0,0 +1 @@
|
||||||
|
default: {{ smtp_default_contact }}
|
|
@ -0,0 +1,19 @@
|
||||||
|
defaults
|
||||||
|
auth on
|
||||||
|
tls on
|
||||||
|
tls_trust_file /etc/ssl/certs/ca-certificates.crt
|
||||||
|
logfile /var/log/msmtp.log
|
||||||
|
|
||||||
|
{% for account in smtp_accounts %}
|
||||||
|
account {{ account.name }}
|
||||||
|
host {{ account.host }}
|
||||||
|
port 587
|
||||||
|
from {{ account.from }}
|
||||||
|
user {{ account.user | default(account.from) }}
|
||||||
|
password {{ account.password }}
|
||||||
|
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
account default : {{ smtp_default_account }}
|
||||||
|
|
||||||
|
aliases /etc/aliases
|
|
@ -0,0 +1,62 @@
|
||||||
|
{{ ansible_managed | comment }}
|
||||||
|
# Default server configuration
|
||||||
|
#
|
||||||
|
server {
|
||||||
|
listen 80 default_server;
|
||||||
|
listen [::]:80 default_server ipv6only=on;
|
||||||
|
|
||||||
|
root /var/www/default;
|
||||||
|
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
location ^~ /.well-known/acme-challenge/ {
|
||||||
|
allow all;
|
||||||
|
root /var/www/acme;
|
||||||
|
try_files $uri =404;
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ =404;
|
||||||
|
}
|
||||||
|
|
||||||
|
location = /favicon.ico {
|
||||||
|
log_not_found off;
|
||||||
|
access_log off;
|
||||||
|
}
|
||||||
|
|
||||||
|
location = /robots.txt {
|
||||||
|
allow all;
|
||||||
|
log_not_found off;
|
||||||
|
access_log off;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~* ^.+.(jpg|jpeg|gif|png|ico|css|zip|tgz|gz|rar|bz2|doc|xls|exe|pdf|ppt|txt|tar|mid|midi|wav|bmp|rtf|js)$ {
|
||||||
|
expires 7d;
|
||||||
|
log_not_found off;
|
||||||
|
access_log off;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~ /\. {
|
||||||
|
deny all;
|
||||||
|
access_log off;
|
||||||
|
log_not_found off;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~ /nginx_status {
|
||||||
|
stub_status on;
|
||||||
|
access_log off;
|
||||||
|
allow 127.0.0.1;
|
||||||
|
deny all;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 443 default_server;
|
||||||
|
listen [::]:443 default_server;
|
||||||
|
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
include snippets/snakeoil.conf;
|
||||||
|
|
||||||
|
return 444;
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
{{ ansible_managed | comment }}
|
||||||
|
|
||||||
|
{% if nginx_server_name is defined %}
|
||||||
|
server {
|
||||||
|
listen {{ nginx_port }};
|
||||||
|
|
||||||
|
server_name {{ nginx_server_name }};
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% for location in nginx_locations %}
|
||||||
|
location {{ location }} {
|
||||||
|
{% if location.proxy_pass is defined %}
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header Host $http_host;
|
||||||
|
proxy_redirect off;
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if location.basic_auth_file is defined %}
|
||||||
|
auth_basic "Authentication required";
|
||||||
|
auth_basic_user_file /etc/nginx/{{ location.basic_auth_file }};
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if location.proxy_pass is defined %}
|
||||||
|
proxy_pass http://localhost:{{ location.proxy_pass.port | default('80') }}{{ location.proxy_pass.path }};
|
||||||
|
{% endif %}
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
|
{% if nginx_server_name is defined %}
|
||||||
|
}
|
||||||
|
{% endif %}
|
|
@ -0,0 +1,17 @@
|
||||||
|
# {{ ansible_managed }}
|
||||||
|
|
||||||
|
Port {{ openssh_port }}
|
||||||
|
|
||||||
|
PermitRootLogin no
|
||||||
|
|
||||||
|
ChallengeResponseAuthentication no
|
||||||
|
|
||||||
|
UsePAM yes
|
||||||
|
|
||||||
|
X11Forwarding yes
|
||||||
|
|
||||||
|
PrintMotd no
|
||||||
|
|
||||||
|
AcceptEnv LANG LC_*
|
||||||
|
|
||||||
|
Subsystem sftp /usr/lib/openssh/sftp-server
|
|
@ -0,0 +1,2 @@
|
||||||
|
---
|
||||||
|
# vars file for common
|
|
@ -0,0 +1,38 @@
|
||||||
|
Role Name
|
||||||
|
=========
|
||||||
|
|
||||||
|
A brief description of the role goes here.
|
||||||
|
|
||||||
|
Requirements
|
||||||
|
------------
|
||||||
|
|
||||||
|
Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required.
|
||||||
|
|
||||||
|
Role Variables
|
||||||
|
--------------
|
||||||
|
|
||||||
|
A description of the settable variables for this role should go here, including any variables that are in defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well.
|
||||||
|
|
||||||
|
Dependencies
|
||||||
|
------------
|
||||||
|
|
||||||
|
A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles.
|
||||||
|
|
||||||
|
Example Playbook
|
||||||
|
----------------
|
||||||
|
|
||||||
|
Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too:
|
||||||
|
|
||||||
|
- hosts: servers
|
||||||
|
roles:
|
||||||
|
- { role: username.rolename, x: 42 }
|
||||||
|
|
||||||
|
License
|
||||||
|
-------
|
||||||
|
|
||||||
|
BSD
|
||||||
|
|
||||||
|
Author Information
|
||||||
|
------------------
|
||||||
|
|
||||||
|
An optional section for the role authors to include contact information, or a website (HTML is not allowed).
|
|
@ -0,0 +1,2 @@
|
||||||
|
---
|
||||||
|
# defaults file for roles/docker
|
|
@ -0,0 +1,2 @@
|
||||||
|
---
|
||||||
|
# handlers file for roles/docker
|
|
@ -0,0 +1,53 @@
|
||||||
|
galaxy_info:
|
||||||
|
author: your name
|
||||||
|
description: your description
|
||||||
|
company: your company (optional)
|
||||||
|
|
||||||
|
# If the issue tracker for your role is not on github, uncomment the
|
||||||
|
# next line and provide a value
|
||||||
|
# issue_tracker_url: http://example.com/issue/tracker
|
||||||
|
|
||||||
|
# Choose a valid license ID from https://spdx.org - some suggested licenses:
|
||||||
|
# - BSD-3-Clause (default)
|
||||||
|
# - MIT
|
||||||
|
# - GPL-2.0-or-later
|
||||||
|
# - GPL-3.0-only
|
||||||
|
# - Apache-2.0
|
||||||
|
# - CC-BY-4.0
|
||||||
|
license: license (GPL-2.0-or-later, MIT, etc)
|
||||||
|
|
||||||
|
min_ansible_version: 2.4
|
||||||
|
|
||||||
|
# If this a Container Enabled role, provide the minimum Ansible Container version.
|
||||||
|
# min_ansible_container_version:
|
||||||
|
|
||||||
|
#
|
||||||
|
# Provide a list of supported platforms, and for each platform a list of versions.
|
||||||
|
# If you don't wish to enumerate all versions for a particular platform, use 'all'.
|
||||||
|
# To view available platforms and versions (or releases), visit:
|
||||||
|
# https://galaxy.ansible.com/api/v1/platforms/
|
||||||
|
#
|
||||||
|
# platforms:
|
||||||
|
# - name: Fedora
|
||||||
|
# versions:
|
||||||
|
# - all
|
||||||
|
# - 25
|
||||||
|
# - name: SomePlatform
|
||||||
|
# versions:
|
||||||
|
# - all
|
||||||
|
# - 1.0
|
||||||
|
# - 7
|
||||||
|
# - 99.99
|
||||||
|
|
||||||
|
galaxy_tags: []
|
||||||
|
# List tags for your role here, one per line. A tag is a keyword that describes
|
||||||
|
# and categorizes the role. Users find roles by searching for tags. Be sure to
|
||||||
|
# remove the '[]' above, if you add tags to this list.
|
||||||
|
#
|
||||||
|
# NOTE: A tag is limited to a single word comprised of alphanumeric characters.
|
||||||
|
# Maximum 20 tags per role.
|
||||||
|
|
||||||
|
dependencies: []
|
||||||
|
# List your role dependencies here, one per line. Be sure to remove the '[]' above,
|
||||||
|
# if you add dependencies to this list.
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
---
|
||||||
|
# tasks file for roles/docker
|
||||||
|
- name: install required system packages
|
||||||
|
apt:
|
||||||
|
names:
|
||||||
|
- apt-transport-https
|
||||||
|
- 'ca-certificates'
|
||||||
|
- 'curl'
|
||||||
|
- 'software-properties-common'
|
||||||
|
- 'python3-pip'
|
||||||
|
- 'virtualenv'
|
||||||
|
- 'python3-setuptools'
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: add Docker GPG apt Key
|
||||||
|
apt_key:
|
||||||
|
url: https://download.docker.com/linux/ubuntu/gpg
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: add Docker Repository
|
||||||
|
apt_repository:
|
||||||
|
repo: deb https://download.docker.com/linux/ubuntu bionic stable
|
||||||
|
state: present
|
||||||
|
register: docker_repo
|
||||||
|
|
||||||
|
- name: update apt cache
|
||||||
|
apt:
|
||||||
|
update_cache: yes
|
||||||
|
cache_valid_time: 3600
|
||||||
|
when: docker_repo is changed
|
||||||
|
|
||||||
|
- name: install docker-ce package
|
||||||
|
apt:
|
||||||
|
name: docker-ce
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: enable docker service
|
||||||
|
systemd:
|
||||||
|
name: docker
|
||||||
|
state: started
|
||||||
|
enabled: yes
|
||||||
|
|
||||||
|
- name: install Docker Module for Python
|
||||||
|
pip:
|
||||||
|
name: docker
|
|
@ -0,0 +1,2 @@
|
||||||
|
localhost
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
- hosts: localhost
|
||||||
|
remote_user: root
|
||||||
|
roles:
|
||||||
|
- roles/docker
|
|
@ -0,0 +1,2 @@
|
||||||
|
---
|
||||||
|
# vars file for roles/docker
|
|
@ -0,0 +1,17 @@
|
||||||
|
---
|
||||||
|
# defaults file for roles/prometheus
|
||||||
|
grafana_admin_user: admin
|
||||||
|
grafana_domain: "{{ inventory_hostname }}"
|
||||||
|
grafana_web_path: /grafana
|
||||||
|
grafana_protocol: http
|
||||||
|
grafana_port: 3000
|
||||||
|
|
||||||
|
prometheus_domain: "{{ inventory_hostname }}"
|
||||||
|
prometheus_web_path: /prometheus
|
||||||
|
prometheus_port: 9090
|
||||||
|
|
||||||
|
nginx_default_path: "{{ grafana_web_path }}"
|
||||||
|
|
||||||
|
alertmanager_domain: "{{ inventory_hostname }}"
|
||||||
|
alertmanager_web_path: /alertmanager
|
||||||
|
alertmanager_port: 9093
|
|
@ -0,0 +1,43 @@
|
||||||
|
groups:
|
||||||
|
- name: Hardware alerts
|
||||||
|
rules:
|
||||||
|
- alert: Instancedown
|
||||||
|
expr: up == 0
|
||||||
|
for: 5m
|
||||||
|
labels:
|
||||||
|
severity: warning
|
||||||
|
annotations:
|
||||||
|
title: Instance {{ $labels.instance }} is down
|
||||||
|
description: Failed to scrape {{ $labels.job }} on {{ $labels.instance }} for more than 5 minute. Instance seems down.
|
||||||
|
- alert: LowRootDiskSpace
|
||||||
|
expr: (node_filesystem_free_bytes{device =~ "/dev/.+"} / node_filesystem_size_bytes{device =~ "/dev/.+"} * 100) < 10
|
||||||
|
for: 5m
|
||||||
|
labels:
|
||||||
|
severity: warning
|
||||||
|
annotations:
|
||||||
|
title: Low free root space on {{ $labels.instance }}
|
||||||
|
description: On {{ $labels.instance }} device {{ $labels.device }} mounted on {{ $labels.mountpoint }} has low free space of {{ $value }}%
|
||||||
|
- alert: LowDataDiskSpace
|
||||||
|
expr: (node_filesystem_free_bytes{device !~ "/dev/.+", fstype !~ "tmpfs|.*lxcfs"} / node_filesystem_size_bytes{device !~ "/dev/.+", fstype !~ "tmpfs|.*lxcfs"} * 100) < 10
|
||||||
|
for: 5m
|
||||||
|
labels:
|
||||||
|
severity: warning
|
||||||
|
annotations:
|
||||||
|
title: Low free data space on {{ $labels.instance }}
|
||||||
|
description: On {{ $labels.instance }} device {{ $labels.device }} mounted on {{ $labels.mountpoint }} has low free space of {{ $value }}%
|
||||||
|
- alert: HighCPULoad
|
||||||
|
expr: 100 - (avg by(instance) (irate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 90
|
||||||
|
for: 5m
|
||||||
|
labels:
|
||||||
|
severity: warning
|
||||||
|
annotations:
|
||||||
|
title: High CPU load on instance {{ $labels.instance }}
|
||||||
|
description: Instance {{ $labels.instance }} has high CPU load.
|
||||||
|
- alert: HighMemoryUsage
|
||||||
|
expr: (1 - ((node_memory_MemFree_bytes + node_memory_Buffers_bytes + node_memory_Cached_bytes) / node_memory_MemTotal_bytes)) * 100 > 90
|
||||||
|
for: 5m
|
||||||
|
labels:
|
||||||
|
severity: warning
|
||||||
|
annotations:
|
||||||
|
description: Instance {{ $labels.instance }} has high memory usage
|
||||||
|
summary: High memory usage on {{ $labels.instance }}
|
|
@ -0,0 +1,13 @@
|
||||||
|
---
|
||||||
|
# handlers file for roles/prometheus
|
||||||
|
- name: restart grafana
|
||||||
|
service:
|
||||||
|
name: grafana-server
|
||||||
|
state: restarted
|
||||||
|
when: not grafana_service.changed
|
||||||
|
|
||||||
|
- name: restart prometheus
|
||||||
|
service:
|
||||||
|
name: prometheus
|
||||||
|
state: restarted
|
||||||
|
when: not prometheus_service.changed
|
|
@ -0,0 +1,24 @@
|
||||||
|
- name: Install Alertmanager
|
||||||
|
import_role:
|
||||||
|
name: cloudalchemy.alertmanager
|
||||||
|
public: yes
|
||||||
|
vars:
|
||||||
|
alertmanager_web_external_url: "http://{{ alertmanager_domain }}{{ alertmanager_web_path }}"
|
||||||
|
alertmanager_config_flags_extra:
|
||||||
|
web.route-prefix: /
|
||||||
|
alertmanager_web_listen_address: "0.0.0.0:{{ alertmanager_port }}"
|
||||||
|
|
||||||
|
- name: Configure Nginx for Alertmaneger
|
||||||
|
import_role:
|
||||||
|
name: nginx
|
||||||
|
vars:
|
||||||
|
nginx_config_file: "{{ inventory_hostname }}.d/alertmanager.conf"
|
||||||
|
nginx_server:
|
||||||
|
locations:
|
||||||
|
- path: "{{ alertmanager_web_path }}/"
|
||||||
|
basic_auth:
|
||||||
|
file: .htpasswd.alertmanager
|
||||||
|
password: "{{ alertmanager_password }}"
|
||||||
|
proxy_pass:
|
||||||
|
port: "{{ alertmanager_port }}"
|
||||||
|
path: /
|
|
@ -0,0 +1,71 @@
|
||||||
|
- name: add grafana GPG apt key
|
||||||
|
apt_key:
|
||||||
|
url: https://packages.grafana.com/gpg.key
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- block:
|
||||||
|
- name: add grafana repository
|
||||||
|
apt_repository:
|
||||||
|
repo: deb https://packages.grafana.com/oss/deb stable main
|
||||||
|
state: present
|
||||||
|
register: grafana_repo
|
||||||
|
notify: restart grafana
|
||||||
|
|
||||||
|
- name: update apt cache
|
||||||
|
apt:
|
||||||
|
update_cache: yes
|
||||||
|
cache_valid_time: 3600
|
||||||
|
when: grafana_repo is changed
|
||||||
|
|
||||||
|
- name: install grafana package
|
||||||
|
apt:
|
||||||
|
pkg: grafana
|
||||||
|
state: present
|
||||||
|
notify: restart grafana
|
||||||
|
|
||||||
|
- name: copy grafana config
|
||||||
|
template:
|
||||||
|
src: grafana.ini.j2
|
||||||
|
dest: /etc/grafana/grafana.ini
|
||||||
|
become: yes
|
||||||
|
notify: restart grafana
|
||||||
|
|
||||||
|
- name: enable grafana service
|
||||||
|
systemd:
|
||||||
|
name: grafana-server
|
||||||
|
state: started
|
||||||
|
enabled: yes
|
||||||
|
register: grafana_service
|
||||||
|
|
||||||
|
- name: Configure Nginx for Grafana
|
||||||
|
import_role:
|
||||||
|
name: nginx
|
||||||
|
vars:
|
||||||
|
nginx_config_file: "{{ inventory_hostname }}.conf"
|
||||||
|
nginx_server:
|
||||||
|
name: "{{ inventory_hostname }}"
|
||||||
|
port: 80
|
||||||
|
locations:
|
||||||
|
- path: "= /"
|
||||||
|
return:
|
||||||
|
code: 301
|
||||||
|
url: "http://{{ inventory_hostname }}{{ grafana_web_path }}"
|
||||||
|
includes:
|
||||||
|
- "{{ nginx_config_dir }}/{{ inventory_hostname }}.d/*.conf"
|
||||||
|
|
||||||
|
- name: ensure nginx config directory exists
|
||||||
|
file:
|
||||||
|
path: "{{ nginx_config_dir }}/{{ inventory_hostname }}.d"
|
||||||
|
state: directory
|
||||||
|
|
||||||
|
- include_role:
|
||||||
|
name: nginx
|
||||||
|
tasks_from: configure
|
||||||
|
vars:
|
||||||
|
nginx_config_file: "{{ inventory_hostname }}.d/grafana.conf"
|
||||||
|
nginx_server:
|
||||||
|
locations:
|
||||||
|
- path: "{{ grafana_web_path }}/"
|
||||||
|
proxy_pass:
|
||||||
|
port: "{{ grafana_port }}"
|
||||||
|
path: /
|
|
@ -0,0 +1,8 @@
|
||||||
|
---
|
||||||
|
- import_tasks: grafana.yml
|
||||||
|
become: yes
|
||||||
|
tags: grafana
|
||||||
|
- import_tasks: alertmanager.yml
|
||||||
|
tags: alertmanager
|
||||||
|
- import_tasks: prometheus.yml
|
||||||
|
tags: prometheus
|
|
@ -0,0 +1,26 @@
|
||||||
|
- name: Install Prometheus
|
||||||
|
import_role:
|
||||||
|
name: cloudalchemy.prometheus
|
||||||
|
public: yes
|
||||||
|
vars:
|
||||||
|
prometheus_config_file: "prometheus_custom.yml.j2"
|
||||||
|
prometheus_alert_rules: []
|
||||||
|
prometheus_web_external_url: "http://{{ prometheus_domain }}{{ prometheus_web_path }}"
|
||||||
|
prometheus_config_flags_extra:
|
||||||
|
web.route-prefix: /
|
||||||
|
prometheus_web_listen_address: "0.0.0.0:{{ prometheus_port }}"
|
||||||
|
|
||||||
|
- name: Configure Nginx for Prometheus
|
||||||
|
import_role:
|
||||||
|
name: nginx
|
||||||
|
vars:
|
||||||
|
nginx_config_file: "{{ inventory_hostname }}.d/prometheus.conf"
|
||||||
|
nginx_server:
|
||||||
|
locations:
|
||||||
|
- path: "{{ prometheus_web_path }}/"
|
||||||
|
basic_auth:
|
||||||
|
file: .htpasswd.prometheus
|
||||||
|
password: "{{ prometheus_password }}"
|
||||||
|
proxy_pass:
|
||||||
|
port: "{{ prometheus_port }}"
|
||||||
|
path: /
|
|
@ -0,0 +1,627 @@
|
||||||
|
[server]
|
||||||
|
|
||||||
|
[security]
|
||||||
|
|
||||||
|
##################### Grafana Configuration Example #####################
|
||||||
|
#
|
||||||
|
# Everything has defaults so you only need to uncomment things you want to
|
||||||
|
# change
|
||||||
|
|
||||||
|
# possible values : production, development
|
||||||
|
;app_mode = production
|
||||||
|
|
||||||
|
# instance name, defaults to HOSTNAME environment variable value or hostname if HOSTNAME var is empty
|
||||||
|
;instance_name = ${HOSTNAME}
|
||||||
|
|
||||||
|
#################################### Paths ####################################
|
||||||
|
[paths]
|
||||||
|
# Path to where grafana can store temp files, sessions, and the sqlite3 db (if that is used)
|
||||||
|
;data = /var/lib/grafana
|
||||||
|
|
||||||
|
# Temporary files in `data` directory older than given duration will be removed
|
||||||
|
;temp_data_lifetime = 24h
|
||||||
|
|
||||||
|
# Directory where grafana can store logs
|
||||||
|
;logs = /var/log/grafana
|
||||||
|
|
||||||
|
# Directory where grafana will automatically scan and look for plugins
|
||||||
|
;plugins = /var/lib/grafana/plugins
|
||||||
|
|
||||||
|
# folder that contains provisioning config files that grafana will apply on startup and while running.
|
||||||
|
;provisioning = conf/provisioning
|
||||||
|
|
||||||
|
#################################### Server ####################################
|
||||||
|
[server]
|
||||||
|
# Protocol (http, https, h2, socket)
|
||||||
|
protocol = {{ grafana_protocol }}
|
||||||
|
|
||||||
|
# The ip address to bind to, empty will bind to all interfaces
|
||||||
|
;http_addr =
|
||||||
|
|
||||||
|
# The http port to use
|
||||||
|
http_port = {{ grafana_port }}
|
||||||
|
|
||||||
|
# The public facing domain name used to access grafana from a browser
|
||||||
|
domain = {{ grafana_domain }}
|
||||||
|
|
||||||
|
# Redirect to correct domain if host header does not match domain
|
||||||
|
# Prevents DNS rebinding attacks
|
||||||
|
;enforce_domain = false
|
||||||
|
|
||||||
|
# The full public facing url you use in browser, used for redirects and emails
|
||||||
|
# If you use reverse proxy and sub path specify full url (with sub path)
|
||||||
|
root_url = {{ grafana_protocol }}://{{ grafana_domain }}{{ grafana_web_path }}
|
||||||
|
|
||||||
|
# Serve Grafana from subpath specified in `root_url` setting. By default it is set to `false` for compatibility reasons.
|
||||||
|
;serve_from_sub_path = false
|
||||||
|
|
||||||
|
# Log web requests
|
||||||
|
;router_logging = false
|
||||||
|
|
||||||
|
# the path relative working path
|
||||||
|
;static_root_path = public
|
||||||
|
|
||||||
|
# enable gzip
|
||||||
|
;enable_gzip = false
|
||||||
|
|
||||||
|
# https certs & key file
|
||||||
|
;cert_file =
|
||||||
|
;cert_key =
|
||||||
|
|
||||||
|
# Unix socket path
|
||||||
|
;socket =
|
||||||
|
|
||||||
|
#################################### Database ####################################
|
||||||
|
[database]
|
||||||
|
# You can configure the database connection by specifying type, host, name, user and password
|
||||||
|
# as separate properties or as on string using the url properties.
|
||||||
|
|
||||||
|
# Either "mysql", "postgres" or "sqlite3", it's your choice
|
||||||
|
;type = sqlite3
|
||||||
|
;host = 127.0.0.1:3306
|
||||||
|
;name = grafana
|
||||||
|
;user = root
|
||||||
|
# If the password contains # or ; you have to wrap it with triple quotes. Ex """#password;"""
|
||||||
|
;password =
|
||||||
|
|
||||||
|
# Use either URL or the previous fields to configure the database
|
||||||
|
# Example: mysql://user:secret@host:port/database
|
||||||
|
;url =
|
||||||
|
|
||||||
|
# For "postgres" only, either "disable", "require" or "verify-full"
|
||||||
|
;ssl_mode = disable
|
||||||
|
|
||||||
|
# For "sqlite3" only, path relative to data_path setting
|
||||||
|
;path = grafana.db
|
||||||
|
|
||||||
|
# Max idle conn setting default is 2
|
||||||
|
;max_idle_conn = 2
|
||||||
|
|
||||||
|
# Max conn setting default is 0 (mean not set)
|
||||||
|
;max_open_conn =
|
||||||
|
|
||||||
|
# Connection Max Lifetime default is 14400 (means 14400 seconds or 4 hours)
|
||||||
|
;conn_max_lifetime = 14400
|
||||||
|
|
||||||
|
# Set to true to log the sql calls and execution times.
|
||||||
|
;log_queries =
|
||||||
|
|
||||||
|
# For "sqlite3" only. cache mode setting used for connecting to the database. (private, shared)
|
||||||
|
;cache_mode = private
|
||||||
|
|
||||||
|
#################################### Cache server #############################
|
||||||
|
[remote_cache]
|
||||||
|
# Either "redis", "memcached" or "database" default is "database"
|
||||||
|
;type = database
|
||||||
|
|
||||||
|
# cache connectionstring options
|
||||||
|
# database: will use Grafana primary database.
|
||||||
|
# redis: config like redis server e.g. `addr=127.0.0.1:6379,pool_size=100,db=0,ssl=false`. Only addr is required. ssl may be 'true', 'false', or 'insecure'.
|
||||||
|
# memcache: 127.0.0.1:11211
|
||||||
|
;connstr =
|
||||||
|
|
||||||
|
#################################### Data proxy ###########################
|
||||||
|
[dataproxy]
|
||||||
|
|
||||||
|
# This enables data proxy logging, default is false
|
||||||
|
;logging = false
|
||||||
|
|
||||||
|
# How long the data proxy should wait before timing out default is 30 (seconds)
|
||||||
|
;timeout = 30
|
||||||
|
|
||||||
|
# If enabled and user is not anonymous, data proxy will add X-Grafana-User header with username into the request, default is false.
|
||||||
|
;send_user_header = false
|
||||||
|
|
||||||
|
#################################### Analytics ####################################
|
||||||
|
[analytics]
|
||||||
|
# Server reporting, sends usage counters to stats.grafana.org every 24 hours.
|
||||||
|
# No ip addresses are being tracked, only simple counters to track
|
||||||
|
# running instances, dashboard and error counts. It is very helpful to us.
|
||||||
|
# Change this option to false to disable reporting.
|
||||||
|
;reporting_enabled = true
|
||||||
|
|
||||||
|
# Set to false to disable all checks to https://grafana.net
|
||||||
|
# for new vesions (grafana itself and plugins), check is used
|
||||||
|
# in some UI views to notify that grafana or plugin update exists
|
||||||
|
# This option does not cause any auto updates, nor send any information
|
||||||
|
# only a GET request to http://grafana.com to get latest versions
|
||||||
|
;check_for_updates = true
|
||||||
|
|
||||||
|
# Google Analytics universal tracking code, only enabled if you specify an id here
|
||||||
|
;google_analytics_ua_id =
|
||||||
|
|
||||||
|
# Google Tag Manager ID, only enabled if you specify an id here
|
||||||
|
;google_tag_manager_id =
|
||||||
|
|
||||||
|
#################################### Security ####################################
|
||||||
|
[security]
|
||||||
|
# disable creation of admin user on first start of grafana
|
||||||
|
;disable_initial_admin_creation = false
|
||||||
|
|
||||||
|
# default admin user, created on startup
|
||||||
|
admin_user = {{ grafana_admin_user }}
|
||||||
|
|
||||||
|
# default admin password, can be changed before first start of grafana, or in profile settings
|
||||||
|
admin_password = {{ grafana_admin_password }}
|
||||||
|
|
||||||
|
# used for signing
|
||||||
|
;secret_key = SW2YcwTIb9zpOOhoPsMm
|
||||||
|
|
||||||
|
# disable gravatar profile images
|
||||||
|
;disable_gravatar = false
|
||||||
|
|
||||||
|
# data source proxy whitelist (ip_or_domain:port separated by spaces)
|
||||||
|
;data_source_proxy_whitelist =
|
||||||
|
|
||||||
|
# disable protection against brute force login attempts
|
||||||
|
;disable_brute_force_login_protection = false
|
||||||
|
|
||||||
|
# set to true if you host Grafana behind HTTPS. default is false.
|
||||||
|
;cookie_secure = false
|
||||||
|
|
||||||
|
# set cookie SameSite attribute. defaults to `lax`. can be set to "lax", "strict" and "none"
|
||||||
|
;cookie_samesite = lax
|
||||||
|
|
||||||
|
# set to true if you want to allow browsers to render Grafana in a <frame>, <iframe>, <embed> or <object>. default is false.
|
||||||
|
;allow_embedding = false
|
||||||
|
|
||||||
|
# Set to true if you want to enable http strict transport security (HSTS) response header.
|
||||||
|
# This is only sent when HTTPS is enabled in this configuration.
|
||||||
|
# HSTS tells browsers that the site should only be accessed using HTTPS.
|
||||||
|
# The default version will change to true in the next minor release, 6.3.
|
||||||
|
;strict_transport_security = false
|
||||||
|
|
||||||
|
# Sets how long a browser should cache HSTS. Only applied if strict_transport_security is enabled.
|
||||||
|
;strict_transport_security_max_age_seconds = 86400
|
||||||
|
|
||||||
|
# Set to true if to enable HSTS preloading option. Only applied if strict_transport_security is enabled.
|
||||||
|
;strict_transport_security_preload = false
|
||||||
|
|
||||||
|
# Set to true if to enable the HSTS includeSubDomains option. Only applied if strict_transport_security is enabled.
|
||||||
|
;strict_transport_security_subdomains = false
|
||||||
|
|
||||||
|
# Set to true to enable the X-Content-Type-Options response header.
|
||||||
|
# The X-Content-Type-Options response HTTP header is a marker used by the server to indicate that the MIME types advertised
|
||||||
|
# in the Content-Type headers should not be changed and be followed. The default will change to true in the next minor release, 6.3.
|
||||||
|
;x_content_type_options = false
|
||||||
|
|
||||||
|
# Set to true to enable the X-XSS-Protection header, which tells browsers to stop pages from loading
|
||||||
|
# when they detect reflected cross-site scripting (XSS) attacks. The default will change to true in the next minor release, 6.3.
|
||||||
|
;x_xss_protection = false
|
||||||
|
|
||||||
|
#################################### Snapshots ###########################
|
||||||
|
[snapshots]
|
||||||
|
# snapshot sharing options
|
||||||
|
;external_enabled = true
|
||||||
|
;external_snapshot_url = https://snapshots-origin.raintank.io
|
||||||
|
;external_snapshot_name = Publish to snapshot.raintank.io
|
||||||
|
|
||||||
|
# Set to true to enable this Grafana instance act as an external snapshot server and allow unauthenticated requests for
|
||||||
|
# creating and deleting snapshots.
|
||||||
|
;public_mode = false
|
||||||
|
|
||||||
|
# remove expired snapshot
|
||||||
|
;snapshot_remove_expired = true
|
||||||
|
|
||||||
|
#################################### Dashboards History ##################
|
||||||
|
[dashboards]
|
||||||
|
# Number dashboard versions to keep (per dashboard). Default: 20, Minimum: 1
|
||||||
|
;versions_to_keep = 20
|
||||||
|
|
||||||
|
#################################### Users ###############################
|
||||||
|
[users]
|
||||||
|
# disable user signup / registration
|
||||||
|
;allow_sign_up = true
|
||||||
|
|
||||||
|
# Allow non admin users to create organizations
|
||||||
|
;allow_org_create = true
|
||||||
|
|
||||||
|
# Set to true to automatically assign new users to the default organization (id 1)
|
||||||
|
;auto_assign_org = true
|
||||||
|
|
||||||
|
# Default role new users will be automatically assigned (if disabled above is set to true)
|
||||||
|
;auto_assign_org_role = Viewer
|
||||||
|
|
||||||
|
# Background text for the user field on the login page
|
||||||
|
;login_hint = email or username
|
||||||
|
;password_hint = password
|
||||||
|
|
||||||
|
# Default UI theme ("dark" or "light")
|
||||||
|
;default_theme = dark
|
||||||
|
|
||||||
|
# External user management, these options affect the organization users view
|
||||||
|
;external_manage_link_url =
|
||||||
|
;external_manage_link_name =
|
||||||
|
;external_manage_info =
|
||||||
|
|
||||||
|
# Viewers can edit/inspect dashboard settings in the browser. But not save the dashboard.
|
||||||
|
;viewers_can_edit = false
|
||||||
|
|
||||||
|
# Editors can administrate dashboard, folders and teams they create
|
||||||
|
;editors_can_admin = false
|
||||||
|
|
||||||
|
[auth]
|
||||||
|
# Login cookie name
|
||||||
|
;login_cookie_name = grafana_session
|
||||||
|
|
||||||
|
# The lifetime (days) an authenticated user can be inactive before being required to login at next visit. Default is 7 days,
|
||||||
|
;login_maximum_inactive_lifetime_days = 7
|
||||||
|
|
||||||
|
# The maximum lifetime (days) an authenticated user can be logged in since login time before being required to login. Default is 30 days.
|
||||||
|
;login_maximum_lifetime_days = 30
|
||||||
|
|
||||||
|
# How often should auth tokens be rotated for authenticated users when being active. The default is each 10 minutes.
|
||||||
|
;token_rotation_interval_minutes = 10
|
||||||
|
|
||||||
|
# Set to true to disable (hide) the login form, useful if you use OAuth, defaults to false
|
||||||
|
;disable_login_form = false
|
||||||
|
|
||||||
|
# Set to true to disable the signout link in the side menu. useful if you use auth.proxy, defaults to false
|
||||||
|
;disable_signout_menu = false
|
||||||
|
|
||||||
|
# URL to redirect the user to after sign out
|
||||||
|
;signout_redirect_url =
|
||||||
|
|
||||||
|
# Set to true to attempt login with OAuth automatically, skipping the login screen.
|
||||||
|
# This setting is ignored if multiple OAuth providers are configured.
|
||||||
|
;oauth_auto_login = false
|
||||||
|
|
||||||
|
#################################### Anonymous Auth ######################
|
||||||
|
[auth.anonymous]
|
||||||
|
# enable anonymous access
|
||||||
|
;enabled = false
|
||||||
|
|
||||||
|
# specify organization name that should be used for unauthenticated users
|
||||||
|
;org_name = Main Org.
|
||||||
|
|
||||||
|
# specify role for unauthenticated users
|
||||||
|
;org_role = Viewer
|
||||||
|
|
||||||
|
#################################### Github Auth ##########################
|
||||||
|
[auth.github]
|
||||||
|
;enabled = false
|
||||||
|
;allow_sign_up = true
|
||||||
|
;client_id = some_id
|
||||||
|
;client_secret = some_secret
|
||||||
|
;scopes = user:email,read:org
|
||||||
|
;auth_url = https://github.com/login/oauth/authorize
|
||||||
|
;token_url = https://github.com/login/oauth/access_token
|
||||||
|
;api_url = https://api.github.com/user
|
||||||
|
;team_ids =
|
||||||
|
;allowed_organizations =
|
||||||
|
|
||||||
|
#################################### Google Auth ##########################
|
||||||
|
[auth.google]
|
||||||
|
;enabled = false
|
||||||
|
;allow_sign_up = true
|
||||||
|
;client_id = some_client_id
|
||||||
|
;client_secret = some_client_secret
|
||||||
|
;scopes = https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email
|
||||||
|
;auth_url = https://accounts.google.com/o/oauth2/auth
|
||||||
|
;token_url = https://accounts.google.com/o/oauth2/token
|
||||||
|
;api_url = https://www.googleapis.com/oauth2/v1/userinfo
|
||||||
|
;allowed_domains =
|
||||||
|
|
||||||
|
#################################### Generic OAuth ##########################
|
||||||
|
[auth.generic_oauth]
|
||||||
|
;enabled = false
|
||||||
|
;name = OAuth
|
||||||
|
;allow_sign_up = true
|
||||||
|
;client_id = some_id
|
||||||
|
;client_secret = some_secret
|
||||||
|
;scopes = user:email,read:org
|
||||||
|
;email_attribute_name = email:primary
|
||||||
|
;email_attribute_path =
|
||||||
|
;auth_url = https://foo.bar/login/oauth/authorize
|
||||||
|
;token_url = https://foo.bar/login/oauth/access_token
|
||||||
|
;api_url = https://foo.bar/user
|
||||||
|
;team_ids =
|
||||||
|
;allowed_organizations =
|
||||||
|
;role_attribute_path =
|
||||||
|
;tls_skip_verify_insecure = false
|
||||||
|
;tls_client_cert =
|
||||||
|
;tls_client_key =
|
||||||
|
;tls_client_ca =
|
||||||
|
|
||||||
|
; Set to true to enable sending client_id and client_secret via POST body instead of Basic authentication HTTP header
|
||||||
|
; This might be required if the OAuth provider is not RFC6749 compliant, only supporting credentials passed via POST payload
|
||||||
|
;send_client_credentials_via_post = false
|
||||||
|
|
||||||
|
#################################### SAML Auth ###########################
|
||||||
|
[auth.saml] # Enterprise only
|
||||||
|
# Defaults to false. If true, the feature is enabled.
|
||||||
|
;enabled = false
|
||||||
|
|
||||||
|
# Base64-encoded public X.509 certificate. Used to sign requests to the IdP
|
||||||
|
;certificate =
|
||||||
|
|
||||||
|
# Path to the public X.509 certificate. Used to sign requests to the IdP
|
||||||
|
;certificate_path =
|
||||||
|
|
||||||
|
# Base64-encoded private key. Used to decrypt assertions from the IdP
|
||||||
|
;private_key =
|
||||||
|
|
||||||
|
;# Path to the private key. Used to decrypt assertions from the IdP
|
||||||
|
;private_key_path =
|
||||||
|
|
||||||
|
# Base64-encoded IdP SAML metadata XML. Used to verify and obtain binding locations from the IdP
|
||||||
|
;idp_metadata =
|
||||||
|
|
||||||
|
# Path to the SAML metadata XML. Used to verify and obtain binding locations from the IdP
|
||||||
|
;idp_metadata_path =
|
||||||
|
|
||||||
|
# URL to fetch SAML IdP metadata. Used to verify and obtain binding locations from the IdP
|
||||||
|
;idp_metadata_url =
|
||||||
|
|
||||||
|
# Duration, since the IdP issued a response and the SP is allowed to process it. Defaults to 90 seconds.
|
||||||
|
;max_issue_delay = 90s
|
||||||
|
|
||||||
|
# Duration, for how long the SP's metadata should be valid. Defaults to 48 hours.
|
||||||
|
;metadata_valid_duration = 48h
|
||||||
|
|
||||||
|
# Friendly name or name of the attribute within the SAML assertion to use as the user's name
|
||||||
|
;assertion_attribute_name = displayName
|
||||||
|
|
||||||
|
# Friendly name or name of the attribute within the SAML assertion to use as the user's login handle
|
||||||
|
;assertion_attribute_login = mail
|
||||||
|
|
||||||
|
# Friendly name or name of the attribute within the SAML assertion to use as the user's email
|
||||||
|
;assertion_attribute_email = mail
|
||||||
|
|
||||||
|
#################################### Grafana.com Auth ####################
|
||||||
|
[auth.grafana_com]
|
||||||
|
;enabled = false
|
||||||
|
;allow_sign_up = true
|
||||||
|
;client_id = some_id
|
||||||
|
;client_secret = some_secret
|
||||||
|
;scopes = user:email
|
||||||
|
;allowed_organizations =
|
||||||
|
|
||||||
|
#################################### Auth Proxy ##########################
|
||||||
|
[auth.proxy]
|
||||||
|
;enabled = false
|
||||||
|
;header_name = X-WEBAUTH-USER
|
||||||
|
;header_property = username
|
||||||
|
;auto_sign_up = true
|
||||||
|
;sync_ttl = 60
|
||||||
|
;whitelist = 192.168.1.1, 192.168.2.1
|
||||||
|
;headers = Email:X-User-Email, Name:X-User-Name
|
||||||
|
# Read the auth proxy docs for details on what the setting below enables
|
||||||
|
;enable_login_token = false
|
||||||
|
|
||||||
|
#################################### Basic Auth ##########################
|
||||||
|
[auth.basic]
|
||||||
|
;enabled = true
|
||||||
|
|
||||||
|
#################################### Auth LDAP ##########################
|
||||||
|
[auth.ldap]
|
||||||
|
;enabled = false
|
||||||
|
;config_file = /etc/grafana/ldap.toml
|
||||||
|
;allow_sign_up = true
|
||||||
|
|
||||||
|
# LDAP backround sync (Enterprise only)
|
||||||
|
# At 1 am every day
|
||||||
|
;sync_cron = "0 0 1 * * *"
|
||||||
|
;active_sync_enabled = true
|
||||||
|
|
||||||
|
#################################### SMTP / Emailing ##########################
|
||||||
|
[smtp]
|
||||||
|
;enabled = false
|
||||||
|
;host = localhost:25
|
||||||
|
;user =
|
||||||
|
# If the password contains # or ; you have to wrap it with triple quotes. Ex """#password;"""
|
||||||
|
;password =
|
||||||
|
;cert_file =
|
||||||
|
;key_file =
|
||||||
|
;skip_verify = false
|
||||||
|
;from_address = admin@grafana.localhost
|
||||||
|
;from_name = Grafana
|
||||||
|
# EHLO identity in SMTP dialog (defaults to instance_name)
|
||||||
|
;ehlo_identity = dashboard.example.com
|
||||||
|
|
||||||
|
[emails]
|
||||||
|
;welcome_email_on_sign_up = false
|
||||||
|
|
||||||
|
#################################### Logging ##########################
|
||||||
|
[log]
|
||||||
|
# Either "console", "file", "syslog". Default is console and file
|
||||||
|
# Use space to separate multiple modes, e.g. "console file"
|
||||||
|
;mode = console file
|
||||||
|
|
||||||
|
# Either "debug", "info", "warn", "error", "critical", default is "info"
|
||||||
|
;level = info
|
||||||
|
|
||||||
|
# optional settings to set different levels for specific loggers. Ex filters = sqlstore:debug
|
||||||
|
;filters =
|
||||||
|
|
||||||
|
# For "console" mode only
|
||||||
|
[log.console]
|
||||||
|
;level =
|
||||||
|
|
||||||
|
# log line format, valid options are text, console and json
|
||||||
|
;format = console
|
||||||
|
|
||||||
|
# For "file" mode only
|
||||||
|
[log.file]
|
||||||
|
;level =
|
||||||
|
|
||||||
|
# log line format, valid options are text, console and json
|
||||||
|
;format = text
|
||||||
|
|
||||||
|
# This enables automated log rotate(switch of following options), default is true
|
||||||
|
;log_rotate = true
|
||||||
|
|
||||||
|
# Max line number of single file, default is 1000000
|
||||||
|
;max_lines = 1000000
|
||||||
|
|
||||||
|
# Max size shift of single file, default is 28 means 1 << 28, 256MB
|
||||||
|
;max_size_shift = 28
|
||||||
|
|
||||||
|
# Segment log daily, default is true
|
||||||
|
;daily_rotate = true
|
||||||
|
|
||||||
|
# Expired days of log file(delete after max days), default is 7
|
||||||
|
;max_days = 7
|
||||||
|
|
||||||
|
[log.syslog]
|
||||||
|
;level =
|
||||||
|
|
||||||
|
# log line format, valid options are text, console and json
|
||||||
|
;format = text
|
||||||
|
|
||||||
|
# Syslog network type and address. This can be udp, tcp, or unix. If left blank, the default unix endpoints will be used.
|
||||||
|
;network =
|
||||||
|
;address =
|
||||||
|
|
||||||
|
# Syslog facility. user, daemon and local0 through local7 are valid.
|
||||||
|
;facility =
|
||||||
|
|
||||||
|
# Syslog tag. By default, the process' argv[0] is used.
|
||||||
|
;tag =
|
||||||
|
|
||||||
|
#################################### Alerting ############################
|
||||||
|
[alerting]
|
||||||
|
# Disable alerting engine & UI features
|
||||||
|
;enabled = true
|
||||||
|
# Makes it possible to turn off alert rule execution but alerting UI is visible
|
||||||
|
;execute_alerts = true
|
||||||
|
|
||||||
|
# Default setting for new alert rules. Defaults to categorize error and timeouts as alerting. (alerting, keep_state)
|
||||||
|
;error_or_timeout = alerting
|
||||||
|
|
||||||
|
# Default setting for how Grafana handles nodata or null values in alerting. (alerting, no_data, keep_state, ok)
|
||||||
|
;nodata_or_nullvalues = no_data
|
||||||
|
|
||||||
|
# Alert notifications can include images, but rendering many images at the same time can overload the server
|
||||||
|
# This limit will protect the server from render overloading and make sure notifications are sent out quickly
|
||||||
|
;concurrent_render_limit = 5
|
||||||
|
|
||||||
|
|
||||||
|
# Default setting for alert calculation timeout. Default value is 30
|
||||||
|
;evaluation_timeout_seconds = 30
|
||||||
|
|
||||||
|
# Default setting for alert notification timeout. Default value is 30
|
||||||
|
;notification_timeout_seconds = 30
|
||||||
|
|
||||||
|
# Default setting for max attempts to sending alert notifications. Default value is 3
|
||||||
|
;max_attempts = 3
|
||||||
|
|
||||||
|
#################################### Explore #############################
|
||||||
|
[explore]
|
||||||
|
# Enable the Explore section
|
||||||
|
;enabled = true
|
||||||
|
|
||||||
|
#################################### Internal Grafana Metrics ##########################
|
||||||
|
# Metrics available at HTTP API Url /metrics
|
||||||
|
[metrics]
|
||||||
|
# Disable / Enable internal metrics
|
||||||
|
;enabled = true
|
||||||
|
# Disable total stats (stat_totals_*) metrics to be generated
|
||||||
|
;disable_total_stats = false
|
||||||
|
|
||||||
|
# Publish interval
|
||||||
|
;interval_seconds = 10
|
||||||
|
|
||||||
|
# Send internal metrics to Graphite
|
||||||
|
[metrics.graphite]
|
||||||
|
# Enable by setting the address setting (ex localhost:2003)
|
||||||
|
;address =
|
||||||
|
;prefix = prod.grafana.%(instance_name)s.
|
||||||
|
|
||||||
|
#################################### Distributed tracing ############
|
||||||
|
[tracing.jaeger]
|
||||||
|
# Enable by setting the address sending traces to jaeger (ex localhost:6831)
|
||||||
|
;address = localhost:6831
|
||||||
|
# Tag that will always be included in when creating new spans. ex (tag1:value1,tag2:value2)
|
||||||
|
;always_included_tag = tag1:value1
|
||||||
|
# Type specifies the type of the sampler: const, probabilistic, rateLimiting, or remote
|
||||||
|
;sampler_type = const
|
||||||
|
# jaeger samplerconfig param
|
||||||
|
# for "const" sampler, 0 or 1 for always false/true respectively
|
||||||
|
# for "probabilistic" sampler, a probability between 0 and 1
|
||||||
|
# for "rateLimiting" sampler, the number of spans per second
|
||||||
|
# for "remote" sampler, param is the same as for "probabilistic"
|
||||||
|
# and indicates the initial sampling rate before the actual one
|
||||||
|
# is received from the mothership
|
||||||
|
;sampler_param = 1
|
||||||
|
# Whether or not to use Zipkin propagation (x-b3- HTTP headers).
|
||||||
|
;zipkin_propagation = false
|
||||||
|
# Setting this to true disables shared RPC spans.
|
||||||
|
# Not disabling is the most common setting when using Zipkin elsewhere in your infrastructure.
|
||||||
|
;disable_shared_zipkin_spans = false
|
||||||
|
|
||||||
|
#################################### Grafana.com integration ##########################
|
||||||
|
# Url used to import dashboards directly from Grafana.com
|
||||||
|
[grafana_com]
|
||||||
|
;url = https://grafana.com
|
||||||
|
|
||||||
|
#################################### External image storage ##########################
|
||||||
|
[external_image_storage]
|
||||||
|
# Used for uploading images to public servers so they can be included in slack/email messages.
|
||||||
|
# you can choose between (s3, webdav, gcs, azure_blob, local)
|
||||||
|
;provider =
|
||||||
|
|
||||||
|
[external_image_storage.s3]
|
||||||
|
;bucket =
|
||||||
|
;region =
|
||||||
|
;path =
|
||||||
|
;access_key =
|
||||||
|
;secret_key =
|
||||||
|
|
||||||
|
[external_image_storage.webdav]
|
||||||
|
;url =
|
||||||
|
;public_url =
|
||||||
|
;username =
|
||||||
|
;password =
|
||||||
|
|
||||||
|
[external_image_storage.gcs]
|
||||||
|
;key_file =
|
||||||
|
;bucket =
|
||||||
|
;path =
|
||||||
|
|
||||||
|
[external_image_storage.azure_blob]
|
||||||
|
;account_name =
|
||||||
|
;account_key =
|
||||||
|
;container_name =
|
||||||
|
|
||||||
|
[external_image_storage.local]
|
||||||
|
# does not require any configuration
|
||||||
|
|
||||||
|
[rendering]
|
||||||
|
# Options to configure a remote HTTP image rendering service, e.g. using https://github.com/grafana/grafana-image-renderer.
|
||||||
|
# URL to a remote HTTP image renderer service, e.g. http://localhost:8081/render, will enable Grafana to render panels and dashboards to PNG-images using HTTP requests to an external service.
|
||||||
|
;server_url =
|
||||||
|
# If the remote HTTP image renderer service runs on a different server than the Grafana server you may have to configure this to a URL where Grafana is reachable, e.g. http://grafana.domain/.
|
||||||
|
;callback_url =
|
||||||
|
|
||||||
|
[enterprise]
|
||||||
|
# Path to a valid Grafana Enterprise license.jwt file
|
||||||
|
;license_path =
|
||||||
|
|
||||||
|
[panels]
|
||||||
|
# If set to true Grafana will allow script tags in text panels. Not recommended as it enable XSS vulnerabilities.
|
||||||
|
;disable_sanitize_html = false
|
||||||
|
|
||||||
|
[plugins]
|
||||||
|
;enable_alpha = false
|
||||||
|
;app_tls_skip_verify_insecure = false
|
|
@ -0,0 +1,11 @@
|
||||||
|
location {{ alertmanager_web_path }}/ {
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header Host $http_host;
|
||||||
|
proxy_redirect off;
|
||||||
|
|
||||||
|
auth_basic "Authentication required for Alert Manager";
|
||||||
|
auth_basic_user_file /etc/nginx/.htpasswd-alertmanager;
|
||||||
|
|
||||||
|
proxy_pass http://localhost:{{ alertmanager_port }}/;
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
{{ ansible_managed | comment }}
|
||||||
|
|
||||||
|
location {{ grafana_web_path }}/ {
|
||||||
|
proxy_pass http://localhost:{{ grafana_port }}/;
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
location {{ prometheus_web_path }}/ {
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header Host $http_host;
|
||||||
|
proxy_redirect off;
|
||||||
|
|
||||||
|
auth_basic "Authentication required";
|
||||||
|
auth_basic_user_file /etc/nginx/.htpasswd-prometheus;
|
||||||
|
|
||||||
|
proxy_pass http://localhost:{{ prometheus_port }}/;
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
# Set the command-line arguments to pass to the server.
|
||||||
|
ARGS="--web.external-url={{ prometheus_protocol }}://{{ prometheus_domain }}{{ prometheus_path }} \
|
||||||
|
--web.route-prefix=/ \
|
||||||
|
--web.listen-address='0.0.0.0:{{ prometheus_port }}'"
|
||||||
|
|
||||||
|
|
||||||
|
# Prometheus supports the following options:
|
||||||
|
# --config.file="/etc/prometheus/prometheus.yml"
|
||||||
|
# Prometheus configuration file path.
|
||||||
|
# --web.listen-address="0.0.0.0:9090"
|
||||||
|
# Address to listen on for UI, API, and telemetry.
|
||||||
|
# --web.read-timeout=5m Maximum duration before timing out read of the
|
||||||
|
# request, and closing idle connections.
|
||||||
|
# --web.max-connections=512 Maximum number of simultaneous connections.
|
||||||
|
# --web.external-url=<URL> The URL under which Prometheus is externally
|
||||||
|
# reachable (for example, if Prometheus is served
|
||||||
|
# via a reverse proxy). Used for generating
|
||||||
|
# relative and absolute links back to Prometheus
|
||||||
|
# itself. If the URL has a path portion, it will
|
||||||
|
# be used to prefix all HTTP endpoints served by
|
||||||
|
# Prometheus. If omitted, relevant URL components
|
||||||
|
# will be derived automatically.
|
||||||
|
# --web.route-prefix=<path> Prefix for the internal routes of web endpoints.
|
||||||
|
# Defaults to path of --web.external-url.
|
||||||
|
# --web.local-assets="/usr/share/prometheus/web/"
|
||||||
|
# Path to static asset/templates directory.
|
||||||
|
# --web.user-assets=<path> Path to static asset directory, available at
|
||||||
|
# /user.
|
||||||
|
# --web.enable-lifecycle Enable shutdown and reload via HTTP request.
|
||||||
|
# --web.enable-admin-api Enables API endpoints for admin control actions.
|
||||||
|
# --web.console.templates="/etc/prometheus/consoles"
|
||||||
|
# Path to the console template directory,
|
||||||
|
# available at /consoles.
|
||||||
|
# --web.console.libraries="/etc/prometheus/console_libraries"
|
||||||
|
# Path to the console library directory.
|
||||||
|
# --storage.tsdb.path="/var/lib/prometheus/metrics2/"
|
||||||
|
# Base path for metrics storage.
|
||||||
|
# --storage.tsdb.min-block-duration=2h
|
||||||
|
# Minimum duration of a data block before being
|
||||||
|
# persisted.
|
||||||
|
# --storage.tsdb.max-block-duration=<duration>
|
||||||
|
# Maximum duration compacted blocks may span.
|
||||||
|
# (Defaults to 10% of the retention period)
|
||||||
|
# --storage.tsdb.retention=15d
|
||||||
|
# How long to retain samples in the storage.
|
||||||
|
# --storage.tsdb.no-lockfile
|
||||||
|
# Do not create lockfile in data directory.
|
||||||
|
# --alertmanager.notification-queue-capacity=10000
|
||||||
|
# The capacity of the queue for pending alert
|
||||||
|
# manager notifications.
|
||||||
|
# --alertmanager.timeout=10s
|
||||||
|
# Timeout for sending alerts to Alertmanager.
|
||||||
|
# --query.lookback-delta=5m The delta difference allowed for retrieving
|
||||||
|
# metrics during expression evaluations.
|
||||||
|
# --query.timeout=2m Maximum time a query may take before being
|
||||||
|
# aborted.
|
||||||
|
# --query.max-concurrency=20
|
||||||
|
# Maximum number of queries executed concurrently.
|
||||||
|
# --log.level=info Only log messages with the given severity or
|
||||||
|
# above. One of: [debug, info, warn, error]
|
|
@ -0,0 +1,31 @@
|
||||||
|
global:
|
||||||
|
scrape_interval: 5s
|
||||||
|
scrape_timeout: 5s
|
||||||
|
|
||||||
|
alerting:
|
||||||
|
alertmanagers:
|
||||||
|
- static_configs:
|
||||||
|
- targets:
|
||||||
|
- localhost:{{ alertmanager_port }}
|
||||||
|
scheme: http
|
||||||
|
timeout: 10s
|
||||||
|
|
||||||
|
rule_files:
|
||||||
|
- '/etc/prometheus/rules.d/*.yml'
|
||||||
|
|
||||||
|
scrape_configs:
|
||||||
|
{% for host in groups['all'] %}
|
||||||
|
{% with
|
||||||
|
node_exporter_password = hostvars[host].node_exporter_password | default(node_exporter_password),
|
||||||
|
node_exporter_path = hostvars[host].node_exporter_path | default(node_exporter_path)
|
||||||
|
%}
|
||||||
|
- job_name: 'node-exporter-{{ host }}'
|
||||||
|
basic_auth:
|
||||||
|
username: admin
|
||||||
|
password: {{ node_exporter_password }}
|
||||||
|
metrics_path: {{ node_exporter_path }}
|
||||||
|
static_configs:
|
||||||
|
- targets:
|
||||||
|
- '{{ host }}'
|
||||||
|
{% endwith %}
|
||||||
|
{% endfor %}
|
|
@ -0,0 +1,40 @@
|
||||||
|
#jinja2: trim_blocks: True, lstrip_blocks: True
|
||||||
|
{{ ansible_managed | comment }}
|
||||||
|
# http://prometheus.io/docs/operating/configuration/
|
||||||
|
|
||||||
|
global:
|
||||||
|
scrape_interval: 5s
|
||||||
|
scrape_timeout: 5s
|
||||||
|
|
||||||
|
rule_files:
|
||||||
|
- {{ prometheus_config_dir }}/rules/*.rules
|
||||||
|
|
||||||
|
alerting:
|
||||||
|
alertmanagers:
|
||||||
|
- scheme: http
|
||||||
|
static_configs:
|
||||||
|
- targets:
|
||||||
|
- "localhost:{{ alertmanager_port }}"
|
||||||
|
timeout: 10s
|
||||||
|
{% if prometheus_alert_relabel_configs | length > 0 %}
|
||||||
|
alert_relabel_configs:
|
||||||
|
{{ prometheus_alert_relabel_configs | to_nice_yaml(indent=2) | indent(2,False) }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
scrape_configs:
|
||||||
|
{% for host in groups['all'] %}
|
||||||
|
{% with
|
||||||
|
node_exporter_password = hostvars[host].node_exporter_password | default(node_exporter_password),
|
||||||
|
node_exporter_path = hostvars[host].node_exporter_path | default(node_exporter_path),
|
||||||
|
node_exporter_public_port = hostvars[host].node_exporter_public_port | default(node_exporter_public_port)
|
||||||
|
%}
|
||||||
|
- job_name: 'node-exporter-{{ host }}'
|
||||||
|
basic_auth:
|
||||||
|
username: admin
|
||||||
|
password: {{ node_exporter_password }}
|
||||||
|
metrics_path: {{ node_exporter_path }}
|
||||||
|
static_configs:
|
||||||
|
- targets:
|
||||||
|
- '{{ host }}:{{ node_exporter_public_port }}'
|
||||||
|
{% endwith %}
|
||||||
|
{% endfor %}
|
|
@ -0,0 +1,24 @@
|
||||||
|
umurmur_user_password: ""
|
||||||
|
umurmur_channel_links:
|
||||||
|
- source: "{{ umurmur_default_channel }}"
|
||||||
|
destinations: >-
|
||||||
|
{{ umurmur_channels
|
||||||
|
| selectattr('parent', 'defined')
|
||||||
|
| selectattr('parent', '==', umurmur_default_channel)
|
||||||
|
| map(attribute='name')
|
||||||
|
| list
|
||||||
|
}}
|
||||||
|
umurmur_ssl_group: "{{ acme_ssl_group }}"
|
||||||
|
umurmur_certificate: "{{ acme_certs_dir }}/{{ umurmur_domain }}.d/fullchain.pem"
|
||||||
|
umurmur_private_key: "{{ acme_keys_dir }}/{{ umurmur_domain }}.pem"
|
||||||
|
umurmur_version: master
|
||||||
|
umurmur_ispublic: yes
|
||||||
|
umurmur_port: "64738"
|
||||||
|
|
||||||
|
mumble_web_certificate: "{{ acme_certs_dir }}/{{ mumble_web_domain }}.d/fullchain.pem"
|
||||||
|
mumble_web_trusted_certificate: "{{ acme_certs_dir }}/{{ mumble_web_domain }}.d/chain.pem"
|
||||||
|
mumble_web_private_key: "{{ acme_keys_dir }}/{{ mumble_web_domain }}.pem"
|
||||||
|
mumble_web_dhparam: "/etc/nginx/ssl/dhparam.pem"
|
||||||
|
mumble_web_www_dir: /var/www/mumble-web
|
||||||
|
mumble_web_version: master
|
||||||
|
mumble_web_websockify_port: "64737"
|
|
@ -0,0 +1,21 @@
|
||||||
|
- name: reload systemd
|
||||||
|
systemd:
|
||||||
|
daemon_reload: yes
|
||||||
|
register: systemd_reloaded
|
||||||
|
|
||||||
|
- name: restart umurmur
|
||||||
|
service:
|
||||||
|
name: umurmur
|
||||||
|
state: restarted
|
||||||
|
when:
|
||||||
|
- not (umurmur_started.changed | default(false)) or (systemd_reloaded.changed | default(false))
|
||||||
|
|
||||||
|
- name: restart mumble-web
|
||||||
|
service:
|
||||||
|
name: mumble-web
|
||||||
|
state: restarted
|
||||||
|
when:
|
||||||
|
- not (mumble_web_started.changed | default(false)) or (systemd_reloaded.changed | default(false))
|
||||||
|
|
||||||
|
- name: reload nginx
|
||||||
|
include_tasks: ../handlers/nginx.yml
|
|
@ -0,0 +1,7 @@
|
||||||
|
- name: Validate Nginx config
|
||||||
|
command: nginx -t
|
||||||
|
|
||||||
|
- name: Reload Nginx server
|
||||||
|
service:
|
||||||
|
name: nginx
|
||||||
|
state: reloaded
|
|
@ -0,0 +1,5 @@
|
||||||
|
dependencies:
|
||||||
|
- role: geerlingguy.nodejs
|
||||||
|
vars:
|
||||||
|
nodejs_install_npm_user: root
|
||||||
|
tags: [nodejs,mumble_web]
|
|
@ -0,0 +1,11 @@
|
||||||
|
- name: Install and configure umurmur server
|
||||||
|
import_tasks: umurmur.yml
|
||||||
|
tags: umurmur
|
||||||
|
|
||||||
|
- name: Install and configure mumble web client
|
||||||
|
import_tasks: mumble_web.yml
|
||||||
|
tags: mumble_web
|
||||||
|
|
||||||
|
- name: Configure Nginx for mumble web client
|
||||||
|
import_tasks: nginx.yml
|
||||||
|
tags: nginx
|
|
@ -0,0 +1,77 @@
|
||||||
|
- name: Install websockify
|
||||||
|
apt:
|
||||||
|
name: websockify
|
||||||
|
state: present
|
||||||
|
notify: restart mumble-web
|
||||||
|
tags: mumble_web_install
|
||||||
|
|
||||||
|
- name: Clone mumble-web git repository
|
||||||
|
git:
|
||||||
|
repo: https://github.com/Johni0702/mumble-web.git
|
||||||
|
dest: "{{ mumble_web_www_dir }}"
|
||||||
|
version: "{{ mumble_web_version }}"
|
||||||
|
tags: mumble_web_install
|
||||||
|
|
||||||
|
- name: Change mumble-web git repository's permissions
|
||||||
|
file:
|
||||||
|
path: "{{ mumble_web_www_dir }}"
|
||||||
|
owner: root
|
||||||
|
group: www-data
|
||||||
|
mode: "755"
|
||||||
|
state: directory
|
||||||
|
tags: mumble_web_install
|
||||||
|
|
||||||
|
- name: Build mumble-web from sources
|
||||||
|
command: npm install
|
||||||
|
args:
|
||||||
|
chdir: "{{ mumble_web_www_dir }}"
|
||||||
|
register: _mumble_web_installed
|
||||||
|
changed_when: _mumble_web_installed.stdout is regex('(added|removed) [0-9]+ package')
|
||||||
|
tags: [mumble_web_install,mumble_web_build]
|
||||||
|
|
||||||
|
- name: Apply security suggestions
|
||||||
|
command: npm audit fix
|
||||||
|
args:
|
||||||
|
chdir: "{{ mumble_web_www_dir }}"
|
||||||
|
when: _mumble_web_installed is changed and _mumble_web_installed.stdout is search('npm audit fix')
|
||||||
|
tags: [mumble_web_install,mumble_web_build]
|
||||||
|
|
||||||
|
- name: Build mumble-web assets
|
||||||
|
command: npm run build
|
||||||
|
args:
|
||||||
|
chdir: "{{ mumble_web_www_dir }}"
|
||||||
|
register: _mumble_web_assets_built
|
||||||
|
when: _mumble_web_installed is changed
|
||||||
|
tags: [mumble_web_install,mumble_web_build]
|
||||||
|
|
||||||
|
- name: Copy mumble-web config file
|
||||||
|
template:
|
||||||
|
src: mumble-web.js.j2
|
||||||
|
dest: "{{ mumble_web_www_dir }}/dist/config.local.js"
|
||||||
|
owner: root
|
||||||
|
group: www-data
|
||||||
|
mode: "640"
|
||||||
|
tags: [mumble_web_install,mumble_web_config]
|
||||||
|
|
||||||
|
- name: Copy mumble-web systemd service
|
||||||
|
template:
|
||||||
|
src: mumble-web.service.j2
|
||||||
|
dest: /etc/systemd/system/mumble-web.service
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "644"
|
||||||
|
notify:
|
||||||
|
- reload systemd
|
||||||
|
- restart mumble-web
|
||||||
|
tags: [mumble_web_install,mumble_web_config]
|
||||||
|
|
||||||
|
- name: Start mumble-web service
|
||||||
|
service:
|
||||||
|
name: mumble-web
|
||||||
|
state: started
|
||||||
|
enabled: yes
|
||||||
|
register: mumble_web_started
|
||||||
|
tags: mumble_web_run
|
||||||
|
|
||||||
|
- name: Trigger mumble-web handlers
|
||||||
|
meta: flush_handlers
|
|
@ -0,0 +1,18 @@
|
||||||
|
- name: Copy Nginx config file
|
||||||
|
template:
|
||||||
|
src: nginx.conf.j2
|
||||||
|
dest: /etc/nginx/sites-available/mumble.conf
|
||||||
|
owner: root
|
||||||
|
group: www-data
|
||||||
|
mode: "755"
|
||||||
|
notify: reload nginx
|
||||||
|
|
||||||
|
- name: Enable Nginx config file
|
||||||
|
file:
|
||||||
|
src: /etc/nginx/sites-available/mumble.conf
|
||||||
|
path: /etc/nginx/sites-enabled/mumble.conf
|
||||||
|
state: link
|
||||||
|
notify: reload nginx
|
||||||
|
|
||||||
|
- name: Trigger Nginx handlers
|
||||||
|
meta: flush_handlers
|
|
@ -0,0 +1,88 @@
|
||||||
|
- name: Install umurmur build dependencies
|
||||||
|
apt:
|
||||||
|
name:
|
||||||
|
- git
|
||||||
|
- build-essential
|
||||||
|
- cmake
|
||||||
|
- libconfig-dev
|
||||||
|
- libprotobuf-c-dev
|
||||||
|
- libmbedtls-dev
|
||||||
|
- ssl-cert
|
||||||
|
|
||||||
|
- name: Clone umurmur git repository
|
||||||
|
git:
|
||||||
|
repo: https://github.com/umurmur/umurmur.git
|
||||||
|
dest: /opt/umurmur
|
||||||
|
version: "{{ umurmur_version }}"
|
||||||
|
|
||||||
|
- name: Change umurmur git repository's permissions
|
||||||
|
file:
|
||||||
|
path: /opt/umurmur
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "775"
|
||||||
|
state: directory
|
||||||
|
|
||||||
|
- name: Create the build directory
|
||||||
|
file:
|
||||||
|
path: /opt/umurmur/build
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "775"
|
||||||
|
state: directory
|
||||||
|
|
||||||
|
- name: Generate the Makefile with cmake
|
||||||
|
shell: cd /opt/umurmur/build && cmake .. -DSSL=mbedtls
|
||||||
|
changed_when: no
|
||||||
|
|
||||||
|
- name: Build umurmur from source
|
||||||
|
make:
|
||||||
|
chdir: /opt/umurmur/build
|
||||||
|
changed_when: _umurmur_built.stdout_lines | length > 1
|
||||||
|
register: _umurmur_built
|
||||||
|
|
||||||
|
- name: Install umurmur
|
||||||
|
make:
|
||||||
|
chdir: /opt/umurmur/build
|
||||||
|
target: install
|
||||||
|
changed_when: "'Installing' in _umurmur_installed.stdout"
|
||||||
|
register: _umurmur_installed
|
||||||
|
notify: restart umurmur
|
||||||
|
|
||||||
|
- name: Copy umurmur config file
|
||||||
|
template:
|
||||||
|
src: umurmur.conf.j2
|
||||||
|
dest: /usr/local/etc/umurmur.conf
|
||||||
|
owner: root
|
||||||
|
group: "{{ umurmur_ssl_group }}"
|
||||||
|
mode: "640"
|
||||||
|
validate: /usr/local/bin/umurmurd -t -c %s
|
||||||
|
notify: restart umurmur
|
||||||
|
tags: umurmur_config
|
||||||
|
|
||||||
|
- name: Copy umurmur systemd service
|
||||||
|
template:
|
||||||
|
src: umurmur.service.j2
|
||||||
|
dest: /etc/systemd/system/umurmur.service
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "644"
|
||||||
|
notify:
|
||||||
|
- reload systemd
|
||||||
|
- restart umurmur
|
||||||
|
|
||||||
|
- name: Start umurmur service
|
||||||
|
service:
|
||||||
|
name: umurmur
|
||||||
|
enabled: yes
|
||||||
|
state: started
|
||||||
|
register: umurmur_started
|
||||||
|
|
||||||
|
- name: Trigger umurmur handlers
|
||||||
|
meta: flush_handlers
|
||||||
|
|
||||||
|
- name: Open umurmur port with UFW
|
||||||
|
ufw:
|
||||||
|
rule: allow
|
||||||
|
port: "{{ umurmur_port }}"
|
||||||
|
when: umurmur_ispublic | bool
|
|
@ -0,0 +1,23 @@
|
||||||
|
{{ ansible_managed | comment('c') }}
|
||||||
|
// You can overwrite the default configuration values set in [config.js] here.
|
||||||
|
// There should never be any required changes to this file and you can always
|
||||||
|
// simply copy it over when updating to a new version.
|
||||||
|
|
||||||
|
let config = window.mumbleWebConfig // eslint-disable-line no-unused-vars
|
||||||
|
|
||||||
|
// E.g. changing default address and theme:
|
||||||
|
// config.defaults.address = 'voice.example.com'
|
||||||
|
// config.defaults.theme = 'MetroMumbleDark
|
||||||
|
|
||||||
|
// Which fields to show on the Connect to Server dialog
|
||||||
|
config.connectDialog.address = false
|
||||||
|
config.connectDialog.port = false
|
||||||
|
config.connectDialog.token = false
|
||||||
|
config.connectDialog.password = {{ (umurmur_user_password != '') | lower }}
|
||||||
|
|
||||||
|
// Default values for user settings
|
||||||
|
// You can see your current value by typing `localStorage.getItem('mumble.$setting')` in the web console.
|
||||||
|
config.settings.pttKey = 'shift'
|
||||||
|
|
||||||
|
// Default values (can be changed by passing a query parameter of the same name)
|
||||||
|
config.defaults.address = "{{ mumble_web_domain }}/mumble"
|
|
@ -0,0 +1,26 @@
|
||||||
|
[Unit]
|
||||||
|
Description=Mumble web client using websockets
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=nobody
|
||||||
|
Group=nogroup
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=3
|
||||||
|
PIDFile=/run/mumble-web.pid
|
||||||
|
ExecStart=/usr/bin/websockify --ssl-target {{ mumble_web_websockify_port }} localhost:{{ umurmur_port }}
|
||||||
|
ExecReload=/bin/kill -HUP $MAINPID
|
||||||
|
PrivateDevices=yes
|
||||||
|
PrivateTmp=yes
|
||||||
|
ProtectSystem=strict
|
||||||
|
ProtectHome=yes
|
||||||
|
ProtectControlGroups=yes
|
||||||
|
ProtectKernelModules=yes
|
||||||
|
ProtectKernelTunables=yes
|
||||||
|
LockPersonality=yes
|
||||||
|
NoNewPrivileges=yes
|
||||||
|
LimitRTPRIO=1
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
|
@ -0,0 +1,65 @@
|
||||||
|
{{ ansible_managed | comment }}
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
listen [::]:80;
|
||||||
|
server_name {{ mumble_web_domain }};
|
||||||
|
|
||||||
|
location / {
|
||||||
|
return 301 https://$host$request_uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
{% if acme_challenge_dir is defined %}
|
||||||
|
location ^~ /.well-known/acme-challenge/ {
|
||||||
|
allow all;
|
||||||
|
root {{ acme_challenge_dir }};
|
||||||
|
try_files $uri =404;
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 443 ssl http2;
|
||||||
|
listen [::]:443 ssl http2;
|
||||||
|
|
||||||
|
server_name {{ mumble_web_domain }};
|
||||||
|
|
||||||
|
ssl_certificate {{ mumble_web_certificate }};
|
||||||
|
ssl_certificate_key {{ mumble_web_private_key }};
|
||||||
|
|
||||||
|
ssl_session_timeout 1d;
|
||||||
|
ssl_session_cache shared:AnsibleSSL:10m; # about 40000 sessions
|
||||||
|
ssl_session_tickets off;
|
||||||
|
|
||||||
|
{% if mumble_web_dhparam is defined and mumble_web_dhparam != '' %}
|
||||||
|
ssl_dhparam {{ mumble_web_dhparam }};
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
|
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
|
||||||
|
ssl_prefer_server_ciphers off;
|
||||||
|
|
||||||
|
add_header Strict-Transport-Security "max-age=63072000" always;
|
||||||
|
|
||||||
|
# OCSP stapling
|
||||||
|
ssl_stapling on;
|
||||||
|
ssl_stapling_verify on;
|
||||||
|
|
||||||
|
# Verify chain of trust of OCSP response using Root CA and Intermediate certs
|
||||||
|
ssl_trusted_certificate {{ mumble_web_trusted_certificate }};
|
||||||
|
|
||||||
|
location / {
|
||||||
|
root /var/www/mumble-web/dist/;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /mumble {
|
||||||
|
proxy_pass http://127.0.0.1:{{ mumble_web_websockify_port }};
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection $connection_upgrade;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
map $http_upgrade $connection_upgrade {
|
||||||
|
default upgrade;
|
||||||
|
'' close;
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
{{ ansible_managed | comment }}
|
||||||
|
max_bandwidth = 48000;
|
||||||
|
welcometext = {{ umurmur_welcome_text
|
||||||
|
if umurmur_welcome_text is string
|
||||||
|
else (umurmur_welcome_text | join('<br />'))
|
||||||
|
| to_json }};
|
||||||
|
certificate = {{ umurmur_certificate | to_json }};
|
||||||
|
private_key = {{ umurmur_private_key | to_json }};
|
||||||
|
password = {{ umurmur_user_password | to_json }};
|
||||||
|
{% if umurmur_admin_password is defined %}
|
||||||
|
admin_password = {{ umurmur_admin_password | to_json }}; # Set to enable admin functionality.
|
||||||
|
{% endif %}
|
||||||
|
# ban_length = 0; # Length in seconds for a ban. Default is 0. 0 = forever.
|
||||||
|
# enable_ban = false; # Default is false
|
||||||
|
# banfile = "banfile.txt"; # File to save bans to. Default is to not save bans to file.
|
||||||
|
# sync_banfile = false; # Keep banfile synced. Default is false, which means it is saved to at shutdown only.
|
||||||
|
allow_textmessage = true; # Default is true
|
||||||
|
# opus_threshold = 100; # Percentage of users supporting Opus codec for it to be chosen. Default is 100.
|
||||||
|
# show_addresses = true; # Whether to show client's IP addresses under user information
|
||||||
|
max_users = {{ umurmur_max_users }};
|
||||||
|
|
||||||
|
bindport = {{ umurmur_port }};
|
||||||
|
# bindaddr = "0.0.0.0";
|
||||||
|
|
||||||
|
# username and groupname for privilege dropping.
|
||||||
|
# Will attempt to switch user if set.
|
||||||
|
# username = "";
|
||||||
|
# If groupname not set the user's default login group will be used
|
||||||
|
# groupname = "";
|
||||||
|
|
||||||
|
# Log to file option. Default is logging to syslog.
|
||||||
|
# umurmurd will close and reopen the logfile if SIGHUP is received.
|
||||||
|
logfile = "/var/log/umurmurd.log";
|
||||||
|
|
||||||
|
# CA location for CA-signed certificates
|
||||||
|
# ca_path = "/path/to/ca/certificates/";
|
||||||
|
|
||||||
|
# Channel tree definition:
|
||||||
|
# Root channel must always be defined first.
|
||||||
|
# If a channel has a parent, the parent must be defined before the child channel(s).
|
||||||
|
channels = (
|
||||||
|
{% for channel in umurmur_channels %}
|
||||||
|
{
|
||||||
|
name = {{ channel.name | to_json }};
|
||||||
|
description = {{ channel.description | default('') | to_json }};
|
||||||
|
parent = {{ channel.parent | default('') | to_json }};
|
||||||
|
noenter = {{ channel.noenter | default(false) | string | lower }};
|
||||||
|
silent = {{ channel.silent | default(false) | string | lower }};
|
||||||
|
{% if channel.position is defined %}
|
||||||
|
position = {{ channel.position }};
|
||||||
|
{% endif %}
|
||||||
|
{% if channel.password is defined %}
|
||||||
|
password = {{ channel.password | to_json }};
|
||||||
|
{% endif %}
|
||||||
|
}{{ loop.last | ternary("", ",") }}
|
||||||
|
{% endfor %}
|
||||||
|
);
|
||||||
|
# Channel links configuration.
|
||||||
|
channel_links = (
|
||||||
|
{% for channel in umurmur_channel_links %}
|
||||||
|
{% for destination in channel.destinations %}
|
||||||
|
{
|
||||||
|
source = {{ channel.source | to_json }};
|
||||||
|
destination = {{ destination | to_json }};
|
||||||
|
}{{ loop.last | ternary('', ',') }}
|
||||||
|
{% endfor %}
|
||||||
|
{% endfor %}
|
||||||
|
);
|
||||||
|
|
||||||
|
# The channel in which users will appear in when connecting.
|
||||||
|
# Note that default channel can't have 'noenter = true' or password set
|
||||||
|
default_channel = {{ umurmur_default_channel | to_json }};
|
|
@ -0,0 +1,28 @@
|
||||||
|
[Unit]
|
||||||
|
Description=Minimalistic Mumble server
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=nobody
|
||||||
|
Group={{ umurmur_ssl_group }}
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=3
|
||||||
|
PIDFile=/run/umurmurd.pid
|
||||||
|
ExecStartPre=/usr/local/bin/umurmurd -t -c /usr/local/etc/umurmur.conf
|
||||||
|
ExecStart=/usr/local/bin/umurmurd -d -r -c /usr/local/etc/umurmur.conf
|
||||||
|
ExecReload=/bin/kill -HUP $MAINPID
|
||||||
|
PrivateDevices=yes
|
||||||
|
PrivateTmp=yes
|
||||||
|
ProtectSystem=strict
|
||||||
|
ReadWriteDirectories=/usr/local/etc/
|
||||||
|
ProtectHome=yes
|
||||||
|
ProtectControlGroups=yes
|
||||||
|
ProtectKernelModules=yes
|
||||||
|
ProtectKernelTunables=yes
|
||||||
|
LockPersonality=yes
|
||||||
|
NoNewPrivileges=yes
|
||||||
|
LimitRTPRIO=1
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
|
@ -0,0 +1,38 @@
|
||||||
|
Role Name
|
||||||
|
=========
|
||||||
|
|
||||||
|
A brief description of the role goes here.
|
||||||
|
|
||||||
|
Requirements
|
||||||
|
------------
|
||||||
|
|
||||||
|
Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required.
|
||||||
|
|
||||||
|
Role Variables
|
||||||
|
--------------
|
||||||
|
|
||||||
|
A description of the settable variables for this role should go here, including any variables that are in defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well.
|
||||||
|
|
||||||
|
Dependencies
|
||||||
|
------------
|
||||||
|
|
||||||
|
A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles.
|
||||||
|
|
||||||
|
Example Playbook
|
||||||
|
----------------
|
||||||
|
|
||||||
|
Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too:
|
||||||
|
|
||||||
|
- hosts: servers
|
||||||
|
roles:
|
||||||
|
- { role: username.rolename, x: 42 }
|
||||||
|
|
||||||
|
License
|
||||||
|
-------
|
||||||
|
|
||||||
|
BSD
|
||||||
|
|
||||||
|
Author Information
|
||||||
|
------------------
|
||||||
|
|
||||||
|
An optional section for the role authors to include contact information, or a website (HTML is not allowed).
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
# defaults file for roles/nginx
|
||||||
|
nginx_user: www-data
|
||||||
|
nginx_htpasswd_user: admin
|
||||||
|
nginx_template_file: nginx.conf.j2
|
|
@ -0,0 +1,28 @@
|
||||||
|
- name: Create .htpasswd file
|
||||||
|
htpasswd:
|
||||||
|
path: "/etc/nginx/{{ location.basic_auth.file }}"
|
||||||
|
name: "{{ location.basic_auth.user | default('admin') }}"
|
||||||
|
password: "{{ location.basic_auth.password }}"
|
||||||
|
state: present
|
||||||
|
create: yes
|
||||||
|
owner: "{{ nginx_user }}"
|
||||||
|
group: "{{ nginx_user }}"
|
||||||
|
mode: 0600
|
||||||
|
loop: "{{ nginx_server.locations }}"
|
||||||
|
loop_control:
|
||||||
|
loop_var: location
|
||||||
|
label: "{{ location.path }}"
|
||||||
|
when: "location.basic_auth is defined"
|
||||||
|
|
||||||
|
notify:
|
||||||
|
- reload nginx
|
||||||
|
|
||||||
|
- name: Copy Nginx config
|
||||||
|
template:
|
||||||
|
src: "{{ nginx_template_file }}"
|
||||||
|
dest: "{{ nginx_config_dir }}/{{ nginx_config_file }}"
|
||||||
|
owner: "{{ nginx_user }}"
|
||||||
|
group: "{{ nginx_user }}"
|
||||||
|
mode: 0644
|
||||||
|
notify:
|
||||||
|
- reload nginx
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
- name: Configure Nginx
|
||||||
|
import_tasks: configure.yml
|
||||||
|
tags: nginx
|
|
@ -0,0 +1,38 @@
|
||||||
|
{{ ansible_managed | comment }}
|
||||||
|
|
||||||
|
{% if nginx_server.name is defined %}
|
||||||
|
server {
|
||||||
|
listen {{ nginx_server.port }};
|
||||||
|
|
||||||
|
server_name {{ nginx_server.name }};
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% for location in nginx_server.locations | default([]) %}
|
||||||
|
location {{ location.path }} {
|
||||||
|
{% if location.proxy_pass is defined %}
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header Host $http_host;
|
||||||
|
proxy_redirect off;
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if location.basic_auth.file is defined %}
|
||||||
|
auth_basic "Authentication required";
|
||||||
|
auth_basic_user_file /etc/nginx/{{ location.basic_auth.file }};
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if location.proxy_pass is defined %}
|
||||||
|
proxy_pass http://localhost:{{ location.proxy_pass.port | default('80') }}{{ location.proxy_pass.path }};
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if location.return is defined %}
|
||||||
|
return {{ location.return.code }} {{ location.return.url }};
|
||||||
|
{% endif %}
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
|
{% if nginx_server.name is defined %}
|
||||||
|
{% for include_path in nginx_server.includes | default([]) %}
|
||||||
|
include {{ include_path }};
|
||||||
|
{% endfor %}
|
||||||
|
}
|
||||||
|
{% endif %}
|
|
@ -0,0 +1,2 @@
|
||||||
|
---
|
||||||
|
# vars file for roles/nginx
|
Loading…
Reference in New Issue