initial upload

This commit is contained in:
2025-10-10 11:07:34 +00:00
commit 6224cd01c6
161 changed files with 8964 additions and 0 deletions

40
README.md Normal file
View File

@@ -0,0 +1,40 @@
# Ansible files and playbooks
This is Maruntiel's Ansible repository
INFRASTRUCTURE SETUP
====================
Examples:
# Ping all hosts to verify connectivity:
ansible all -m ping
# Show all facts about some hosts:
ansible mysql -m setup
# Run a command on all asterisk servers:
ansible asterisk -m shell -a "uname"
# Install/upgrade a package on MySQL servers:
ansible mysql -m apt -a "name=innotop state=latest"
# Provision the whole infrastructure:
ansible-playbook site.yml [--diff] [--tags=<tags>]
# Provision the whole infrastructure in dry run mode and see what would change:
ansible-playbook site.yml --check --diff
# Update the hosts file on all servers:
ansible-playbook tools/update_hosts.yml
Files:
ansible.cfg Ansible config file
inventory Hosts inventory file defining all hosts and groups
site.yml Main playbook: provision all hosts and services
playbook/*.yml Playbooks for provisioning services (included by site.yml)
tools/*.yml Playbooks for operations
others

19
ansible.cfg Normal file
View File

@@ -0,0 +1,19 @@
[defaults]
inventory = ./inventory
roles_path = roles
timeout = 10
private_key_file = ~/.ssh/id_rsa
interpreter_python = auto_silent
ansible_managed = ANSIBLE deployed. DO NOT EDIT!!!
[inventory]
enable_plugins = host_list, script, auto, yaml, ini, toml
[ssh_connection]
ssh_args = -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey,keyboard-interactive -o ControlMaster=auto -o ControlPersist=60s
[privilege_escalation]
become = True
become_method = sudo
become_user = root
become_ask_pass = True

11
bash-prompt.yml Normal file
View File

@@ -0,0 +1,11 @@
---
- hosts: all
become: true
tasks:
- name: change bash promp and color
copy: src={{ item.src }} dest={{ item.dest }}
with_items:
- {src: 'bashrc', dest: '/root/.bashrc'}
- {src: 'bashrc', dest: '/home/sebastian/.bashrc'}

22
basic-tools.yml Normal file
View File

@@ -0,0 +1,22 @@
---
- hosts: all
become: true
tasks:
- name: update repo index
apt:
update_cache: yes
- name: install usefull and basic system tools
apt:
name:
- vim-nox
- mc
- nmap
- net-tools
- dnsutils
- tmux
- tcpdump
- iptraf-ng
- screen

64
consul.yml Normal file
View File

@@ -0,0 +1,64 @@
---
- hosts: servers
become: true
tasks:
- name: install required UNZIP
package:
name: unzip
- name: add the CONSUL group
group:
name: consul
state: present
gid: 199
- name: add the CONSUL user
user:
name: consul
comment: CONSUL user
state: present
uid: 199
- name: install CONSUL from HashiCorp
unarchive:
src: https://releases.hashicorp.com/consul/1.8.5/consul_1.8.5_linux_amd64.zip
dest: /usr/local/bin
remote_src: yes
mode: 0755
owner: consul
group: consul
- name: create CONSUL required data folders
file:
path: /opt/consul
state: directory
mode: '0755'
recurse: yes
owner: consul
group: consul
- name: create CONSUL required config folders
file:
path: /etc/consul.d
state: directory
mode: '0755'
recurse: yes
owner: consul
group: consul
- name: copy CONSUL systemd script
copy: src={{ item.src }} dest={{ item.dest }}
with_items:
- {src: 'consul/configs/consul.service', dest: '/etc/systemd/system'}
- {src: 'consul/configs/consul.hcl', dest: '/etc/consul.d'}
- {src: 'consul/configs/service-ssh.hcl', dest: '/etc/consul.d'}
- name: enable CONSUL systemd script
service:
name: consul
enabled: yes
daemon_reload: yes
state: started

940
facts Normal file
View File

@@ -0,0 +1,940 @@
admin.srv | SUCCESS => {
"ansible_facts": {
"ansible_all_ipv4_addresses": [
"10.11.11.200"
],
"ansible_all_ipv6_addresses": [
"fe80::215:5dff:fe0b:6a02"
],
"ansible_apparmor": {
"status": "enabled"
},
"ansible_architecture": "x86_64",
"ansible_bios_date": "11/01/2019",
"ansible_bios_version": "Hyper-V UEFI Release v4.0",
"ansible_cmdline": {
"BOOT_IMAGE": "/vmlinuz-5.4.0-53-generic",
"ro": true,
"root": "/dev/mapper/ubuntu--vg-ubuntu--lv"
},
"ansible_date_time": {
"date": "2020-11-26",
"day": "26",
"epoch": "1606413037",
"hour": "17",
"iso8601": "2020-11-26T17:50:37Z",
"iso8601_basic": "20201126T175037815822",
"iso8601_basic_short": "20201126T175037",
"iso8601_micro": "2020-11-26T17:50:37.815922Z",
"minute": "50",
"month": "11",
"second": "37",
"time": "17:50:37",
"tz": "UTC",
"tz_offset": "+0000",
"weekday": "Thursday",
"weekday_number": "4",
"weeknumber": "47",
"year": "2020"
},
"ansible_default_ipv4": {
"address": "10.11.11.200",
"alias": "eth0",
"broadcast": "10.11.11.255",
"gateway": "10.11.11.1",
"interface": "eth0",
"macaddress": "00:15:5d:0b:6a:02",
"mtu": 1500,
"netmask": "255.255.255.0",
"network": "10.11.11.0",
"type": "ether"
},
"ansible_default_ipv6": {},
"ansible_device_links": {
"ids": {
"dm-0": [
"dm-name-ubuntu--vg-ubuntu--lv",
"dm-uuid-LVM-TWUHOGOKoNuLMn4gNb51IdtDrSue1Rvw1Gv9YnBSffsXBbWX84dmduc9M2oMYmsB"
],
"sda": [
"scsi-14d53465420202020fb6c2ab55f82e74f8c8f7a8b61e2d533",
"scsi-360022480fb6c2ab55f827a8b61e2d533",
"wwn-0x60022480fb6c2ab55f827a8b61e2d533"
],
"sda1": [
"scsi-14d53465420202020fb6c2ab55f82e74f8c8f7a8b61e2d533-part1",
"scsi-360022480fb6c2ab55f827a8b61e2d533-part1",
"wwn-0x60022480fb6c2ab55f827a8b61e2d533-part1"
],
"sda2": [
"scsi-14d53465420202020fb6c2ab55f82e74f8c8f7a8b61e2d533-part2",
"scsi-360022480fb6c2ab55f827a8b61e2d533-part2",
"wwn-0x60022480fb6c2ab55f827a8b61e2d533-part2"
],
"sda3": [
"lvm-pv-uuid-yDm3er-tLzM-3fJR-VE3j-mCEz-0QJv-FzswgU",
"scsi-14d53465420202020fb6c2ab55f82e74f8c8f7a8b61e2d533-part3",
"scsi-360022480fb6c2ab55f827a8b61e2d533-part3",
"wwn-0x60022480fb6c2ab55f827a8b61e2d533-part3"
],
"sr0": [
"scsi-14d534654202020207305e3437703544694957d7ced624a7d"
]
},
"labels": {},
"masters": {
"sda3": [
"dm-0"
]
},
"uuids": {
"dm-0": [
"78d8f127-d14d-4a2b-89da-4cc16b1c4c31"
],
"sda1": [
"C2A5-06BF"
],
"sda2": [
"ac252c97-f517-4be8-b499-9fcc8f8d5c68"
]
}
},
"ansible_devices": {
"dm-0": {
"holders": [],
"host": "",
"links": {
"ids": [
"dm-name-ubuntu--vg-ubuntu--lv",
"dm-uuid-LVM-TWUHOGOKoNuLMn4gNb51IdtDrSue1Rvw1Gv9YnBSffsXBbWX84dmduc9M2oMYmsB"
],
"labels": [],
"masters": [],
"uuids": [
"78d8f127-d14d-4a2b-89da-4cc16b1c4c31"
]
},
"model": null,
"partitions": {},
"removable": "0",
"rotational": "1",
"sas_address": null,
"sas_device_handle": null,
"scheduler_mode": "",
"sectors": "38789120",
"sectorsize": "512",
"size": "18.50 GB",
"support_discard": "2097152",
"vendor": null,
"virtual": 1
},
"loop0": {
"holders": [],
"host": "",
"links": {
"ids": [],
"labels": [],
"masters": [],
"uuids": []
},
"model": null,
"partitions": {},
"removable": "0",
"rotational": "1",
"sas_address": null,
"sas_device_handle": null,
"scheduler_mode": "mq-deadline",
"sectors": "112552",
"sectorsize": "512",
"size": "54.96 MB",
"support_discard": "4096",
"vendor": null,
"virtual": 1
},
"loop1": {
"holders": [],
"host": "",
"links": {
"ids": [],
"labels": [],
"masters": [],
"uuids": []
},
"model": null,
"partitions": {},
"removable": "0",
"rotational": "1",
"sas_address": null,
"sas_device_handle": null,
"scheduler_mode": "mq-deadline",
"sectors": "113384",
"sectorsize": "512",
"size": "55.36 MB",
"support_discard": "4096",
"vendor": null,
"virtual": 1
},
"loop2": {
"holders": [],
"host": "",
"links": {
"ids": [],
"labels": [],
"masters": [],
"uuids": []
},
"model": null,
"partitions": {},
"removable": "0",
"rotational": "1",
"sas_address": null,
"sas_device_handle": null,
"scheduler_mode": "mq-deadline",
"sectors": "145968",
"sectorsize": "512",
"size": "71.27 MB",
"support_discard": "4096",
"vendor": null,
"virtual": 1
},
"loop3": {
"holders": [],
"host": "",
"links": {
"ids": [],
"labels": [],
"masters": [],
"uuids": []
},
"model": null,
"partitions": {},
"removable": "0",
"rotational": "1",
"sas_address": null,
"sas_device_handle": null,
"scheduler_mode": "mq-deadline",
"sectors": "61200",
"sectorsize": "512",
"size": "29.88 MB",
"support_discard": "4096",
"vendor": null,
"virtual": 1
},
"loop4": {
"holders": [],
"host": "",
"links": {
"ids": [],
"labels": [],
"masters": [],
"uuids": []
},
"model": null,
"partitions": {},
"removable": "0",
"rotational": "1",
"sas_address": null,
"sas_device_handle": null,
"scheduler_mode": "mq-deadline",
"sectors": "138752",
"sectorsize": "512",
"size": "67.75 MB",
"support_discard": "4096",
"vendor": null,
"virtual": 1
},
"loop5": {
"holders": [],
"host": "",
"links": {
"ids": [],
"labels": [],
"masters": [],
"uuids": []
},
"model": null,
"partitions": {},
"removable": "0",
"rotational": "1",
"sas_address": null,
"sas_device_handle": null,
"scheduler_mode": "mq-deadline",
"sectors": "63360",
"sectorsize": "512",
"size": "30.94 MB",
"support_discard": "4096",
"vendor": null,
"virtual": 1
},
"loop6": {
"holders": [],
"host": "",
"links": {
"ids": [],
"labels": [],
"masters": [],
"uuids": []
},
"model": null,
"partitions": {},
"removable": "0",
"rotational": "1",
"sas_address": null,
"sas_device_handle": null,
"scheduler_mode": "mq-deadline",
"sectors": "0",
"sectorsize": "512",
"size": "0.00 Bytes",
"support_discard": "4096",
"vendor": null,
"virtual": 1
},
"loop7": {
"holders": [],
"host": "",
"links": {
"ids": [],
"labels": [],
"masters": [],
"uuids": []
},
"model": null,
"partitions": {},
"removable": "0",
"rotational": "1",
"sas_address": null,
"sas_device_handle": null,
"scheduler_mode": "mq-deadline",
"sectors": "0",
"sectorsize": "512",
"size": "0.00 Bytes",
"support_discard": "0",
"vendor": null,
"virtual": 1
},
"sda": {
"holders": [],
"host": "",
"links": {
"ids": [
"scsi-14d53465420202020fb6c2ab55f82e74f8c8f7a8b61e2d533",
"scsi-360022480fb6c2ab55f827a8b61e2d533",
"wwn-0x60022480fb6c2ab55f827a8b61e2d533"
],
"labels": [],
"masters": [],
"uuids": []
},
"model": "Virtual Disk",
"partitions": {
"sda1": {
"holders": [],
"links": {
"ids": [
"scsi-14d53465420202020fb6c2ab55f82e74f8c8f7a8b61e2d533-part1",
"scsi-360022480fb6c2ab55f827a8b61e2d533-part1",
"wwn-0x60022480fb6c2ab55f827a8b61e2d533-part1"
],
"labels": [],
"masters": [],
"uuids": [
"C2A5-06BF"
]
},
"sectors": "1048576",
"sectorsize": 512,
"size": "512.00 MB",
"start": "2048",
"uuid": "C2A5-06BF"
},
"sda2": {
"holders": [],
"links": {
"ids": [
"scsi-14d53465420202020fb6c2ab55f82e74f8c8f7a8b61e2d533-part2",
"scsi-360022480fb6c2ab55f827a8b61e2d533-part2",
"wwn-0x60022480fb6c2ab55f827a8b61e2d533-part2"
],
"labels": [],
"masters": [],
"uuids": [
"ac252c97-f517-4be8-b499-9fcc8f8d5c68"
]
},
"sectors": "2097152",
"sectorsize": 512,
"size": "1.00 GB",
"start": "1050624",
"uuid": "ac252c97-f517-4be8-b499-9fcc8f8d5c68"
},
"sda3": {
"holders": [
"ubuntu--vg-ubuntu--lv"
],
"links": {
"ids": [
"lvm-pv-uuid-yDm3er-tLzM-3fJR-VE3j-mCEz-0QJv-FzswgU",
"scsi-14d53465420202020fb6c2ab55f82e74f8c8f7a8b61e2d533-part3",
"scsi-360022480fb6c2ab55f827a8b61e2d533-part3",
"wwn-0x60022480fb6c2ab55f827a8b61e2d533-part3"
],
"labels": [],
"masters": [
"dm-0"
],
"uuids": []
},
"sectors": "38793216",
"sectorsize": 512,
"size": "18.50 GB",
"start": "3147776",
"uuid": null
}
},
"removable": "0",
"rotational": "1",
"sas_address": null,
"sas_device_handle": null,
"scheduler_mode": "none",
"sectors": "41943040",
"sectorsize": "512",
"size": "20.00 GB",
"support_discard": "2097152",
"vendor": "Msft",
"virtual": 1,
"wwn": "0x60022480fb6c2ab55f827a8b61e2d533"
},
"sr0": {
"holders": [],
"host": "",
"links": {
"ids": [
"scsi-14d534654202020207305e3437703544694957d7ced624a7d"
],
"labels": [],
"masters": [],
"uuids": []
},
"model": "Virtual DVD-ROM",
"partitions": {},
"removable": "1",
"rotational": "1",
"sas_address": null,
"sas_device_handle": null,
"scheduler_mode": "none",
"sectors": "2097151",
"sectorsize": "512",
"size": "1024.00 MB",
"support_discard": "0",
"vendor": "Msft",
"virtual": 1
}
},
"ansible_distribution": "Ubuntu",
"ansible_distribution_file_parsed": true,
"ansible_distribution_file_path": "/etc/os-release",
"ansible_distribution_file_variety": "Debian",
"ansible_distribution_major_version": "20",
"ansible_distribution_release": "focal",
"ansible_distribution_version": "20.04",
"ansible_dns": {
"nameservers": [
"127.0.0.53"
],
"options": {
"edns0": true,
"trust-ad": true
},
"search": [
"maruntiel.com"
]
},
"ansible_domain": "srv",
"ansible_effective_group_id": 0,
"ansible_effective_user_id": 0,
"ansible_env": {
"HOME": "/root",
"LANG": "C.UTF-8",
"LOGNAME": "root",
"MAIL": "/var/mail/root",
"PATH": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin",
"PWD": "/home/sebastian",
"SHELL": "/bin/bash",
"SUDO_COMMAND": "/bin/sh -c echo BECOME-SUCCESS-vprmhxvksdowhqkfdpqvsmyyibyglvwk ; /usr/bin/python3 /home/sebastian/.ansible/tmp/ansible-tmp-1606413037.0662265-70241438907100/AnsiballZ_setup.py",
"SUDO_GID": "1000",
"SUDO_UID": "1000",
"SUDO_USER": "sebastian",
"TERM": "xterm",
"USER": "root"
},
"ansible_eth0": {
"active": true,
"device": "eth0",
"features": {
"esp_hw_offload": "off [fixed]",
"esp_tx_csum_hw_offload": "off [fixed]",
"fcoe_mtu": "off [fixed]",
"generic_receive_offload": "on",
"generic_segmentation_offload": "on",
"highdma": "on [fixed]",
"hw_tc_offload": "off [fixed]",
"l2_fwd_offload": "off [fixed]",
"large_receive_offload": "on",
"loopback": "off [fixed]",
"netns_local": "off [fixed]",
"ntuple_filters": "off [fixed]",
"receive_hashing": "off [fixed]",
"rx_all": "off [fixed]",
"rx_checksumming": "on",
"rx_fcs": "off [fixed]",
"rx_gro_hw": "off [fixed]",
"rx_udp_tunnel_port_offload": "off [fixed]",
"rx_vlan_filter": "off [fixed]",
"rx_vlan_offload": "on [fixed]",
"rx_vlan_stag_filter": "off [fixed]",
"rx_vlan_stag_hw_parse": "off [fixed]",
"scatter_gather": "on",
"tcp_segmentation_offload": "on",
"tls_hw_record": "off [fixed]",
"tls_hw_rx_offload": "off [fixed]",
"tls_hw_tx_offload": "off [fixed]",
"tx_checksum_fcoe_crc": "off [fixed]",
"tx_checksum_ip_generic": "off [fixed]",
"tx_checksum_ipv4": "on",
"tx_checksum_ipv6": "on",
"tx_checksum_sctp": "off [fixed]",
"tx_checksumming": "on",
"tx_esp_segmentation": "off [fixed]",
"tx_fcoe_segmentation": "off [fixed]",
"tx_gre_csum_segmentation": "off [fixed]",
"tx_gre_segmentation": "off [fixed]",
"tx_gso_partial": "off [fixed]",
"tx_gso_robust": "off [fixed]",
"tx_ipxip4_segmentation": "off [fixed]",
"tx_ipxip6_segmentation": "off [fixed]",
"tx_lockless": "off [fixed]",
"tx_nocache_copy": "off",
"tx_scatter_gather": "on",
"tx_scatter_gather_fraglist": "off [fixed]",
"tx_sctp_segmentation": "off [fixed]",
"tx_tcp6_segmentation": "on",
"tx_tcp_ecn_segmentation": "off [fixed]",
"tx_tcp_mangleid_segmentation": "off",
"tx_tcp_segmentation": "on",
"tx_udp_segmentation": "off [fixed]",
"tx_udp_tnl_csum_segmentation": "off [fixed]",
"tx_udp_tnl_segmentation": "off [fixed]",
"tx_vlan_offload": "on [fixed]",
"tx_vlan_stag_hw_insert": "off [fixed]",
"vlan_challenged": "off [fixed]"
},
"hw_timestamp_filters": [],
"ipv4": {
"address": "10.11.11.200",
"broadcast": "10.11.11.255",
"netmask": "255.255.255.0",
"network": "10.11.11.0"
},
"ipv6": [
{
"address": "fe80::215:5dff:fe0b:6a02",
"prefix": "64",
"scope": "link"
}
],
"macaddress": "00:15:5d:0b:6a:02",
"module": "hv_netvsc",
"mtu": 1500,
"pciid": "afef4346-a050-4719-accd-7555c8675429",
"promisc": false,
"speed": 195,
"timestamping": [
"tx_software",
"rx_software",
"software"
],
"type": "ether"
},
"ansible_fibre_channel_wwn": [],
"ansible_fips": false,
"ansible_form_factor": "Desktop",
"ansible_fqdn": "admin.srv",
"ansible_hostname": "admin",
"ansible_hostnqn": "",
"ansible_interfaces": [
"lo",
"eth0"
],
"ansible_is_chroot": false,
"ansible_iscsi_iqn": "iqn.1993-08.org.debian:01:af5bf2af245",
"ansible_kernel": "5.4.0-53-generic",
"ansible_kernel_version": "#59-Ubuntu SMP Wed Oct 21 09:38:44 UTC 2020",
"ansible_lo": {
"active": true,
"device": "lo",
"features": {
"esp_hw_offload": "off [fixed]",
"esp_tx_csum_hw_offload": "off [fixed]",
"fcoe_mtu": "off [fixed]",
"generic_receive_offload": "on",
"generic_segmentation_offload": "on",
"highdma": "on [fixed]",
"hw_tc_offload": "off [fixed]",
"l2_fwd_offload": "off [fixed]",
"large_receive_offload": "off [fixed]",
"loopback": "on [fixed]",
"netns_local": "on [fixed]",
"ntuple_filters": "off [fixed]",
"receive_hashing": "off [fixed]",
"rx_all": "off [fixed]",
"rx_checksumming": "on [fixed]",
"rx_fcs": "off [fixed]",
"rx_gro_hw": "off [fixed]",
"rx_udp_tunnel_port_offload": "off [fixed]",
"rx_vlan_filter": "off [fixed]",
"rx_vlan_offload": "off [fixed]",
"rx_vlan_stag_filter": "off [fixed]",
"rx_vlan_stag_hw_parse": "off [fixed]",
"scatter_gather": "on",
"tcp_segmentation_offload": "on",
"tls_hw_record": "off [fixed]",
"tls_hw_rx_offload": "off [fixed]",
"tls_hw_tx_offload": "off [fixed]",
"tx_checksum_fcoe_crc": "off [fixed]",
"tx_checksum_ip_generic": "on [fixed]",
"tx_checksum_ipv4": "off [fixed]",
"tx_checksum_ipv6": "off [fixed]",
"tx_checksum_sctp": "on [fixed]",
"tx_checksumming": "on",
"tx_esp_segmentation": "off [fixed]",
"tx_fcoe_segmentation": "off [fixed]",
"tx_gre_csum_segmentation": "off [fixed]",
"tx_gre_segmentation": "off [fixed]",
"tx_gso_partial": "off [fixed]",
"tx_gso_robust": "off [fixed]",
"tx_ipxip4_segmentation": "off [fixed]",
"tx_ipxip6_segmentation": "off [fixed]",
"tx_lockless": "on [fixed]",
"tx_nocache_copy": "off [fixed]",
"tx_scatter_gather": "on [fixed]",
"tx_scatter_gather_fraglist": "on [fixed]",
"tx_sctp_segmentation": "on",
"tx_tcp6_segmentation": "on",
"tx_tcp_ecn_segmentation": "on",
"tx_tcp_mangleid_segmentation": "on",
"tx_tcp_segmentation": "on",
"tx_udp_segmentation": "off [fixed]",
"tx_udp_tnl_csum_segmentation": "off [fixed]",
"tx_udp_tnl_segmentation": "off [fixed]",
"tx_vlan_offload": "off [fixed]",
"tx_vlan_stag_hw_insert": "off [fixed]",
"vlan_challenged": "on [fixed]"
},
"hw_timestamp_filters": [],
"ipv4": {
"address": "127.0.0.1",
"broadcast": "host",
"netmask": "255.0.0.0",
"network": "127.0.0.0"
},
"ipv6": [
{
"address": "::1",
"prefix": "128",
"scope": "host"
}
],
"mtu": 65536,
"promisc": false,
"timestamping": [
"tx_software",
"rx_software",
"software"
],
"type": "loopback"
},
"ansible_local": {},
"ansible_lsb": {
"codename": "focal",
"description": "Ubuntu 20.04.1 LTS",
"id": "Ubuntu",
"major_release": "20",
"release": "20.04"
},
"ansible_lvm": {
"lvs": {
"ubuntu-lv": {
"size_g": "18.50",
"vg": "ubuntu-vg"
}
},
"pvs": {
"/dev/sda3": {
"free_g": "0",
"size_g": "18.50",
"vg": "ubuntu-vg"
}
},
"vgs": {
"ubuntu-vg": {
"free_g": "0",
"num_lvs": "1",
"num_pvs": "1",
"size_g": "18.50"
}
}
},
"ansible_machine": "x86_64",
"ansible_machine_id": "dd0100d596a7407c9f7b39315324d71f",
"ansible_memfree_mb": 296,
"ansible_memory_mb": {
"nocache": {
"free": 1234,
"used": 2701
},
"real": {
"free": 296,
"total": 3935,
"used": 3639
},
"swap": {
"cached": 0,
"free": 3934,
"total": 3934,
"used": 0
}
},
"ansible_memtotal_mb": 3935,
"ansible_mounts": [
{
"block_available": 2287820,
"block_size": 4096,
"block_total": 4739756,
"block_used": 2451936,
"device": "/dev/mapper/ubuntu--vg-ubuntu--lv",
"fstype": "ext4",
"inode_available": 1103535,
"inode_total": 1212416,
"inode_used": 108881,
"mount": "/",
"options": "rw,relatime",
"size_available": 9370910720,
"size_total": 19414040576,
"uuid": "78d8f127-d14d-4a2b-89da-4cc16b1c4c31"
},
{
"block_available": 205917,
"block_size": 4096,
"block_total": 249830,
"block_used": 43913,
"device": "/dev/sda2",
"fstype": "ext4",
"inode_available": 65232,
"inode_total": 65536,
"inode_used": 304,
"mount": "/boot",
"options": "rw,relatime",
"size_available": 843436032,
"size_total": 1023303680,
"uuid": "ac252c97-f517-4be8-b499-9fcc8f8d5c68"
},
{
"block_available": 128816,
"block_size": 4096,
"block_total": 130812,
"block_used": 1996,
"device": "/dev/sda1",
"fstype": "vfat",
"inode_available": 0,
"inode_total": 0,
"inode_used": 0,
"mount": "/boot/efi",
"options": "rw,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=iso8859-1,shortname=mixed,errors=remount-ro",
"size_available": 527630336,
"size_total": 535805952,
"uuid": "C2A5-06BF"
},
{
"block_available": 0,
"block_size": 131072,
"block_total": 443,
"block_used": 443,
"device": "/dev/loop1",
"fstype": "squashfs",
"inode_available": 0,
"inode_total": 10779,
"inode_used": 10779,
"mount": "/snap/core18/1932",
"options": "ro,nodev,relatime",
"size_available": 0,
"size_total": 58064896,
"uuid": "N/A"
},
{
"block_available": 0,
"block_size": 131072,
"block_total": 440,
"block_used": 440,
"device": "/dev/loop0",
"fstype": "squashfs",
"inode_available": 0,
"inode_total": 10756,
"inode_used": 10756,
"mount": "/snap/core18/1880",
"options": "ro,nodev,relatime",
"size_available": 0,
"size_total": 57671680,
"uuid": "N/A"
},
{
"block_available": 0,
"block_size": 131072,
"block_total": 571,
"block_used": 571,
"device": "/dev/loop2",
"fstype": "squashfs",
"inode_available": 0,
"inode_total": 1495,
"inode_used": 1495,
"mount": "/snap/lxd/16099",
"options": "ro,nodev,relatime",
"size_available": 0,
"size_total": 74842112,
"uuid": "N/A"
},
{
"block_available": 0,
"block_size": 131072,
"block_total": 240,
"block_used": 240,
"device": "/dev/loop3",
"fstype": "squashfs",
"inode_available": 0,
"inode_total": 463,
"inode_used": 463,
"mount": "/snap/snapd/8542",
"options": "ro,nodev,relatime",
"size_available": 0,
"size_total": 31457280,
"uuid": "N/A"
},
{
"block_available": 0,
"block_size": 131072,
"block_total": 542,
"block_used": 542,
"device": "/dev/loop4",
"fstype": "squashfs",
"inode_available": 0,
"inode_total": 1551,
"inode_used": 1551,
"mount": "/snap/lxd/18150",
"options": "ro,nodev,relatime",
"size_available": 0,
"size_total": 71041024,
"uuid": "N/A"
},
{
"block_available": 0,
"block_size": 131072,
"block_total": 248,
"block_used": 248,
"device": "/dev/loop5",
"fstype": "squashfs",
"inode_available": 0,
"inode_total": 472,
"inode_used": 472,
"mount": "/snap/snapd/9721",
"options": "ro,nodev,relatime",
"size_available": 0,
"size_total": 32505856,
"uuid": "N/A"
}
],
"ansible_nodename": "admin",
"ansible_os_family": "Debian",
"ansible_pkg_mgr": "apt",
"ansible_proc_cmdline": {
"BOOT_IMAGE": "/vmlinuz-5.4.0-53-generic",
"ro": true,
"root": "/dev/mapper/ubuntu--vg-ubuntu--lv"
},
"ansible_processor": [
"0",
"GenuineIntel",
"Intel(R) Xeon(R) CPU X5670 @ 2.93GHz",
"1",
"GenuineIntel",
"Intel(R) Xeon(R) CPU X5670 @ 2.93GHz",
"2",
"GenuineIntel",
"Intel(R) Xeon(R) CPU X5670 @ 2.93GHz",
"3",
"GenuineIntel",
"Intel(R) Xeon(R) CPU X5670 @ 2.93GHz"
],
"ansible_processor_cores": 2,
"ansible_processor_count": 1,
"ansible_processor_threads_per_core": 2,
"ansible_processor_vcpus": 4,
"ansible_product_name": "Virtual Machine",
"ansible_product_serial": "4364-0105-9520-7945-2132-8495-89",
"ansible_product_uuid": "b9545019-0c6e-4533-b8e4-ef00df451640",
"ansible_product_version": "Hyper-V UEFI Release v4.0",
"ansible_python": {
"executable": "/usr/bin/python3",
"has_sslcontext": true,
"type": "cpython",
"version": {
"major": 3,
"micro": 5,
"minor": 8,
"releaselevel": "final",
"serial": 0
},
"version_info": [
3,
8,
5,
"final",
0
]
},
"ansible_python_version": "3.8.5",
"ansible_real_group_id": 0,
"ansible_real_user_id": 0,
"ansible_selinux": {
"status": "disabled"
},
"ansible_selinux_python_present": true,
"ansible_service_mgr": "systemd",
"ansible_ssh_host_key_dsa_public": "AAAAB3NzaC1kc3MAAACBANYatGk5k7IcCJqX9dHtia70J09844SQPS8f0dPJuNJeKwBIiRHlUTHPLcFNbAcs/GCZjbQnbOWKn65H2XKZg4wWMNsrt6+iSGo2Am7F6ez+VQghIz7bQ49H7/f+mYOpsmFa6V764ZIVQJFn32rZc0zAuePUyNOw5kRKiJA0LDEzAAAAFQC9W1l577X6vlo5VtqBtc15vjPrPQAAAIBM3GuywQOtT8C86A6j55/QrY/9p8VyGJi2q5zDH70hOateJ9rYGYNMO5u1yV9cQ+Tmt4Dx9gFaDSYdrl/hPJioKLfL24vRcig3dBZ/CX2DBRKn3QB5tWZxwR6LhrMlG+ySzRyI9QOUEctoHeNwfvxhat8DGKJrYN3RvDYD/B2DCAAAAIEAonGmX0umHV1Af9E0y2Wfb8pzxshvD/36Kiw9Ra3Ice+9iDbPnliceZ6IOGbPEa51NOBvlEwz6tYmqCvkAySw1Evp8WYeovawjBNoIX1oVFcZ7Mjp5fN+2dxZpXzrNzwmgzstj1XIm3PvCggNjkjrGjp1SL3Q8hPZNjjRNOnp358=",
"ansible_ssh_host_key_ecdsa_public": "AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJrI8yWXqcscSUbtPzPsvwvLo381hSKnZwQe8K4nevf/7jFENYniewhC0vweZfw1wIkKsChvodSD/mErmlbRDlo=",
"ansible_ssh_host_key_ed25519_public": "AAAAC3NzaC1lZDI1NTE5AAAAICSpL5B07ScCMKtsBZ2WW3ZE45kUoy+1Zmq0ye74Cwyl",
"ansible_ssh_host_key_rsa_public": "AAAAB3NzaC1yc2EAAAADAQABAAABgQCxc/4SV8IE4wEG8aPExj7U0la2dV2bQYhKZAWtI1swYDACOLyVRUitY3vRahzBO5VX6x/S95lS1ibD4nmPEcXzjNp62za8jOE4q8SP3C6xqgokQRBjr8oWxsIpO+0EgXaqLBZ7lzovbTtKoyUvlctb9g2qEbKOzQoEyVQoZyWksrXQ4Rn21W1Y9J4+BD5XBLc9XGIDcxuTg44/tl21e5P0sj6NX1QnQT1lJPnqPl98QPaXOy9xp7/d0JTbW0fVuf4iuen6oK9z2UaH2qbnxkBMg/HnJD5Er0c2bCsJmPBqobp3rlajbvnU9Wc4jCIHVqMGfzf6ayaXzSw33uKgIpd9vh2RJFsTXkgdmk3X7jFZZppprrsukexOx4ldKO1FO9XklzuS85UFyxQtofW7wKavkAipq4TMpHRl1jvHwH/I3+FqIbfaCD4jqqmcsdcLq1+9UkgiOEBQOUQ8BYat6bnsvEX3Izv0MM07mfzhbsWhfDntjLSfy6vWVAaWqV8gMZs=",
"ansible_swapfree_mb": 3934,
"ansible_swaptotal_mb": 3934,
"ansible_system": "Linux",
"ansible_system_capabilities": [],
"ansible_system_capabilities_enforced": "False",
"ansible_system_vendor": "Microsoft Corporation",
"ansible_uptime_seconds": 250721,
"ansible_user_dir": "/root",
"ansible_user_gecos": "root",
"ansible_user_gid": 0,
"ansible_user_id": "root",
"ansible_user_shell": "/bin/bash",
"ansible_user_uid": 0,
"ansible_userspace_architecture": "x86_64",
"ansible_userspace_bits": "64",
"ansible_virtualization_role": "guest",
"ansible_virtualization_type": "VirtualPC",
"gather_subset": [
"all"
],
"module_setup": true
},
"changed": false,
"deprecations": [],
"warnings": []
}

102
files/bashrc Normal file
View File

@@ -0,0 +1,102 @@
# ~/.bashrc: executed by bash(1) for non-login shells.
# see /usr/share/doc/bash/examples/startup-files (in the package bash-doc)
# for examples
# If not running interactively, don't do anything
[ -z "$PS1" ] && return
# don't put duplicate lines in the history. See bash(1) for more options
# ... or force ignoredups and ignorespace
HISTCONTROL=ignoredups:ignorespace
# append to the history file, don't overwrite it
shopt -s histappend
# for setting history length see HISTSIZE and HISTFILESIZE in bash(1)
HISTSIZE=1000
HISTFILESIZE=2000
# check the window size after each command and, if necessary,
# update the values of LINES and COLUMNS.
shopt -s checkwinsize
# make less more friendly for non-text input files, see lesspipe(1)
[ -x /usr/bin/lesspipe ] && eval "$(SHELL=/bin/sh lesspipe)"
# set variable identifying the chroot you work in (used in the prompt below)
if [ -z "$debian_chroot" ] && [ -r /etc/debian_chroot ]; then
debian_chroot=$(cat /etc/debian_chroot)
fi
# set a fancy prompt (non-color, unless we know we "want" color)
case "$TERM" in
xterm-color) color_prompt=yes;;
esac
# uncomment for a colored prompt, if the terminal has the capability; turned
# off by default to not distract the user: the focus in a terminal window
# should be on the output of commands, not on the prompt
#force_color_prompt=yes
if [ -n "$force_color_prompt" ]; then
if [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then
# We have color support; assume it's compliant with Ecma-48
# (ISO/IEC-6429). (Lack of such support is extremely rare, and such
# a case would tend to support setf rather than setaf.)
color_prompt=yes
else
color_prompt=
fi
fi
if [ "$color_prompt" = yes ]; then
PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
else
PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ '
fi
unset color_prompt force_color_prompt
# If this is an xterm set the title to user@host:dir
case "$TERM" in
xterm*|rxvt*)
PS1="\[\e]0;${debian_chroot:+($debian_chroot)}\u@\h: \w\a\]$PS1"
;;
*)
;;
esac
# enable color support of ls and also add handy aliases
if [ -x /usr/bin/dircolors ]; then
test -r ~/.dircolors && eval "$(dircolors -b ~/.dircolors)" || eval "$(dircolors -b)"
alias ls='ls --color=auto'
#alias dir='dir --color=auto'
#alias vdir='vdir --color=auto'
alias grep='grep --color=auto'
alias fgrep='fgrep --color=auto'
alias egrep='egrep --color=auto'
fi
# some more ls aliases
alias ll='ls -alF'
alias la='ls -A'
alias l='ls -CF'
# Alias definitions.
# You may want to put all your additions into a separate file like
# ~/.bash_aliases, instead of adding them here directly.
# See /usr/share/doc/bash-doc/examples in the bash-doc package.
if [ -f ~/.bash_aliases ]; then
. ~/.bash_aliases
fi
# enable programmable completion features (you don't need to enable
# this, if it's already enabled in /etc/bash.bashrc and /etc/profile
# sources /etc/bash.bashrc).
#if [ -f /etc/bash_completion ] && ! shopt -oq posix; then
# . /etc/bash_completion
#fi
alias enable_ipv6='sed -i "/net.ipv6.conf.all.disable_ipv6.*/d" /etc/sysctl.conf && sysctl -q -p && echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6 && sed -i "s/#//" /etc/netplan/01-netcfg.yaml && netplan generate && netplan apply'
export PS1="[\[$(tput sgr0)\]\[\033[38;5;10m\]\t\[$(tput sgr0)\] \[$(tput sgr0)\]\[\033[38;5;9m\]\u\[$(tput sgr0)\]@\[$(tput sgr0)\]\[\033[38;5;9m\]\h\[$(tput sgr0)\]:\[$(tput sgr0)\]\[\033[38;5;10m\]\W\[$(tput sgr0)\]]\[$(tput sgr0)\] "

2024
files/certbot-auto Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,7 @@
datacenter = "MSI-DC"
data_dir = "/opt/consul"
encrypt = "eRhnp22+c0bkV0wPolk6Mw=="
retry_join = ["consul-admin"]
performance {
raft_multiplier = 1
}

View File

@@ -0,0 +1,23 @@
[Unit]
Description=Consul Service Discovery Agent
Documentation=https://www.consul.io/
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=consul
Group=consul
ExecStart=/usr/local/bin/consul agent -server -ui \
-data-dir=/opt/consul \
-node=consul-%H \
-config-dir=/etc/consul.d
ExecReload=/bin/kill -HUP $MAINPID
KillSignal=SIGINT
TimeoutStopSec=5
Restart=on-failure
SyslogIdentifier=consul
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,4 @@
server = true
bootstrap_expect = 2
bind_addr = "10.11.10.101"
ui = true

View File

@@ -0,0 +1,10 @@
service {
name = "apache"
port = 443
tags = [ "srv1", "pedimedic", "webmail", "git" ]
check {
http = "https://srv1.maruntiel.com"
interval = "5s"
tlsSkipVerify = true
}
}

View File

@@ -0,0 +1,9 @@
service {
name = "mariadb"
port = 3306
tags = [ "db" ]
check {
tcp = "localhost:3306"
interval = "5s"
}
}

View File

@@ -0,0 +1,8 @@
service {
name = "SSHD"
port = 22
check {
tcp = "localhost:22"
interval = "5s"
}
}

70
files/consul/consul-tag Normal file
View File

@@ -0,0 +1,70 @@
#!/usr/bin/python3
import os
import sys
import requests
CONSUL_API = 'http://localhost:8500'
def get_service(sess, service_id):
r = sess.get(CONSUL_API + '/v1/agent/services', timeout=2)
r.raise_for_status()
services = r.json()
for svc in services.values():
if svc['ID'] == service_id:
return svc
return None
def change_service_tags(service, tags_to_add, tags_to_remove):
with requests.Session() as sess:
sess.headers = {'X-Consul-Token': os.getenv('CONSUL_HTTP_TOKEN')}
svc = get_service(sess, service)
if svc:
new_tags = (set(svc.get('Tags', [])) | tags_to_add) - tags_to_remove
new_svc = {
'ID': svc['ID'],
'Name': svc['Service'],
'Address': svc.get('Address', ''),
'Port': svc.get('Port', 0),
'Meta': svc.get('Meta', {}),
'Tags': sorted(list(new_tags)),
'EnableTagOverride': svc.get('EnableTagOverride', False),
}
for k, v in new_svc.items():
print('{} = {}'.format(k, v))
r = sess.put(CONSUL_API + '/v1/agent/service/register', json=new_svc, timeout=2)
r.raise_for_status()
def main(argv):
if len(argv) < 3:
print("Usage: consul-tag service +tag -tag...")
return 1
service = argv[1]
tags_to_add = set()
tags_to_remove = set()
for tag in argv[2:]:
if tag.startswith('-'):
tags_to_remove.add(tag[1:])
elif tag.startswith('+'):
tags_to_add.add(tag[1:])
else:
tags_to_add.add(tag)
try:
change_service_tags(service, tags_to_add, tags_to_remove)
except Exception as exc:
print("Error: {}".format(exc))
return 2
return 0
if __name__ == '__main__':
sys.exit(main(sys.argv))

BIN
files/consul/consul.1.7.4 Normal file

Binary file not shown.

BIN
files/gitea-1.12.5 Normal file

Binary file not shown.

BIN
files/unison-2.48.3 Normal file

Binary file not shown.

916
files/unison-fsmonitor Normal file
View File

@@ -0,0 +1,916 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<link rel="dns-prefetch" href="https://github.githubassets.com">
<link rel="dns-prefetch" href="https://avatars0.githubusercontent.com">
<link rel="dns-prefetch" href="https://avatars1.githubusercontent.com">
<link rel="dns-prefetch" href="https://avatars2.githubusercontent.com">
<link rel="dns-prefetch" href="https://avatars3.githubusercontent.com">
<link rel="dns-prefetch" href="https://github-cloud.s3.amazonaws.com">
<link rel="dns-prefetch" href="https://user-images.githubusercontent.com/">
<link crossorigin="anonymous" media="all" integrity="sha512-xnQIMZDOHZTyEPkXHdiwqBPPUAyzDzAU5iDJa6OfzDqwhJdI+0IyBajpzgDAKoegEWUXs4Ze9+/jGhP/OMD98w==" rel="stylesheet" href="https://github.githubassets.com/assets/frameworks-c674083190ce1d94f210f9171dd8b0a8.css" />
<link crossorigin="anonymous" media="all" integrity="sha512-IX5BoM4/CZcF+rw+yhDLhCjHTA1gz+F8rA3dQU3p7CFWAx1qGb+MVztQXTLG7jyLCn2kDGNZkp3pHjbL9Ac4kw==" rel="stylesheet" href="https://github.githubassets.com/assets/site-217e41a0ce3f099705fabc3eca10cb84.css" />
<link crossorigin="anonymous" media="all" integrity="sha512-NC0d+qVeTgvgc3XpqnmqrmnpQnjdeOmUq0XiH+8aCQipkK2dTy+BLVTNHCRJtcZxZCDUd7gBO8ccGLa7nVTYTw==" rel="stylesheet" href="https://github.githubassets.com/assets/github-342d1dfaa55e4e0be07375e9aa79aaae.css" />
<meta name="viewport" content="width=device-width">
<title>Syndicator/unison-fsmonitor at master · TentativeConvert/Syndicator · GitHub</title>
<meta name="description" content="Dropbox inspired Unity indicator for Unison. Contribute to TentativeConvert/Syndicator development by creating an account on GitHub.">
<link rel="search" type="application/opensearchdescription+xml" href="/opensearch.xml" title="GitHub">
<link rel="fluid-icon" href="https://github.com/fluidicon.png" title="GitHub">
<meta property="fb:app_id" content="1401488693436528">
<meta name="twitter:image:src" content="https://avatars0.githubusercontent.com/u/16051841?s=400&amp;v=4" /><meta name="twitter:site" content="@github" /><meta name="twitter:card" content="summary" /><meta name="twitter:title" content="TentativeConvert/Syndicator" /><meta name="twitter:description" content="Dropbox inspired Unity indicator for Unison. Contribute to TentativeConvert/Syndicator development by creating an account on GitHub." />
<meta property="og:image" content="https://avatars0.githubusercontent.com/u/16051841?s=400&amp;v=4" /><meta property="og:site_name" content="GitHub" /><meta property="og:type" content="object" /><meta property="og:title" content="TentativeConvert/Syndicator" /><meta property="og:url" content="https://github.com/TentativeConvert/Syndicator" /><meta property="og:description" content="Dropbox inspired Unity indicator for Unison. Contribute to TentativeConvert/Syndicator development by creating an account on GitHub." />
<link rel="assets" href="https://github.githubassets.com/">
<meta name="request-id" content="8218:3132:2D45E9B:418D651:5EE7BA48" data-pjax-transient="true"/><meta name="html-safe-nonce" content="43c24f6039d9265c3d6ee773a5c9ad1aba822d05" data-pjax-transient="true"/><meta name="visitor-payload" content="eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiI4MjE4OjMxMzI6MkQ0NUU5Qjo0MThENjUxOjVFRTdCQTQ4IiwidmlzaXRvcl9pZCI6IjI4NDA2Nzg5MzI5NDEzNTU1OTIiLCJyZWdpb25fZWRnZSI6ImlhZCIsInJlZ2lvbl9yZW5kZXIiOiJpYWQifQ==" data-pjax-transient="true"/><meta name="visitor-hmac" content="d7f08ea9f85f04144ea6597464f44ecf0e62ac41e90f78f64f60f6f3167165e1" data-pjax-transient="true"/>
<meta name="github-keyboard-shortcuts" content="repository,source-code" data-pjax-transient="true" />
<meta name="selected-link" value="repo_source" data-pjax-transient>
<meta name="google-site-verification" content="c1kuD-K2HIVF635lypcsWPoD4kilo5-jA_wBFyT4uMY">
<meta name="google-site-verification" content="KT5gs8h0wvaagLKAVWq8bbeNwnZZK1r1XQysX3xurLU">
<meta name="google-site-verification" content="ZzhVyEFwb7w3e0-uOTltm8Jsck2F5StVihD0exw2fsA">
<meta name="google-site-verification" content="GXs5KoUUkNCoaAZn7wPN-t01Pywp9M3sEjnt_3_ZWPc">
<meta name="octolytics-host" content="collector.githubapp.com" /><meta name="octolytics-app-id" content="github" /><meta name="octolytics-event-url" content="https://collector.githubapp.com/github-external/browser_event" /><meta name="octolytics-dimension-ga_id" content="" class="js-octo-ga-id" />
<meta name="analytics-location" content="/&lt;user-name&gt;/&lt;repo-name&gt;/blob/show" data-pjax-transient="true" />
<meta name="optimizely-sdk-key" content="cowimJNste4j7QnBNCjaw" />
<meta name="google-analytics" content="UA-3769691-2">
<meta class="js-ga-set" name="dimension10" content="Responsive">
<meta class="js-ga-set" name="dimension1" content="Logged Out">
<meta name="hostname" content="github.com">
<meta name="user-login" content="">
<meta name="expected-hostname" content="github.com">
<meta name="enabled-features" content="MARKETPLACE_PENDING_INSTALLATIONS">
<meta http-equiv="x-pjax-version" content="7224aed3c08edc3ad5d7d232605a565c">
<link href="https://github.com/TentativeConvert/Syndicator/commits/master.atom" rel="alternate" title="Recent Commits to Syndicator:master" type="application/atom+xml">
<meta name="go-import" content="github.com/TentativeConvert/Syndicator git https://github.com/TentativeConvert/Syndicator.git">
<meta name="octolytics-dimension-user_id" content="16051841" /><meta name="octolytics-dimension-user_login" content="TentativeConvert" /><meta name="octolytics-dimension-repository_id" content="56348458" /><meta name="octolytics-dimension-repository_nwo" content="TentativeConvert/Syndicator" /><meta name="octolytics-dimension-repository_public" content="true" /><meta name="octolytics-dimension-repository_is_fork" content="false" /><meta name="octolytics-dimension-repository_network_root_id" content="56348458" /><meta name="octolytics-dimension-repository_network_root_nwo" content="TentativeConvert/Syndicator" /><meta name="octolytics-dimension-repository_explore_github_marketplace_ci_cta_shown" content="false" />
<link rel="canonical" href="https://github.com/TentativeConvert/Syndicator/blob/master/unison-binaries/unison-fsmonitor" data-pjax-transient>
<meta name="browser-stats-url" content="https://api.github.com/_private/browser/stats">
<meta name="browser-errors-url" content="https://api.github.com/_private/browser/errors">
<link rel="mask-icon" href="https://github.githubassets.com/pinned-octocat.svg" color="#000000">
<link rel="alternate icon" class="js-site-favicon" type="image/png" href="https://github.githubassets.com/favicons/favicon.png">
<link rel="icon" class="js-site-favicon" type="image/svg+xml" href="https://github.githubassets.com/favicons/favicon.svg">
<meta name="theme-color" content="#1e2327">
<link rel="manifest" href="/manifest.json" crossOrigin="use-credentials">
</head>
<body class="logged-out env-production page-responsive page-blob">
<div class="position-relative js-header-wrapper ">
<a href="#start-of-content" class="px-2 py-4 bg-blue text-white show-on-focus js-skip-to-content">Skip to content</a>
<span class="Progress progress-pjax-loader position-fixed width-full js-pjax-loader-bar">
<span class="progress-pjax-loader-bar top-0 left-0" style="width: 0%;"></span>
</span>
<header class="Header-old header-logged-out js-details-container Details position-relative f4 py-2" role="banner">
<div class="container-lg d-lg-flex flex-items-center p-responsive">
<div class="d-flex flex-justify-between flex-items-center">
<a class="mr-4" href="https://github.com/" aria-label="Homepage" data-ga-click="(Logged out) Header, go to homepage, icon:logo-wordmark">
<svg height="32" class="octicon octicon-mark-github text-white" viewBox="0 0 16 16" version="1.1" width="32" aria-hidden="true"><path fill-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"></path></svg>
</a>
<div class="d-lg-none css-truncate css-truncate-target width-fit p-2">
<svg class="octicon octicon-repo" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M2 2.5A2.5 2.5 0 014.5 0h8.75a.75.75 0 01.75.75v12.5a.75.75 0 01-.75.75h-2.5a.75.75 0 110-1.5h1.75v-2h-8a1 1 0 00-.714 1.7.75.75 0 01-1.072 1.05A2.495 2.495 0 012 11.5v-9zm10.5-1V9h-8c-.356 0-.694.074-1 .208V2.5a1 1 0 011-1h8zM5 12.25v3.25a.25.25 0 00.4.2l1.45-1.087a.25.25 0 01.3 0L8.6 15.7a.25.25 0 00.4-.2v-3.25a.25.25 0 00-.25-.25h-3.5a.25.25 0 00-.25.25z"></path></svg>
<a class="Header-link" href="/TentativeConvert">TentativeConvert</a>
/
<a class="Header-link" href="/TentativeConvert/Syndicator">Syndicator</a>
</div>
<div class="d-flex flex-items-center">
<a href="/join?ref_cta=Sign+up&amp;ref_loc=header+logged+out&amp;ref_page=%2F%3Cuser-name%3E%2F%3Crepo-name%3E%2Fblob%2Fshow&amp;source=header-repo"
class="d-inline-block d-lg-none f5 text-white no-underline border border-gray-dark rounded-2 px-2 py-1 mr-3 mr-sm-5"
data-hydro-click="{&quot;event_type&quot;:&quot;authentication.click&quot;,&quot;payload&quot;:{&quot;location_in_page&quot;:&quot;site header&quot;,&quot;repository_id&quot;:null,&quot;auth_type&quot;:&quot;SIGN_UP&quot;,&quot;originating_url&quot;:&quot;https://github.com/TentativeConvert/Syndicator/blob/master/unison-binaries/unison-fsmonitor&quot;,&quot;user_id&quot;:null}}" data-hydro-click-hmac="680ac3c8b9a32623f9b0b3795e598488956d471b2369af2700f1f8a257cc95b6"
data-ga-click="Sign up, click to sign up for account, ref_page:/&lt;user-name&gt;/&lt;repo-name&gt;/blob/show;ref_cta:Sign up;ref_loc:header logged out">
Sign&nbsp;up
</a>
<button class="btn-link d-lg-none mt-1 js-details-target" type="button" aria-label="Toggle navigation" aria-expanded="false">
<svg height="24" class="octicon octicon-three-bars text-white" viewBox="0 0 16 16" version="1.1" width="24" aria-hidden="true"><path fill-rule="evenodd" d="M1 2.75A.75.75 0 011.75 2h12.5a.75.75 0 110 1.5H1.75A.75.75 0 011 2.75zm0 5A.75.75 0 011.75 7h12.5a.75.75 0 110 1.5H1.75A.75.75 0 011 7.75zM1.75 12a.75.75 0 100 1.5h12.5a.75.75 0 100-1.5H1.75z"></path></svg>
</button>
</div>
</div>
<div class="HeaderMenu HeaderMenu--logged-out position-fixed top-0 right-0 bottom-0 height-fit position-lg-relative d-lg-flex flex-justify-between flex-items-center flex-auto">
<div class="d-flex d-lg-none flex-justify-end border-bottom bg-gray-light p-3">
<button class="btn-link js-details-target" type="button" aria-label="Toggle navigation" aria-expanded="false">
<svg height="24" class="octicon octicon-x text-gray" viewBox="0 0 24 24" version="1.1" width="24" aria-hidden="true"><path fill-rule="evenodd" d="M5.72 5.72a.75.75 0 011.06 0L12 10.94l5.22-5.22a.75.75 0 111.06 1.06L13.06 12l5.22 5.22a.75.75 0 11-1.06 1.06L12 13.06l-5.22 5.22a.75.75 0 01-1.06-1.06L10.94 12 5.72 6.78a.75.75 0 010-1.06z"></path></svg>
</button>
</div>
<nav class="mt-0 px-3 px-lg-0 mb-5 mb-lg-0" aria-label="Global">
<ul class="d-lg-flex list-style-none">
<li class="d-block d-lg-flex flex-lg-nowrap flex-lg-items-center border-bottom border-lg-bottom-0 mr-0 mr-lg-3 edge-item-fix position-relative flex-wrap flex-justify-between d-flex flex-items-center ">
<details class="HeaderMenu-details details-overlay details-reset width-full">
<summary class="HeaderMenu-summary HeaderMenu-link px-0 py-3 border-0 no-wrap d-block d-lg-inline-block">
Why GitHub?
<svg x="0px" y="0px" viewBox="0 0 14 8" xml:space="preserve" fill="none" class="icon-chevon-down-mktg position-absolute position-lg-relative">
<path d="M1,1l6.2,6L13,1"></path>
</svg>
</summary>
<div class="dropdown-menu flex-auto rounded-1 bg-white px-0 mt-0 pb-4 p-lg-4 position-relative position-lg-absolute left-0 left-lg-n4">
<a href="/features" class="py-2 lh-condensed-ultra d-block link-gray-dark no-underline h5 Bump-link--hover" data-ga-click="(Logged out) Header, go to Features">Features <span class="Bump-link-symbol float-right text-normal text-gray-light">&rarr;</span></a>
<ul class="list-style-none f5 pb-3">
<li class="edge-item-fix"><a href="/features/code-review/" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Code review">Code review</a></li>
<li class="edge-item-fix"><a href="/features/project-management/" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Project management">Project management</a></li>
<li class="edge-item-fix"><a href="/features/integrations" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Integrations">Integrations</a></li>
<li class="edge-item-fix"><a href="/features/actions" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Actions">Actions</a></li>
<li class="edge-item-fix"><a href="/features/packages" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to GitHub Packages">Packages</a></li>
<li class="edge-item-fix"><a href="/features/security" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Security">Security</a></li>
<li class="edge-item-fix"><a href="/features#team-management" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Team management">Team management</a></li>
<li class="edge-item-fix"><a href="/features#hosting" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Code hosting">Hosting</a></li>
</ul>
<ul class="list-style-none mb-0 border-lg-top pt-lg-3">
<li class="edge-item-fix"><a href="/customer-stories" class="py-2 lh-condensed-ultra d-block no-underline link-gray-dark no-underline h5 Bump-link--hover" data-ga-click="(Logged out) Header, go to Customer stories">Customer stories <span class="Bump-link-symbol float-right text-normal text-gray-light">&rarr;</span></a></li>
<li class="edge-item-fix"><a href="/security" class="py-2 lh-condensed-ultra d-block no-underline link-gray-dark no-underline h5 Bump-link--hover" data-ga-click="(Logged out) Header, go to Security">Security <span class="Bump-link-symbol float-right text-normal text-gray-light">&rarr;</span></a></li>
</ul>
</div>
</details>
</li>
<li class="border-bottom border-lg-bottom-0 mr-0 mr-lg-3">
<a href="/team" class="HeaderMenu-link no-underline py-3 d-block d-lg-inline-block" data-ga-click="(Logged out) Header, go to Team">Team</a>
</li>
<li class="border-bottom border-lg-bottom-0 mr-0 mr-lg-3">
<a href="/enterprise" class="HeaderMenu-link no-underline py-3 d-block d-lg-inline-block" data-ga-click="(Logged out) Header, go to Enterprise">Enterprise</a>
</li>
<li class="d-block d-lg-flex flex-lg-nowrap flex-lg-items-center border-bottom border-lg-bottom-0 mr-0 mr-lg-3 edge-item-fix position-relative flex-wrap flex-justify-between d-flex flex-items-center ">
<details class="HeaderMenu-details details-overlay details-reset width-full">
<summary class="HeaderMenu-summary HeaderMenu-link px-0 py-3 border-0 no-wrap d-block d-lg-inline-block">
Explore
<svg x="0px" y="0px" viewBox="0 0 14 8" xml:space="preserve" fill="none" class="icon-chevon-down-mktg position-absolute position-lg-relative">
<path d="M1,1l6.2,6L13,1"></path>
</svg>
</summary>
<div class="dropdown-menu flex-auto rounded-1 bg-white px-0 pt-2 pb-0 mt-0 pb-4 p-lg-4 position-relative position-lg-absolute left-0 left-lg-n4">
<ul class="list-style-none mb-3">
<li class="edge-item-fix"><a href="/explore" class="py-2 lh-condensed-ultra d-block link-gray-dark no-underline h5 Bump-link--hover" data-ga-click="(Logged out) Header, go to Explore">Explore GitHub <span class="Bump-link-symbol float-right text-normal text-gray-light">&rarr;</span></a></li>
</ul>
<h4 class="text-gray-light text-normal text-mono f5 mb-2 border-lg-top pt-lg-3">Learn &amp; contribute</h4>
<ul class="list-style-none mb-3">
<li class="edge-item-fix"><a href="/topics" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Topics">Topics</a></li>
<li class="edge-item-fix"><a href="/collections" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Collections">Collections</a></li>
<li class="edge-item-fix"><a href="/trending" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Trending">Trending</a></li>
<li class="edge-item-fix"><a href="https://lab.github.com/" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Learning lab">Learning Lab</a></li>
<li class="edge-item-fix"><a href="https://opensource.guide" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Open source guides">Open source guides</a></li>
</ul>
<h4 class="text-gray-light text-normal text-mono f5 mb-2 border-lg-top pt-lg-3">Connect with others</h4>
<ul class="list-style-none mb-0">
<li class="edge-item-fix"><a href="https://github.com/events" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Events">Events</a></li>
<li class="edge-item-fix"><a href="https://github.community" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Community forum">Community forum</a></li>
<li class="edge-item-fix"><a href="https://education.github.com" class="py-2 pb-0 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to GitHub Education">GitHub Education</a></li>
</ul>
</div>
</details>
</li>
<li class="border-bottom border-lg-bottom-0 mr-0 mr-lg-3">
<a href="/marketplace" class="HeaderMenu-link no-underline py-3 d-block d-lg-inline-block" data-ga-click="(Logged out) Header, go to Marketplace">Marketplace</a>
</li>
<li class="d-block d-lg-flex flex-lg-nowrap flex-lg-items-center border-bottom border-lg-bottom-0 mr-0 mr-lg-3 edge-item-fix position-relative flex-wrap flex-justify-between d-flex flex-items-center ">
<details class="HeaderMenu-details details-overlay details-reset width-full">
<summary class="HeaderMenu-summary HeaderMenu-link px-0 py-3 border-0 no-wrap d-block d-lg-inline-block">
Pricing
<svg x="0px" y="0px" viewBox="0 0 14 8" xml:space="preserve" fill="none" class="icon-chevon-down-mktg position-absolute position-lg-relative">
<path d="M1,1l6.2,6L13,1"></path>
</svg>
</summary>
<div class="dropdown-menu flex-auto rounded-1 bg-white px-0 pt-2 pb-4 mt-0 p-lg-4 position-relative position-lg-absolute left-0 left-lg-n4">
<a href="/pricing" class="pb-2 lh-condensed-ultra d-block link-gray-dark no-underline h5 Bump-link--hover" data-ga-click="(Logged out) Header, go to Pricing">Plans <span class="Bump-link-symbol float-right text-normal text-gray-light">&rarr;</span></a>
<ul class="list-style-none mb-3">
<li class="edge-item-fix"><a href="/pricing#feature-comparison" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Compare plans">Compare plans</a></li>
<li class="edge-item-fix"><a href="https://enterprise.github.com/contact" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Contact Sales">Contact Sales</a></li>
</ul>
<ul class="list-style-none mb-0 border-lg-top pt-lg-3">
<li class="edge-item-fix"><a href="/nonprofit" class="py-2 lh-condensed-ultra d-block no-underline link-gray-dark no-underline h5 Bump-link--hover" data-ga-click="(Logged out) Header, go to Nonprofits">Nonprofit <span class="Bump-link-symbol float-right text-normal text-gray-light">&rarr;</span></a></li>
<li class="edge-item-fix"><a href="https://education.github.com" class="py-2 pb-0 lh-condensed-ultra d-block no-underline link-gray-dark no-underline h5 Bump-link--hover" data-ga-click="(Logged out) Header, go to Education">Education <span class="Bump-link-symbol float-right text-normal text-gray-light">&rarr;</span></a></li>
</ul>
</div>
</details>
</li>
</ul>
</nav>
<div class="d-lg-flex flex-items-center px-3 px-lg-0 text-center text-lg-left">
<div class="d-lg-flex mb-3 mb-lg-0">
<div class="header-search header-search-current js-header-search-current flex-self-stretch flex-lg-self-auto mr-0 mr-lg-3 mb-3 mb-lg-0 scoped-search site-scoped-search js-site-search position-relative js-jump-to js-header-search-current-jump-to"
role="combobox"
aria-owns="jump-to-results"
aria-label="Search or jump to"
aria-haspopup="listbox"
aria-expanded="false"
>
<div class="position-relative">
<!-- '"` --><!-- </textarea></xmp> --></option></form><form class="js-site-search-form" role="search" aria-label="Site" data-scope-type="Repository" data-scope-id="56348458" data-scoped-search-url="/TentativeConvert/Syndicator/search" data-unscoped-search-url="/search" action="/TentativeConvert/Syndicator/search" accept-charset="UTF-8" method="get">
<label class="form-control input-sm header-search-wrapper p-0 header-search-wrapper-jump-to position-relative d-flex flex-justify-between flex-items-center js-chromeless-input-container">
<input type="text"
class="form-control input-sm header-search-input jump-to-field js-jump-to-field js-site-search-focus js-site-search-field is-clearable"
data-hotkey="s,/"
name="q"
value=""
placeholder="Search"
data-unscoped-placeholder="Search GitHub"
data-scoped-placeholder="Search"
autocapitalize="off"
aria-autocomplete="list"
aria-controls="jump-to-results"
aria-label="Search"
data-jump-to-suggestions-path="/_graphql/GetSuggestedNavigationDestinations"
spellcheck="false"
autocomplete="off"
>
<input type="hidden" data-csrf="true" class="js-data-jump-to-suggestions-path-csrf" value="iGKYl3li7XdMAQCjToVo8AeZHw2MIFXfYIqWc8vfVBfDkgVKqpo2W4S62AN2DUwRj6RvHwzShava4ZzasCFsog==" />
<input type="hidden" class="js-site-search-type-field" name="type" >
<img src="https://github.githubassets.com/images/search-key-slash.svg" alt="" class="mr-2 header-search-key-slash">
<div class="Box position-absolute overflow-hidden d-none jump-to-suggestions js-jump-to-suggestions-container">
<ul class="d-none js-jump-to-suggestions-template-container">
<li class="d-flex flex-justify-start flex-items-center p-0 f5 navigation-item js-navigation-item js-jump-to-suggestion" role="option">
<a tabindex="-1" class="no-underline d-flex flex-auto flex-items-center jump-to-suggestions-path js-jump-to-suggestion-path js-navigation-open p-2" href="">
<div class="jump-to-octicon js-jump-to-octicon flex-shrink-0 mr-2 text-center d-none">
<svg height="16" width="16" class="octicon octicon-repo flex-shrink-0 js-jump-to-octicon-repo d-none" title="Repository" aria-label="Repository" viewBox="0 0 16 16" version="1.1" role="img"><path fill-rule="evenodd" d="M2 2.5A2.5 2.5 0 014.5 0h8.75a.75.75 0 01.75.75v12.5a.75.75 0 01-.75.75h-2.5a.75.75 0 110-1.5h1.75v-2h-8a1 1 0 00-.714 1.7.75.75 0 01-1.072 1.05A2.495 2.495 0 012 11.5v-9zm10.5-1V9h-8c-.356 0-.694.074-1 .208V2.5a1 1 0 011-1h8zM5 12.25v3.25a.25.25 0 00.4.2l1.45-1.087a.25.25 0 01.3 0L8.6 15.7a.25.25 0 00.4-.2v-3.25a.25.25 0 00-.25-.25h-3.5a.25.25 0 00-.25.25z"></path></svg>
<svg height="16" width="16" class="octicon octicon-project flex-shrink-0 js-jump-to-octicon-project d-none" title="Project" aria-label="Project" viewBox="0 0 16 16" version="1.1" role="img"><path fill-rule="evenodd" d="M1.75 0A1.75 1.75 0 000 1.75v12.5C0 15.216.784 16 1.75 16h12.5A1.75 1.75 0 0016 14.25V1.75A1.75 1.75 0 0014.25 0H1.75zM1.5 1.75a.25.25 0 01.25-.25h12.5a.25.25 0 01.25.25v12.5a.25.25 0 01-.25.25H1.75a.25.25 0 01-.25-.25V1.75zM11.75 3a.75.75 0 00-.75.75v7.5a.75.75 0 001.5 0v-7.5a.75.75 0 00-.75-.75zm-8.25.75a.75.75 0 011.5 0v5.5a.75.75 0 01-1.5 0v-5.5zM8 3a.75.75 0 00-.75.75v3.5a.75.75 0 001.5 0v-3.5A.75.75 0 008 3z"></path></svg>
<svg height="16" width="16" class="octicon octicon-search flex-shrink-0 js-jump-to-octicon-search d-none" title="Search" aria-label="Search" viewBox="0 0 16 16" version="1.1" role="img"><path fill-rule="evenodd" d="M11.5 7a4.499 4.499 0 11-8.998 0A4.499 4.499 0 0111.5 7zm-.82 4.74a6 6 0 111.06-1.06l3.04 3.04a.75.75 0 11-1.06 1.06l-3.04-3.04z"></path></svg>
</div>
<img class="avatar mr-2 flex-shrink-0 js-jump-to-suggestion-avatar d-none" alt="" aria-label="Team" src="" width="28" height="28">
<div class="jump-to-suggestion-name js-jump-to-suggestion-name flex-auto overflow-hidden text-left no-wrap css-truncate css-truncate-target">
</div>
<div class="border rounded-1 flex-shrink-0 bg-gray px-1 text-gray-light ml-1 f6 d-none js-jump-to-badge-search">
<span class="js-jump-to-badge-search-text-default d-none" aria-label="in this repository">
In this repository
</span>
<span class="js-jump-to-badge-search-text-global d-none" aria-label="in all of GitHub">
All GitHub
</span>
<span aria-hidden="true" class="d-inline-block ml-1 v-align-middle">↵</span>
</div>
<div aria-hidden="true" class="border rounded-1 flex-shrink-0 bg-gray px-1 text-gray-light ml-1 f6 d-none d-on-nav-focus js-jump-to-badge-jump">
Jump to
<span class="d-inline-block ml-1 v-align-middle">↵</span>
</div>
</a>
</li>
</ul>
<ul class="d-none js-jump-to-no-results-template-container">
<li class="d-flex flex-justify-center flex-items-center f5 d-none js-jump-to-suggestion p-2">
<span class="text-gray">No suggested jump to results</span>
</li>
</ul>
<ul id="jump-to-results" role="listbox" class="p-0 m-0 js-navigation-container jump-to-suggestions-results-container js-jump-to-suggestions-results-container">
<li class="d-flex flex-justify-start flex-items-center p-0 f5 navigation-item js-navigation-item js-jump-to-scoped-search d-none" role="option">
<a tabindex="-1" class="no-underline d-flex flex-auto flex-items-center jump-to-suggestions-path js-jump-to-suggestion-path js-navigation-open p-2" href="">
<div class="jump-to-octicon js-jump-to-octicon flex-shrink-0 mr-2 text-center d-none">
<svg height="16" width="16" class="octicon octicon-repo flex-shrink-0 js-jump-to-octicon-repo d-none" title="Repository" aria-label="Repository" viewBox="0 0 16 16" version="1.1" role="img"><path fill-rule="evenodd" d="M2 2.5A2.5 2.5 0 014.5 0h8.75a.75.75 0 01.75.75v12.5a.75.75 0 01-.75.75h-2.5a.75.75 0 110-1.5h1.75v-2h-8a1 1 0 00-.714 1.7.75.75 0 01-1.072 1.05A2.495 2.495 0 012 11.5v-9zm10.5-1V9h-8c-.356 0-.694.074-1 .208V2.5a1 1 0 011-1h8zM5 12.25v3.25a.25.25 0 00.4.2l1.45-1.087a.25.25 0 01.3 0L8.6 15.7a.25.25 0 00.4-.2v-3.25a.25.25 0 00-.25-.25h-3.5a.25.25 0 00-.25.25z"></path></svg>
<svg height="16" width="16" class="octicon octicon-project flex-shrink-0 js-jump-to-octicon-project d-none" title="Project" aria-label="Project" viewBox="0 0 16 16" version="1.1" role="img"><path fill-rule="evenodd" d="M1.75 0A1.75 1.75 0 000 1.75v12.5C0 15.216.784 16 1.75 16h12.5A1.75 1.75 0 0016 14.25V1.75A1.75 1.75 0 0014.25 0H1.75zM1.5 1.75a.25.25 0 01.25-.25h12.5a.25.25 0 01.25.25v12.5a.25.25 0 01-.25.25H1.75a.25.25 0 01-.25-.25V1.75zM11.75 3a.75.75 0 00-.75.75v7.5a.75.75 0 001.5 0v-7.5a.75.75 0 00-.75-.75zm-8.25.75a.75.75 0 011.5 0v5.5a.75.75 0 01-1.5 0v-5.5zM8 3a.75.75 0 00-.75.75v3.5a.75.75 0 001.5 0v-3.5A.75.75 0 008 3z"></path></svg>
<svg height="16" width="16" class="octicon octicon-search flex-shrink-0 js-jump-to-octicon-search d-none" title="Search" aria-label="Search" viewBox="0 0 16 16" version="1.1" role="img"><path fill-rule="evenodd" d="M11.5 7a4.499 4.499 0 11-8.998 0A4.499 4.499 0 0111.5 7zm-.82 4.74a6 6 0 111.06-1.06l3.04 3.04a.75.75 0 11-1.06 1.06l-3.04-3.04z"></path></svg>
</div>
<img class="avatar mr-2 flex-shrink-0 js-jump-to-suggestion-avatar d-none" alt="" aria-label="Team" src="" width="28" height="28">
<div class="jump-to-suggestion-name js-jump-to-suggestion-name flex-auto overflow-hidden text-left no-wrap css-truncate css-truncate-target">
</div>
<div class="border rounded-1 flex-shrink-0 bg-gray px-1 text-gray-light ml-1 f6 d-none js-jump-to-badge-search">
<span class="js-jump-to-badge-search-text-default d-none" aria-label="in this repository">
In this repository
</span>
<span class="js-jump-to-badge-search-text-global d-none" aria-label="in all of GitHub">
All GitHub
</span>
<span aria-hidden="true" class="d-inline-block ml-1 v-align-middle">↵</span>
</div>
<div aria-hidden="true" class="border rounded-1 flex-shrink-0 bg-gray px-1 text-gray-light ml-1 f6 d-none d-on-nav-focus js-jump-to-badge-jump">
Jump to
<span class="d-inline-block ml-1 v-align-middle">↵</span>
</div>
</a>
</li>
<li class="d-flex flex-justify-start flex-items-center p-0 f5 navigation-item js-navigation-item js-jump-to-global-search d-none" role="option">
<a tabindex="-1" class="no-underline d-flex flex-auto flex-items-center jump-to-suggestions-path js-jump-to-suggestion-path js-navigation-open p-2" href="">
<div class="jump-to-octicon js-jump-to-octicon flex-shrink-0 mr-2 text-center d-none">
<svg height="16" width="16" class="octicon octicon-repo flex-shrink-0 js-jump-to-octicon-repo d-none" title="Repository" aria-label="Repository" viewBox="0 0 16 16" version="1.1" role="img"><path fill-rule="evenodd" d="M2 2.5A2.5 2.5 0 014.5 0h8.75a.75.75 0 01.75.75v12.5a.75.75 0 01-.75.75h-2.5a.75.75 0 110-1.5h1.75v-2h-8a1 1 0 00-.714 1.7.75.75 0 01-1.072 1.05A2.495 2.495 0 012 11.5v-9zm10.5-1V9h-8c-.356 0-.694.074-1 .208V2.5a1 1 0 011-1h8zM5 12.25v3.25a.25.25 0 00.4.2l1.45-1.087a.25.25 0 01.3 0L8.6 15.7a.25.25 0 00.4-.2v-3.25a.25.25 0 00-.25-.25h-3.5a.25.25 0 00-.25.25z"></path></svg>
<svg height="16" width="16" class="octicon octicon-project flex-shrink-0 js-jump-to-octicon-project d-none" title="Project" aria-label="Project" viewBox="0 0 16 16" version="1.1" role="img"><path fill-rule="evenodd" d="M1.75 0A1.75 1.75 0 000 1.75v12.5C0 15.216.784 16 1.75 16h12.5A1.75 1.75 0 0016 14.25V1.75A1.75 1.75 0 0014.25 0H1.75zM1.5 1.75a.25.25 0 01.25-.25h12.5a.25.25 0 01.25.25v12.5a.25.25 0 01-.25.25H1.75a.25.25 0 01-.25-.25V1.75zM11.75 3a.75.75 0 00-.75.75v7.5a.75.75 0 001.5 0v-7.5a.75.75 0 00-.75-.75zm-8.25.75a.75.75 0 011.5 0v5.5a.75.75 0 01-1.5 0v-5.5zM8 3a.75.75 0 00-.75.75v3.5a.75.75 0 001.5 0v-3.5A.75.75 0 008 3z"></path></svg>
<svg height="16" width="16" class="octicon octicon-search flex-shrink-0 js-jump-to-octicon-search d-none" title="Search" aria-label="Search" viewBox="0 0 16 16" version="1.1" role="img"><path fill-rule="evenodd" d="M11.5 7a4.499 4.499 0 11-8.998 0A4.499 4.499 0 0111.5 7zm-.82 4.74a6 6 0 111.06-1.06l3.04 3.04a.75.75 0 11-1.06 1.06l-3.04-3.04z"></path></svg>
</div>
<img class="avatar mr-2 flex-shrink-0 js-jump-to-suggestion-avatar d-none" alt="" aria-label="Team" src="" width="28" height="28">
<div class="jump-to-suggestion-name js-jump-to-suggestion-name flex-auto overflow-hidden text-left no-wrap css-truncate css-truncate-target">
</div>
<div class="border rounded-1 flex-shrink-0 bg-gray px-1 text-gray-light ml-1 f6 d-none js-jump-to-badge-search">
<span class="js-jump-to-badge-search-text-default d-none" aria-label="in this repository">
In this repository
</span>
<span class="js-jump-to-badge-search-text-global d-none" aria-label="in all of GitHub">
All GitHub
</span>
<span aria-hidden="true" class="d-inline-block ml-1 v-align-middle">↵</span>
</div>
<div aria-hidden="true" class="border rounded-1 flex-shrink-0 bg-gray px-1 text-gray-light ml-1 f6 d-none d-on-nav-focus js-jump-to-badge-jump">
Jump to
<span class="d-inline-block ml-1 v-align-middle">↵</span>
</div>
</a>
</li>
</ul>
</div>
</label>
</form> </div>
</div>
</div>
<a href="/login?return_to=%2FTentativeConvert%2FSyndicator%2Fblob%2Fmaster%2Funison-binaries%2Funison-fsmonitor"
class="HeaderMenu-link no-underline mr-3"
data-hydro-click="{&quot;event_type&quot;:&quot;authentication.click&quot;,&quot;payload&quot;:{&quot;location_in_page&quot;:&quot;site header menu&quot;,&quot;repository_id&quot;:null,&quot;auth_type&quot;:&quot;SIGN_UP&quot;,&quot;originating_url&quot;:&quot;https://github.com/TentativeConvert/Syndicator/blob/master/unison-binaries/unison-fsmonitor&quot;,&quot;user_id&quot;:null}}" data-hydro-click-hmac="da6780c89024c2e221fb739fab5bea3a0d09289e7fd5e54d443cfb4c5666e5fe"
data-ga-click="(Logged out) Header, clicked Sign in, text:sign-in">
Sign&nbsp;in
</a>
<a href="/join?ref_cta=Sign+up&amp;ref_loc=header+logged+out&amp;ref_page=%2F%3Cuser-name%3E%2F%3Crepo-name%3E%2Fblob%2Fshow&amp;source=header-repo&amp;source_repo=TentativeConvert%2FSyndicator"
class="HeaderMenu-link d-inline-block no-underline border border-gray-dark rounded-1 px-2 py-1"
data-hydro-click="{&quot;event_type&quot;:&quot;authentication.click&quot;,&quot;payload&quot;:{&quot;location_in_page&quot;:&quot;site header menu&quot;,&quot;repository_id&quot;:null,&quot;auth_type&quot;:&quot;SIGN_UP&quot;,&quot;originating_url&quot;:&quot;https://github.com/TentativeConvert/Syndicator/blob/master/unison-binaries/unison-fsmonitor&quot;,&quot;user_id&quot;:null}}" data-hydro-click-hmac="da6780c89024c2e221fb739fab5bea3a0d09289e7fd5e54d443cfb4c5666e5fe"
data-ga-click="Sign up, click to sign up for account, ref_page:/&lt;user-name&gt;/&lt;repo-name&gt;/blob/show;ref_cta:Sign up;ref_loc:header logged out">
Sign&nbsp;up
</a>
</div>
</div>
</div>
</header>
</div>
<div id="start-of-content" class="show-on-focus"></div>
<div id="js-flash-container">
<template class="js-flash-template">
<div class="flash flash-full js-flash-template-container">
<div class="container-lg px-2" >
<button class="flash-close js-flash-close" type="button" aria-label="Dismiss this message">
<svg class="octicon octicon-x" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M3.72 3.72a.75.75 0 011.06 0L8 6.94l3.22-3.22a.75.75 0 111.06 1.06L9.06 8l3.22 3.22a.75.75 0 11-1.06 1.06L8 9.06l-3.22 3.22a.75.75 0 01-1.06-1.06L6.94 8 3.72 4.78a.75.75 0 010-1.06z"></path></svg>
</button>
<div class="js-flash-template-message"></div>
</div>
</div>
</template>
</div>
<include-fragment class="js-notification-shelf-include-fragment" data-base-src="https://github.com/notifications/beta/shelf"></include-fragment>
<div class="application-main " data-commit-hovercards-enabled>
<div itemscope itemtype="http://schema.org/SoftwareSourceCode" class="">
<main >
<div class="pagehead repohead hx_repohead readability-menu bg-gray-light pb-0 pt-0 pt-lg-3">
<div class="d-flex container-lg mb-4 p-responsive d-none d-lg-flex">
<div class="flex-auto min-width-0 width-fit mr-3">
<h1 class="public d-flex flex-wrap flex-items-center break-word float-none ">
<span class="flex-self-stretch" style="margin-top: -2px;">
<svg class="octicon octicon-repo" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M2 2.5A2.5 2.5 0 014.5 0h8.75a.75.75 0 01.75.75v12.5a.75.75 0 01-.75.75h-2.5a.75.75 0 110-1.5h1.75v-2h-8a1 1 0 00-.714 1.7.75.75 0 01-1.072 1.05A2.495 2.495 0 012 11.5v-9zm10.5-1V9h-8c-.356 0-.694.074-1 .208V2.5a1 1 0 011-1h8zM5 12.25v3.25a.25.25 0 00.4.2l1.45-1.087a.25.25 0 01.3 0L8.6 15.7a.25.25 0 00.4-.2v-3.25a.25.25 0 00-.25-.25h-3.5a.25.25 0 00-.25.25z"></path></svg>
</span>
<span class="author ml-2 flex-self-stretch" itemprop="author">
<a class="url fn" rel="author" data-hovercard-type="user" data-hovercard-url="/users/TentativeConvert/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="/TentativeConvert">TentativeConvert</a>
</span>
<span class="path-divider flex-self-stretch">/</span>
<strong itemprop="name" class="mr-2 flex-self-stretch">
<a data-pjax="#js-repo-pjax-container" href="/TentativeConvert/Syndicator">Syndicator</a>
</strong>
</h1>
</div>
<ul class="pagehead-actions flex-shrink-0 " >
<li>
<a class="tooltipped tooltipped-s btn btn-sm btn-with-count" aria-label="You must be signed in to watch a repository" rel="nofollow" data-hydro-click="{&quot;event_type&quot;:&quot;authentication.click&quot;,&quot;payload&quot;:{&quot;location_in_page&quot;:&quot;notification subscription menu watch&quot;,&quot;repository_id&quot;:null,&quot;auth_type&quot;:&quot;LOG_IN&quot;,&quot;originating_url&quot;:&quot;https://github.com/TentativeConvert/Syndicator/blob/master/unison-binaries/unison-fsmonitor&quot;,&quot;user_id&quot;:null}}" data-hydro-click-hmac="780b355fa92f5a368ca1e8decd0b1e5fdd7e6cbe1413e296084553863f446652" href="/login?return_to=%2FTentativeConvert%2FSyndicator">
<svg class="octicon octicon-eye" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M1.679 7.932c.412-.621 1.242-1.75 2.366-2.717C5.175 4.242 6.527 3.5 8 3.5c1.473 0 2.824.742 3.955 1.715 1.124.967 1.954 2.096 2.366 2.717a.119.119 0 010 .136c-.412.621-1.242 1.75-2.366 2.717C10.825 11.758 9.473 12.5 8 12.5c-1.473 0-2.824-.742-3.955-1.715C2.92 9.818 2.09 8.69 1.679 8.068a.119.119 0 010-.136zM8 2c-1.981 0-3.67.992-4.933 2.078C1.797 5.169.88 6.423.43 7.1a1.619 1.619 0 000 1.798c.45.678 1.367 1.932 2.637 3.024C4.329 13.008 6.019 14 8 14c1.981 0 3.67-.992 4.933-2.078 1.27-1.091 2.187-2.345 2.637-3.023a1.619 1.619 0 000-1.798c-.45-.678-1.367-1.932-2.637-3.023C11.671 2.992 9.981 2 8 2zm0 8a2 2 0 100-4 2 2 0 000 4z"></path></svg>
Watch
</a> <a class="social-count" href="/TentativeConvert/Syndicator/watchers"
aria-label="2 users are watching this repository">
2
</a>
</li>
<li>
<a class="btn btn-sm btn-with-count tooltipped tooltipped-s" aria-label="You must be signed in to star a repository" rel="nofollow" data-hydro-click="{&quot;event_type&quot;:&quot;authentication.click&quot;,&quot;payload&quot;:{&quot;location_in_page&quot;:&quot;star button&quot;,&quot;repository_id&quot;:56348458,&quot;auth_type&quot;:&quot;LOG_IN&quot;,&quot;originating_url&quot;:&quot;https://github.com/TentativeConvert/Syndicator/blob/master/unison-binaries/unison-fsmonitor&quot;,&quot;user_id&quot;:null}}" data-hydro-click-hmac="e37ade38041f1d43580563d5c36599f5edd04c0fe8b09bd28485428997d46847" href="/login?return_to=%2FTentativeConvert%2FSyndicator">
<svg height="16" class="octicon octicon-star v-align-text-bottom" vertical_align="text_bottom" viewBox="0 0 16 16" version="1.1" width="16" aria-hidden="true"><path fill-rule="evenodd" d="M8 .25a.75.75 0 01.673.418l1.882 3.815 4.21.612a.75.75 0 01.416 1.279l-3.046 2.97.719 4.192a.75.75 0 01-1.088.791L8 12.347l-3.766 1.98a.75.75 0 01-1.088-.79l.72-4.194L.818 6.374a.75.75 0 01.416-1.28l4.21-.611L7.327.668A.75.75 0 018 .25zm0 2.445L6.615 5.5a.75.75 0 01-.564.41l-3.097.45 2.24 2.184a.75.75 0 01.216.664l-.528 3.084 2.769-1.456a.75.75 0 01.698 0l2.77 1.456-.53-3.084a.75.75 0 01.216-.664l2.24-2.183-3.096-.45a.75.75 0 01-.564-.41L8 2.694v.001z"></path></svg>
Star
</a>
<a class="social-count js-social-count" href="/TentativeConvert/Syndicator/stargazers"
aria-label="15 users starred this repository">
15
</a>
</li>
<li>
<a class="btn btn-sm btn-with-count tooltipped tooltipped-s" aria-label="You must be signed in to fork a repository" rel="nofollow" data-hydro-click="{&quot;event_type&quot;:&quot;authentication.click&quot;,&quot;payload&quot;:{&quot;location_in_page&quot;:&quot;repo details fork button&quot;,&quot;repository_id&quot;:56348458,&quot;auth_type&quot;:&quot;LOG_IN&quot;,&quot;originating_url&quot;:&quot;https://github.com/TentativeConvert/Syndicator/blob/master/unison-binaries/unison-fsmonitor&quot;,&quot;user_id&quot;:null}}" data-hydro-click-hmac="aa9145d6cb5cc8dcfd52adc72f149a7ed9646dfbd4cc470d01090ddb61685778" href="/login?return_to=%2FTentativeConvert%2FSyndicator">
<svg class="octicon octicon-repo-forked" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M5 3.25a.75.75 0 11-1.5 0 .75.75 0 011.5 0zm0 2.122a2.25 2.25 0 10-1.5 0v.878A2.25 2.25 0 005.75 8.5h1.5v2.128a2.251 2.251 0 101.5 0V8.5h1.5a2.25 2.25 0 002.25-2.25v-.878a2.25 2.25 0 10-1.5 0v.878a.75.75 0 01-.75.75h-4.5A.75.75 0 015 6.25v-.878zm3.75 7.378a.75.75 0 11-1.5 0 .75.75 0 011.5 0zm3-8.75a.75.75 0 100-1.5.75.75 0 000 1.5z"></path></svg>
Fork
</a>
<a href="/TentativeConvert/Syndicator/network/members" class="social-count"
aria-label="4 users forked this repository">
4
</a>
</li>
</ul>
</div>
<nav class="js-repo-nav js-sidenav-container-pjax clearfix hx_reponav reponav p-responsive d-none d-lg-block container-lg"
itemscope
itemtype="http://schema.org/BreadcrumbList"
aria-label="Repository"
data-pjax="#js-repo-pjax-container">
<ul class="list-style-none">
<li itemscope itemtype="http://schema.org/ListItem" itemprop="itemListElement">
<a class="js-selected-navigation-item selected reponav-item" itemprop="url" data-hotkey="g c" aria-current="page" data-selected-links="repo_source repo_downloads repo_commits repo_releases repo_tags repo_branches repo_packages repo_deployments /TentativeConvert/Syndicator" href="/TentativeConvert/Syndicator">
<div class="d-inline"><svg class="octicon octicon-code" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M4.72 3.22a.75.75 0 011.06 1.06L2.06 8l3.72 3.72a.75.75 0 11-1.06 1.06L.47 8.53a.75.75 0 010-1.06l4.25-4.25zm6.56 0a.75.75 0 10-1.06 1.06L13.94 8l-3.72 3.72a.75.75 0 101.06 1.06l4.25-4.25a.75.75 0 000-1.06l-4.25-4.25z"></path></svg></div>
<span itemprop="name">Code</span>
<meta itemprop="position" content="1">
</a> </li>
<li itemscope itemtype="http://schema.org/ListItem" itemprop="itemListElement">
<a itemprop="url" data-hotkey="g i" class="js-selected-navigation-item reponav-item" data-selected-links="repo_issues repo_labels repo_milestones /TentativeConvert/Syndicator/issues" href="/TentativeConvert/Syndicator/issues">
<div class="d-inline"><svg class="octicon octicon-issue-opened" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M8 1.5a6.5 6.5 0 100 13 6.5 6.5 0 000-13zM0 8a8 8 0 1116 0A8 8 0 010 8zm9 3a1 1 0 11-2 0 1 1 0 012 0zm-.25-6.25a.75.75 0 00-1.5 0v3.5a.75.75 0 001.5 0v-3.5z"></path></svg></div>
<span itemprop="name">Issues</span>
<span class="Counter">2</span>
<meta itemprop="position" content="2">
</a> </li>
<li itemscope itemtype="http://schema.org/ListItem" itemprop="itemListElement">
<a data-hotkey="g p" data-skip-pjax="true" itemprop="url" class="js-selected-navigation-item reponav-item" data-selected-links="repo_pulls checks /TentativeConvert/Syndicator/pulls" href="/TentativeConvert/Syndicator/pulls">
<div class="d-inline"><svg class="octicon octicon-git-pull-request" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z"></path></svg></div>
<span itemprop="name">Pull requests</span>
<span class="Counter">0</span>
<meta itemprop="position" content="4">
</a> </li>
<li itemscope itemtype="http://schema.org/ListItem" itemprop="itemListElement" class="position-relative float-left ">
<a data-hotkey="g w" data-skip-pjax="true" class="js-selected-navigation-item reponav-item" data-selected-links="repo_actions /TentativeConvert/Syndicator/actions" href="/TentativeConvert/Syndicator/actions">
<div class="d-inline"><svg class="octicon octicon-play" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M1.5 8a6.5 6.5 0 1113 0 6.5 6.5 0 01-13 0zM8 0a8 8 0 100 16A8 8 0 008 0zM6.379 5.227A.25.25 0 006 5.442v5.117a.25.25 0 00.379.214l4.264-2.559a.25.25 0 000-.428L6.379 5.227z"></path></svg></div>
Actions
</a>
</li>
<li >
<a data-hotkey="g b" class="js-selected-navigation-item reponav-item" data-selected-links="repo_projects new_repo_project repo_project /TentativeConvert/Syndicator/projects" href="/TentativeConvert/Syndicator/projects">
<div class="d-inline"><svg class="octicon octicon-project" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M1.75 0A1.75 1.75 0 000 1.75v12.5C0 15.216.784 16 1.75 16h12.5A1.75 1.75 0 0016 14.25V1.75A1.75 1.75 0 0014.25 0H1.75zM1.5 1.75a.25.25 0 01.25-.25h12.5a.25.25 0 01.25.25v12.5a.25.25 0 01-.25.25H1.75a.25.25 0 01-.25-.25V1.75zM11.75 3a.75.75 0 00-.75.75v7.5a.75.75 0 001.5 0v-7.5a.75.75 0 00-.75-.75zm-8.25.75a.75.75 0 011.5 0v5.5a.75.75 0 01-1.5 0v-5.5zM8 3a.75.75 0 00-.75.75v3.5a.75.75 0 001.5 0v-3.5A.75.75 0 008 3z"></path></svg></div>
Projects
<span class="Counter">0</span>
</a> </li>
<li >
<a data-skip-pjax="true" class="js-selected-navigation-item reponav-item" data-selected-links="security overview alerts policy token_scanning code_scanning /TentativeConvert/Syndicator/security" href="/TentativeConvert/Syndicator/security">
<div class="d-inline"><svg class="octicon octicon-shield" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M7.467.133a1.75 1.75 0 011.066 0l5.25 1.68A1.75 1.75 0 0115 3.48V7c0 1.566-.32 3.182-1.303 4.682-.983 1.498-2.585 2.813-5.032 3.855a1.7 1.7 0 01-1.33 0c-2.447-1.042-4.049-2.357-5.032-3.855C1.32 10.182 1 8.566 1 7V3.48a1.75 1.75 0 011.217-1.667l5.25-1.68zm.61 1.429a.25.25 0 00-.153 0l-5.25 1.68a.25.25 0 00-.174.238V7c0 1.358.275 2.666 1.057 3.86.784 1.194 2.121 2.34 4.366 3.297a.2.2 0 00.154 0c2.245-.956 3.582-2.104 4.366-3.298C13.225 9.666 13.5 8.36 13.5 7V3.48a.25.25 0 00-.174-.237l-5.25-1.68zM9 10.5a1 1 0 11-2 0 1 1 0 012 0zm-.25-5.75a.75.75 0 10-1.5 0v3a.75.75 0 001.5 0v-3z"></path></svg></div>
Security
<span class="Counter js-security-tab-count" data-url="/TentativeConvert/Syndicator/security/overall-count" hidden></span>
</a> </li>
<li >
<a class="js-selected-navigation-item reponav-item" data-selected-links="repo_graphs repo_contributors dependency_graph dependabot_updates pulse people /TentativeConvert/Syndicator/pulse" href="/TentativeConvert/Syndicator/pulse">
<div class="d-inline"><svg class="octicon octicon-graph" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M1.5 1.75a.75.75 0 00-1.5 0v12.5c0 .414.336.75.75.75h14.5a.75.75 0 000-1.5H1.5V1.75zm14.28 2.53a.75.75 0 00-1.06-1.06L10 7.94 7.53 5.47a.75.75 0 00-1.06 0L3.22 8.72a.75.75 0 001.06 1.06L7 7.06l2.47 2.47a.75.75 0 001.06 0l5.25-5.25z"></path></svg></div>
Insights
</a> </li>
</ul>
</nav>
<div class="reponav-wrapper reponav-small d-lg-none">
<nav class="reponav js-reponav text-center no-wrap"
itemscope
itemtype="http://schema.org/BreadcrumbList">
<span itemscope itemtype="http://schema.org/ListItem" itemprop="itemListElement">
<a class="js-selected-navigation-item selected reponav-item" itemprop="url" aria-current="page" data-selected-links="repo_source repo_downloads repo_commits repo_releases repo_tags repo_branches repo_packages repo_deployments /TentativeConvert/Syndicator" href="/TentativeConvert/Syndicator">
<span itemprop="name">Code</span>
<meta itemprop="position" content="1">
</a> </span>
<span itemscope itemtype="http://schema.org/ListItem" itemprop="itemListElement">
<a itemprop="url" class="js-selected-navigation-item reponav-item" data-selected-links="repo_issues repo_labels repo_milestones /TentativeConvert/Syndicator/issues" href="/TentativeConvert/Syndicator/issues">
<span itemprop="name">Issues</span>
<span class="Counter">2</span>
<meta itemprop="position" content="2">
</a> </span>
<span itemscope itemtype="http://schema.org/ListItem" itemprop="itemListElement">
<a itemprop="url" class="js-selected-navigation-item reponav-item" data-selected-links="repo_pulls checks /TentativeConvert/Syndicator/pulls" href="/TentativeConvert/Syndicator/pulls">
<span itemprop="name">Pull requests</span>
<span class="Counter">0</span>
<meta itemprop="position" content="4">
</a> </span>
<span itemscope itemtype="http://schema.org/ListItem" itemprop="itemListElement">
<a itemprop="url" class="js-selected-navigation-item reponav-item" data-selected-links="repo_projects new_repo_project repo_project /TentativeConvert/Syndicator/projects" href="/TentativeConvert/Syndicator/projects">
<span itemprop="name">Projects</span>
<span class="Counter">0</span>
<meta itemprop="position" content="5">
</a> </span>
<span itemscope itemtype="http://schema.org/ListItem" itemprop="itemListElement">
<a itemprop="url" class="js-selected-navigation-item reponav-item" data-selected-links="repo_actions /TentativeConvert/Syndicator/actions" href="/TentativeConvert/Syndicator/actions">
<span itemprop="name">Actions</span>
<meta itemprop="position" content="6">
</a> </span>
<a itemprop="url" class="js-selected-navigation-item reponav-item" data-selected-links="security overview alerts policy token_scanning code_scanning /TentativeConvert/Syndicator/security" href="/TentativeConvert/Syndicator/security">
<span itemprop="name">Security</span>
<span class="Counter js-security-deferred-tab-count" hidden></span>
<meta itemprop="position" content="8">
</a>
<a class="js-selected-navigation-item reponav-item" data-selected-links="pulse /TentativeConvert/Syndicator/pulse" href="/TentativeConvert/Syndicator/pulse">
Pulse
</a>
</nav>
</div>
</div>
<include-fragment class="js-notification-shelf-include-fragment" data-base-src="https://github.com/notifications/beta/shelf"></include-fragment>
<div class="container-lg clearfix new-discussion-timeline p-responsive">
<div class="repository-content ">
<a class="d-none js-permalink-shortcut" data-hotkey="y" href="/TentativeConvert/Syndicator/blob/baaac7eeece091f9c04d37d6bec1a5bdf047bb4c/unison-binaries/unison-fsmonitor">Permalink</a>
<!-- blob contrib key: blob_contributors:v22:6611a79f22df40397158909c7effec2a -->
<signup-prompt-controller class="signup-prompt-bg rounded-1" data-prompt="signup" hidden>
<div class="signup-prompt p-4 text-center mb-4 rounded-1">
<div class="position-relative">
<button
type="button"
class="position-absolute top-0 right-0 btn-link link-gray"
data-action="click:signup-prompt-controller#dismiss"
data-ga-click="(Logged out) Sign up prompt, clicked Dismiss, text:dismiss"
>
Dismiss
</button>
<h3 class="pt-2">Join GitHub today</h3>
<p class="col-6 mx-auto">GitHub is home to over 50 million developers working together to host and review code, manage projects, and build software together.</p>
<a class="btn btn-primary" data-ga-click="(Logged out) Sign up prompt, clicked Sign up, text:sign-up" data-hydro-click="{&quot;event_type&quot;:&quot;authentication.click&quot;,&quot;payload&quot;:{&quot;location_in_page&quot;:&quot;files signup prompt&quot;,&quot;repository_id&quot;:null,&quot;auth_type&quot;:&quot;SIGN_UP&quot;,&quot;originating_url&quot;:&quot;https://github.com/TentativeConvert/Syndicator/blob/master/unison-binaries/unison-fsmonitor&quot;,&quot;user_id&quot;:null}}" data-hydro-click-hmac="36df168af3d085afdf05ab781a169778c2cb9cb53b41a32eef24572f7a38b03b" href="/join?source=prompt-blob-show&amp;source_repo=TentativeConvert%2FSyndicator">Sign up</a>
</div>
</div>
</signup-prompt-controller>
<div class="d-flex flex-items-start flex-shrink-0 flex-column flex-md-row pb-3">
<span class="d-flex flex-justify-between width-full width-md-auto">
<details class="details-reset details-overlay branch-select-menu " id="branch-select-menu">
<summary class="btn css-truncate btn-sm"
data-hotkey="w"
title="Switch branches or tags">
<i>Branch:</i>
<span class="css-truncate-target" data-menu-button>master</span>
<span class="dropdown-caret"></span>
</summary>
<details-menu class="SelectMenu SelectMenu--hasFilter" src="/TentativeConvert/Syndicator/refs/master/unison-binaries/unison-fsmonitor?source_action=show&amp;source_controller=blob" preload>
<div class="SelectMenu-modal">
<include-fragment class="SelectMenu-loading" aria-label="Menu is loading">
<svg class="octicon octicon-octoface anim-pulse" height="32" viewBox="0 0 16 16" version="1.1" width="32" aria-hidden="true"><path fill-rule="evenodd" d="M14.7 5.34c.13-.32.55-1.59-.13-3.31 0 0-1.05-.33-3.44 1.3-1-.28-2.07-.32-3.13-.32s-2.13.04-3.13.32c-2.39-1.64-3.44-1.3-3.44-1.3-.68 1.72-.26 2.99-.13 3.31C.49 6.21 0 7.33 0 8.69 0 13.84 3.33 15 7.98 15S16 13.84 16 8.69c0-1.36-.49-2.48-1.3-3.35zM8 14.02c-3.3 0-5.98-.15-5.98-3.35 0-.76.38-1.48 1.02-2.07 1.07-.98 2.9-.46 4.96-.46 2.07 0 3.88-.52 4.96.46.65.59 1.02 1.3 1.02 2.07 0 3.19-2.68 3.35-5.98 3.35zM5.49 9.01c-.66 0-1.2.8-1.2 1.78s.54 1.79 1.2 1.79c.66 0 1.2-.8 1.2-1.79s-.54-1.78-1.2-1.78zm5.02 0c-.66 0-1.2.79-1.2 1.78s.54 1.79 1.2 1.79c.66 0 1.2-.8 1.2-1.79s-.53-1.78-1.2-1.78z"></path></svg>
</include-fragment>
</div>
</details-menu>
</details>
<div class="BtnGroup flex-shrink-0 d-md-none">
<a href="/TentativeConvert/Syndicator/find/master"
class="js-pjax-capture-input btn btn-sm BtnGroup-item"
data-pjax
data-hotkey="t">
Find file
</a>
<clipboard-copy value="unison-binaries/unison-fsmonitor" class="btn btn-sm BtnGroup-item">
Copy path
</clipboard-copy>
</div>
</span>
<h2 id="blob-path" class="breadcrumb flex-auto min-width-0 text-normal flex-md-self-center ml-md-2 mr-md-3 my-2 my-md-0">
<span class="js-repo-root text-bold"><span class="js-path-segment d-inline-block wb-break-all"><a data-pjax="true" href="/TentativeConvert/Syndicator"><span>Syndicator</span></a></span></span><span class="separator">/</span><span class="js-path-segment d-inline-block wb-break-all"><a data-pjax="true" href="/TentativeConvert/Syndicator/tree/master/unison-binaries"><span>unison-binaries</span></a></span><span class="separator">/</span><strong class="final-path">unison-fsmonitor</strong>
</h2>
<div class="BtnGroup flex-shrink-0 d-none d-md-inline-block">
<a href="/TentativeConvert/Syndicator/find/master"
class="js-pjax-capture-input btn btn-sm BtnGroup-item"
data-pjax
data-hotkey="t">
Find file
</a>
<clipboard-copy value="unison-binaries/unison-fsmonitor" class="btn btn-sm BtnGroup-item">
Copy path
</clipboard-copy>
</div>
</div>
<include-fragment src="/TentativeConvert/Syndicator/contributors/master/unison-binaries/unison-fsmonitor" class="Box Box--condensed commit-loader">
<div class="Box-body bg-blue-light f6">
Fetching contributors&hellip;
</div>
<div class="Box-body d-flex flex-items-center" >
<img alt="" class="loader-loading mr-2" src="https://github.githubassets.com/images/spinners/octocat-spinner-32-EAF2F5.gif" width="16" height="16" />
<span class="text-red h6 loader-error">Cannot retrieve contributors at this time</span>
</div>
</include-fragment>
<div class="Box mt-3 position-relative
">
<div class="Box-header py-2 d-flex flex-column flex-shrink-0 flex-md-row flex-md-items-center">
<div class="text-mono f6 flex-auto pr-3 flex-order-2 flex-md-order-1 mt-2 mt-md-0">
834 KB
</div>
<div class="d-flex py-1 py-md-0 flex-auto flex-order-1 flex-md-order-2 flex-sm-grow-0 flex-justify-between">
<div class="BtnGroup">
<a id="raw-url" class="btn btn-sm BtnGroup-item" href="/TentativeConvert/Syndicator/raw/master/unison-binaries/unison-fsmonitor">Download</a>
<a rel="nofollow" class="btn btn-sm BtnGroup-item" href="/TentativeConvert/Syndicator/commits/master/unison-binaries/unison-fsmonitor">History</a>
</div>
<div>
<a class="btn-octicon tooltipped tooltipped-nw js-remove-unless-platform"
data-platforms="windows,mac"
href="https://desktop.github.com"
aria-label="Open this file in GitHub Desktop"
data-ga-click="Repository, open with desktop">
<svg class="octicon octicon-device-desktop" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M1.75 2.5h12.5a.25.25 0 01.25.25v7.5a.25.25 0 01-.25.25H1.75a.25.25 0 01-.25-.25v-7.5a.25.25 0 01.25-.25zM14.25 1H1.75A1.75 1.75 0 000 2.75v7.5C0 11.216.784 12 1.75 12h3.727c-.1 1.041-.52 1.872-1.292 2.757A.75.75 0 004.75 16h6.5a.75.75 0 00.565-1.243c-.772-.885-1.193-1.716-1.292-2.757h3.727A1.75 1.75 0 0016 10.25v-7.5A1.75 1.75 0 0014.25 1zM9.018 12H6.982a5.72 5.72 0 01-.765 2.5h3.566a5.72 5.72 0 01-.765-2.5z"></path></svg>
</a>
<!-- '"` --><!-- </textarea></xmp> --></option></form><form class="inline-form" action="/TentativeConvert/Syndicator/delete/master/unison-binaries/unison-fsmonitor" accept-charset="UTF-8" method="post"><input type="hidden" data-csrf="true" name="authenticity_token" value="OstobtNJussoKcHS60sSto5G416bzOeti0kRP9AffxFZdmVlwfDDpiwDfdSqqvymgo9VPhXVtI/4InsT0glg0Q==" />
<button class="btn-octicon btn-octicon-danger tooltipped tooltipped-nw" type="submit"
aria-label="You must be signed in to make or propose changes" data-disable-with>
<svg class="octicon octicon-trashcan" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M6.5 1.75a.25.25 0 01.25-.25h2.5a.25.25 0 01.25.25V3h-3V1.75zm4.5 0V3h2.25a.75.75 0 010 1.5H2.75a.75.75 0 010-1.5H5V1.75C5 .784 5.784 0 6.75 0h2.5C10.216 0 11 .784 11 1.75zM4.496 6.675a.75.75 0 10-1.492.15l.66 6.6A1.75 1.75 0 005.405 15h5.19c.9 0 1.652-.681 1.741-1.576l.66-6.6a.75.75 0 00-1.492-.149l-.66 6.6a.25.25 0 01-.249.225h-5.19a.25.25 0 01-.249-.225l-.66-6.6z"></path></svg>
</button>
</form> </div>
</div>
</div>
<div itemprop="text" class="Box-body p-0 blob-wrapper data type-text ">
<div class="text-center p-3">
<a href="/TentativeConvert/Syndicator/blob/master/unison-binaries/unison-fsmonitor?raw=true">View raw</a>
</div>
</div>
</div>
<details class="details-reset details-overlay details-overlay-dark">
<summary data-hotkey="l" aria-label="Jump to line"></summary>
<details-dialog class="Box Box--overlay d-flex flex-column anim-fade-in fast linejump" aria-label="Jump to line">
<!-- '"` --><!-- </textarea></xmp> --></option></form><form class="js-jump-to-line-form Box-body d-flex" action="" accept-charset="UTF-8" method="get">
<input class="form-control flex-auto mr-3 linejump-input js-jump-to-line-field" type="text" placeholder="Jump to line&hellip;" aria-label="Jump to line" autofocus>
<button type="submit" class="btn" data-close-dialog>Go</button>
</form> </details-dialog>
</details>
</div>
</div>
</main>
</div>
</div>
<div class="footer container-lg width-full p-responsive" role="contentinfo">
<div class="position-relative d-flex flex-row-reverse flex-lg-row flex-wrap flex-lg-nowrap flex-justify-center flex-lg-justify-between pt-6 pb-2 mt-6 f6 text-gray border-top border-gray-light ">
<ul class="list-style-none d-flex flex-wrap col-12 col-lg-5 flex-justify-center flex-lg-justify-between mb-2 mb-lg-0">
<li class="mr-3 mr-lg-0">&copy; 2020 GitHub, Inc.</li>
<li class="mr-3 mr-lg-0"><a data-ga-click="Footer, go to terms, text:terms" href="https://github.com/site/terms">Terms</a></li>
<li class="mr-3 mr-lg-0"><a data-ga-click="Footer, go to privacy, text:privacy" href="https://github.com/site/privacy">Privacy</a></li>
<li class="mr-3 mr-lg-0"><a data-ga-click="Footer, go to security, text:security" href="https://github.com/security">Security</a></li>
<li class="mr-3 mr-lg-0"><a href="https://githubstatus.com/" data-ga-click="Footer, go to status, text:status">Status</a></li>
<li><a data-ga-click="Footer, go to help, text:help" href="https://help.github.com">Help</a></li>
</ul>
<a aria-label="Homepage" title="GitHub" class="footer-octicon d-none d-lg-block mx-lg-4" href="https://github.com">
<svg height="24" class="octicon octicon-mark-github" viewBox="0 0 16 16" version="1.1" width="24" aria-hidden="true"><path fill-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"></path></svg>
</a>
<ul class="list-style-none d-flex flex-wrap col-12 col-lg-5 flex-justify-center flex-lg-justify-between mb-2 mb-lg-0">
<li class="mr-3 mr-lg-0"><a data-ga-click="Footer, go to contact, text:contact" href="https://github.com/contact">Contact GitHub</a></li>
<li class="mr-3 mr-lg-0"><a href="https://github.com/pricing" data-ga-click="Footer, go to Pricing, text:Pricing">Pricing</a></li>
<li class="mr-3 mr-lg-0"><a href="https://developer.github.com" data-ga-click="Footer, go to api, text:api">API</a></li>
<li class="mr-3 mr-lg-0"><a href="https://training.github.com" data-ga-click="Footer, go to training, text:training">Training</a></li>
<li class="mr-3 mr-lg-0"><a href="https://github.blog" data-ga-click="Footer, go to blog, text:blog">Blog</a></li>
<li><a data-ga-click="Footer, go to about, text:about" href="https://github.com/about">About</a></li>
</ul>
</div>
<div class="d-flex flex-justify-center pb-6">
<span class="f6 text-gray-light"></span>
</div>
</div>
<div id="ajax-error-message" class="ajax-error-message flash flash-error">
<svg class="octicon octicon-alert" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M8.22 1.754a.25.25 0 00-.44 0L1.698 13.132a.25.25 0 00.22.368h12.164a.25.25 0 00.22-.368L8.22 1.754zm-1.763-.707c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0114.082 15H1.918a1.75 1.75 0 01-1.543-2.575L6.457 1.047zM9 11a1 1 0 11-2 0 1 1 0 012 0zm-.25-5.25a.75.75 0 00-1.5 0v2.5a.75.75 0 001.5 0v-2.5z"></path></svg>
<button type="button" class="flash-close js-ajax-error-dismiss" aria-label="Dismiss error">
<svg class="octicon octicon-x" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M3.72 3.72a.75.75 0 011.06 0L8 6.94l3.22-3.22a.75.75 0 111.06 1.06L9.06 8l3.22 3.22a.75.75 0 11-1.06 1.06L8 9.06l-3.22 3.22a.75.75 0 01-1.06-1.06L6.94 8 3.72 4.78a.75.75 0 010-1.06z"></path></svg>
</button>
You cant perform that action at this time.
</div>
<script crossorigin="anonymous" async="async" integrity="sha512-WcQmT2vhcClFVOaaAJV/M+HqsJ2Gq/myvl6F3gCVBxykazXTs+i5fvxncSXwyG1CSfcrqmLFw/R/bmFYzprX2A==" type="application/javascript" id="js-conditional-compat" data-src="https://github.githubassets.com/assets/compat-bootstrap-59c4264f.js"></script>
<script crossorigin="anonymous" integrity="sha512-Y86V8OBlvF6I/7e56GKOOt80Yg1RTGA09uqFFX18aiBtevLbKGxB7sVpCn79fukppFIBqyBTB/s6l0Bhn0kidQ==" type="application/javascript" src="https://github.githubassets.com/assets/environment-bootstrap-63ce95f0.js"></script>
<script crossorigin="anonymous" async="async" integrity="sha512-jfR+4VdZuPf5Ck+JA3AZuzWGHz9Sb21keZOYuMoNdfMJovIUb9vxfSdvNSchxAwj5oav48KBfa54+wbuuW8Tlg==" type="application/javascript" src="https://github.githubassets.com/assets/vendor-8df47ee1.js"></script>
<script crossorigin="anonymous" async="async" integrity="sha512-bnTRHbVvU80xFKdsRq3F+73vfYnCKrG6J4UPGgli/ihqO3C1SwjXmcR10RCfMbNGphMvxKX7mn6lFQM3Mc25Sw==" type="application/javascript" src="https://github.githubassets.com/assets/frameworks-6e74d11d.js"></script>
<script crossorigin="anonymous" async="async" integrity="sha512-WYDfGvwYLFP8eY1vZBdBP+zu2OjhYTlGGdNVx9wbSXzjY0tGrCFO4bDdbexcMhngArJuMmiX9V+hHcJ/mZRfQg==" type="application/javascript" src="https://github.githubassets.com/assets/github-bootstrap-5980df1a.js"></script>
<script crossorigin="anonymous" async="async" integrity="sha512-4GcSWGoe36+BoWho4gtJcByZe8j43w+lt2/PDe3rmBxRVSgD29YipDwuIywe8fvOd2b2CszBqaPGxSznUtE3Xg==" type="application/javascript" data-module-id="./drag-drop.js" data-src="https://github.githubassets.com/assets/drag-drop-e0671258.js"></script>
<script crossorigin="anonymous" async="async" integrity="sha512-3Vk1NFIOm+TBUMM6pTA6DCUwwLLnc/QIT8jpENm71InvSU8O4p2plDagpst1tH1l+9jOBnneaXZnAskA9a2b3w==" type="application/javascript" data-module-id="./gist-vendor.js" data-src="https://github.githubassets.com/assets/gist-vendor-dd593534.js"></script>
<script crossorigin="anonymous" async="async" integrity="sha512-urN6bhHnHu4C12A+cTH3dOp+CwLaycy2HUXr95hvu5pbYRdF8z6iR+UQcTZutQ6mZG3Njluw2MTZVCNmwcqh8g==" type="application/javascript" data-module-id="./randomColor.js" data-src="https://github.githubassets.com/assets/randomColor-bab37a6e.js"></script>
<div class="js-stale-session-flash flash flash-warn flash-banner" hidden
>
<svg class="octicon octicon-alert" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M8.22 1.754a.25.25 0 00-.44 0L1.698 13.132a.25.25 0 00.22.368h12.164a.25.25 0 00.22-.368L8.22 1.754zm-1.763-.707c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0114.082 15H1.918a1.75 1.75 0 01-1.543-2.575L6.457 1.047zM9 11a1 1 0 11-2 0 1 1 0 012 0zm-.25-5.25a.75.75 0 00-1.5 0v2.5a.75.75 0 001.5 0v-2.5z"></path></svg>
<span class="js-stale-session-flash-signed-in" hidden>You signed in with another tab or window. <a href="">Reload</a> to refresh your session.</span>
<span class="js-stale-session-flash-signed-out" hidden>You signed out in another tab or window. <a href="">Reload</a> to refresh your session.</span>
</div>
<template id="site-details-dialog">
<details class="details-reset details-overlay details-overlay-dark lh-default text-gray-dark hx_rsm" open>
<summary role="button" aria-label="Close dialog"></summary>
<details-dialog class="Box Box--overlay d-flex flex-column anim-fade-in fast hx_rsm-dialog hx_rsm-modal">
<button class="Box-btn-octicon m-0 btn-octicon position-absolute right-0 top-0" type="button" aria-label="Close dialog" data-close-dialog>
<svg class="octicon octicon-x" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M3.72 3.72a.75.75 0 011.06 0L8 6.94l3.22-3.22a.75.75 0 111.06 1.06L9.06 8l3.22 3.22a.75.75 0 11-1.06 1.06L8 9.06l-3.22 3.22a.75.75 0 01-1.06-1.06L6.94 8 3.72 4.78a.75.75 0 010-1.06z"></path></svg>
</button>
<div class="octocat-spinner my-6 js-details-dialog-spinner"></div>
</details-dialog>
</details>
</template>
<div class="Popover js-hovercard-content position-absolute" style="display: none; outline: none;" tabindex="0">
<div class="Popover-message Popover-message--bottom-left Popover-message--large Box box-shadow-large" style="width:360px;">
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1 @@
ansible_python_interpreter: /usr/bin/python3

22
group_vars/all/consul.yml Normal file
View File

@@ -0,0 +1,22 @@
---
consul_server: "{{ ansible_hostname in consul_servers }}"
consul_acl_datacenter: msiserv
consul_acl_master_token: "229369d9-6345-6c57-72b3-166f3c2a74a5"
consul_acl_agent_token: "ad92623d-fcab-85c2-55ae-3fbd36da6f83"
consul_acl_token: "168d2a19-0a8d-b197-03dc-0e2b0c324421"
consul_acl_replication_token: "377fdfae-02ac-7a43-f9d4-c5a9b1c2bdeb"
# Bootstrap only:
#consul_bootstrap_expect: 2
#consul_encrypt_key: "eUQzZHtGbDlNmMuBr1UM2Q=="
consul_servers:
- eu.srv
- us.srv
- admin.srv
consul_services: yes
consul_dns_forwarders: "{{ network_fallback_resolvers }}"

View File

@@ -0,0 +1,10 @@
datacenter_global_networks:
- 192.168.255.0/24
- 10.11.0.0/16
datacenter_id:
- msiserv
datacenter_public_networks:
- 62.171.160.169/32
- 207.244.234.58/32

View File

@@ -0,0 +1,30 @@
---
firewall_ssh_acl:
- 0.0.0.0/0 # allow SSH from everywhere
firewall_influx_acl:
- 10.11.0.0/16 # allow influx from ip(s)
- 192.168.255.0/24 # allow influx from ip(s)
firewall_mariadb_acl:
- 10.11.0.0/16 # allow mariadb from ip(s)
- 192.168.255.0/24 # allow mariadb from ip(s)
firewall_ssh_acl_extra: "{{ datacenter_global_networks + datacenter_public_networks }}"
firewall_influx_acl_extra: "{{ datacenter_global_networks + datacenter_public_networks }}"
firewall_mariadb_acl_extra: "{{ datacenter_global_networks + datacenter_public_networks }}"
firewall_monitoring_ips:
- 10.11.11.200
- 10.11.12.150
# TODO: Needs an inventory of all external services.
firewall_output_default_drop: no

View File

@@ -0,0 +1,8 @@
---
network_default_gateway: "{{ ansible_default_ipv4.gateway }}"
network_nameservers:
- 1.1.1.1
network_bind_listen: "{{ network_private_ip }}"

View File

@@ -0,0 +1,5 @@
postfix_mynetworks: "{{ datacenter_global_networks + datacenter_public_networks + datacenter_public_ipv6_networks if postfix_relay else [] }}"
postfix_dkim_domains:
maruntiel.net:
selector: 201903

View File

@@ -0,0 +1,11 @@
---
datacenter_id: msiserv
datacenter_name: EU-Germany
datacenter_full_name: Contabo
datacenter_local_networks:
- 192.168.255.0/24
- 10.11.201.0/24
datacenter_public_networks:
- 62.171.160.169/32

11
group_vars/eu/network.yml Normal file
View File

@@ -0,0 +1,11 @@
---
network_default_gateway: 62.171.160.1
network_nameservers:
- 213.136.95.10
- 213.136.95.11
network_fallback_resolvers:
- 10.11.201.101
network_private_ip:
- 10.11.201.101
- 10.11.202.101
- 10.11.11.200

View File

@@ -0,0 +1,9 @@
---
datacenter_id: msiserv
datacenter_name: EU-Romania
datacenter_full_name: Maruntiel
datacenter_local_networks:
- 10.11.11.0/24
- 10.11.12.0/24

10
group_vars/ro/network.yml Normal file
View File

@@ -0,0 +1,10 @@
---
network_default_gateway: 10.11.12.1
network_nameservers:
- 1.1.1.2
- 8.8.4.4
network_fallback_resolvers:
- 10.11.201.101
network_private_ip:
- 10.11.11.200
- 10.11.12.150

View File

@@ -0,0 +1,10 @@
---
datacenter_id: msiserv
datacenter_name: US-New_York
datacenter_full_name: Contabo
datacenter_local_networks:
- 192.168.255.0/24
- 10.11.202.0/24
datacenter_public_networks:
- 207.244.234.58/32

View File

@@ -0,0 +1,7 @@
---
network_default_gateway: 207.244.224.1
network_nameservers:
- 209.126.15.51
- 209.126.15.52
network_fallback_resolvers:
- 10.11.202.101

View File

@@ -0,0 +1 @@
ansible_python_interpreter: /usr/bin/python3

View File

@@ -0,0 +1,9 @@
---
datacenter_id: msiserv
datacenter_name: EU-Romania
datacenter_full_name: Maruntiel
datacenter_local_networks:
- 10.11.11.0/24
- 10.11.12.0/24

View File

@@ -0,0 +1 @@
network_private_ip: 10.11.11.200

View File

@@ -0,0 +1 @@
ansible_python_interpreter: /usr/bin/python3

View File

@@ -0,0 +1,11 @@
---
datacenter_id: msiserv
datacenter_name: EU-Germany
datacenter_full_name: Contabo
datacenter_local_networks:
- 192.168.255.0/24
- 10.11.201.0/24
datacenter_public_networks:
- 62.171.160.169/32

View File

@@ -0,0 +1,2 @@
network_public_ip: 62.171.160.169
network_private_ip: 10.11.201.101

View File

@@ -0,0 +1 @@
ansible_python_interpreter: /usr/bin/python3

View File

@@ -0,0 +1,9 @@
---
datacenter_id: msiserv
datacenter_name: EU-Romania
datacenter_full_name: Maruntiel
datacenter_local_networks:
- 10.11.11.0/24
- 10.11.12.0/24

View File

@@ -0,0 +1 @@
network_private_ip: 10.11.12.150

View File

@@ -0,0 +1 @@
ansible_python_interpreter: /usr/bin/python3

View File

@@ -0,0 +1,10 @@
---
datacenter_id: msiserv
datacenter_name: US-New_York
datacenter_full_name: Contabo
datacenter_local_networks:
- 192.168.255.0/24
- 10.11.202.0/24
datacenter_public_networks:
- 207.244.234.58/32

View File

@@ -0,0 +1,2 @@
network_public_ip: 207.244.234.58
network_private_ip: 10.11.202.101

31
inventory Normal file
View File

@@ -0,0 +1,31 @@
[eu]
eu.srv
[us]
us.srv
[ro]
admin.srv
[consul:children]
ro
eu
us
[consul]
[mysql]
eu.srv
us.srv
[zookeeper]
eu.srv
us.srv
[apache_php]
eu.srv
us.srv
[postfix]
eu.srv
us.srv

6
playbooks/apache.yml Normal file
View File

@@ -0,0 +1,6 @@
---
# Configure Apache.
- hosts: apache_php
roles:
- apache_php

22
playbooks/basic-tools.yml Normal file
View File

@@ -0,0 +1,22 @@
---
- hosts: all
become: true
tasks:
- name: update repo index
apt:
update_cache: yes
- name: install usefull and basic system tools
apt:
name:
- vim-nox
- mc
- nmap
- net-tools
- dnsutils
- tmux
- tcpdump
- iptraf-ng
- screen

6
playbooks/consul.yml Normal file
View File

@@ -0,0 +1,6 @@
---
- hosts: consul
serial: 2
gather_facts: true
roles:
- consul

4
playbooks/firewall.yml Normal file
View File

@@ -0,0 +1,4 @@
---
- hosts: all
roles:
- firewall

4
playbooks/network.yml Normal file
View File

@@ -0,0 +1,4 @@
---
- hosts: all
roles:
- role: network

7
playbooks/ntp.yml Normal file
View File

@@ -0,0 +1,7 @@
---
# Configure the base settings for all hosts.
- hosts: all
roles:
- role: ntp

4
playbooks/postfix.yml Normal file
View File

@@ -0,0 +1,4 @@
---
- hosts: all
roles:
- postfix

View File

@@ -0,0 +1,60 @@
---
apache_consul_service: "{{ consul_services|default(False) }}"
apache_mpm_prefork: true
apache_timeout: 30
apache_monitoring_ips: "{{ (nagios_nrpe_monitoring_ips|default([]) + firewall_monitoring_ips|default([])) | join(' ') }}"
apache_mod_ssl_protocols: all -SSLv2 -SSLv3 -TLSv1
apache_mod_ssl_ciphers:
- ECDHE-RSA-AES128-GCM-SHA256
- ECDHE-ECDSA-AES128-GCM-SHA256
- ECDHE-RSA-AES256-GCM-SHA384
- ECDHE-ECDSA-AES256-GCM-SHA384
- ECDHE-RSA-CHACHA20-POLY1305
- ECDHE-ECDSA-CHACHA20-POLY1305
- ECDHE-RSA-AES128-SHA256
- ECDHE-ECDSA-AES128-SHA256
- ECDHE-RSA-AES256-SHA384
- ECDHE-ECDSA-AES256-SHA384
- ECDHE-RSA-AES128-SHA
- ECDHE-ECDSA-AES128-SHA
- ECDHE-RSA-AES256-SHA
- ECDHE-ECDSA-AES256-SHA
- DHE-RSA-AES128-GCM-SHA256
- DHE-RSA-AES256-GCM-SHA384
- DHE-RSA-AES128-SHA256
- DHE-RSA-AES256-SHA256
- DHE-RSA-AES128-SHA
- DHE-RSA-AES256-SHA
# - AES128-GCM-SHA256
# - AES256-GCM-SHA384
# - AES128-SHA256
# - AES256-SHA256
# - AES128-SHA
# - AES256-SHA
apache_http2_enabled: on
apache_firewall: yes
apache_firewall_public: yes
apache_firewall_public_isolated: no
apache_firewall_acl: []
apache_firewall_drop_dst: []
apache_security_headers: false
apache_mod_evasive: off
apache_mod_evasive_settings:
DOSHashTableSize: 3097
DOSPageCount: 20
DOSSiteCount: 100
DOSPageInterval: 2
DOSSiteInterval: 1
DOSBlockingPeriod: 10
apache_mod_security: "{{ apache_firewall_public }}"
apache_mod_security_enabled: false

View File

@@ -0,0 +1,11 @@
---
- name: Restart Apache
service: name=apache2 state=restarted
- name: Reload Apache
service: name=apache2 state=reloaded
- name: Reload Apache systemd
systemd: daemon_reload=yes

View File

@@ -0,0 +1,8 @@
---
dependencies:
- role: firewall
when: apache_firewall
- role: consul
when: apache_consul_service

164
roles/apache/tasks/main.yml Normal file
View File

@@ -0,0 +1,164 @@
---
- name: Install Apache packages
apt:
pkg:
- apache2
- socat
state: present
tags: packages
- name: Ensure the ssl-cert group exists
group:
name: ssl-cert
system: yes
tags: packages
- name: Ensure apache is a member of ssl-cert
user:
name: www-data
groups: ssl-cert
append: yes
tags: packages
- name: Install Apache config
template:
dest: /etc/apache2/apache2.conf
src: etc_apache2_apache2.conf.j2
mode: 0644
owner: root
group: root
notify: Reload Apache
tags: configs
- name: Install Apache module configs
template:
dest: "/etc/apache2/mods-available/{{ item }}"
src: "etc_apache2_mods-available_{{ item }}.j2"
mode: 0644
owner: root
group: root
with_items:
- deflate.conf
- http2.conf
- ssl.conf
- status.conf
notify: Reload Apache
tags:
- configs
- apache-configs
- name: Enable Apache modules
apache2_module:
name: "{{ item }}"
state: present
force: yes
with_items:
- deflate
- env
- expires
- headers
- http2
- reqtimeout
- rewrite
- setenvif
- ssl
- status
- unique_id
notify: Restart Apache
tags: configs
- name: Install Apache other configs
template:
dest: "/etc/apache2/conf-available/{{ item }}"
src: "etc_apache2_conf-available_{{ item }}.j2"
with_items:
- logging.conf
- security.conf
notify: Reload Apache
tags: [configs, logging]
- name: Enable Apache other configs
command: "a2enconf {{ item }}"
args:
creates: "/etc/apache2/conf-enabled/{{ item }}"
with_items:
- logging.conf
- security.conf
notify: Reload Apache
tags: configs
- name: Enable the SSL default vhost
command: a2ensite default-ssl
args:
creates: /etc/apache2/sites-enabled/default-ssl.conf
notify: Reload Apache
tags: configs
- name: Install Apache logrotate snippet
template:
dest: /etc/logrotate.d/apache2
src: etc_logrotate.d_apache2.j2
mode: 0644
owner: root
group: root
tags: [configs, logrotate]
- name: Install apache2.service override dir
file:
dest: /etc/systemd/system/apache2.service.d
state: directory
mode: 0755
owner: root
group: root
tags: [configs, systemd]
- name: Install apache2.service override
template:
dest: /etc/systemd/system/apache2.service.d/local.conf
src: etc_systemd_system_apache2.service.d_local.conf.j2
mode: 0644
owner: root
group: root
notify: Reload Apache systemd
tags: [configs, systemd]
- name: Ensure Apache is running
systemd:
name: apache2
state: started
enabled: yes
tags: configs
- include: mod_evasive.yml
when: apache_mod_evasive
tags: mod_evasive
- include: mod_security.yml
when: apache_mod_security
tags: mod_security
- name: Install the Apache firewall config
template:
dest: "/etc/firewall/{{ item }}"
src: "etc_firewall_{{ item | replace('/', '_') }}.j2"
mode: 0600
owner: root
group: root
when: firewall_enabled and apache_firewall
notify: Restart firewall
with_items:
- rules-v4.d/40_apache.sh
- rules-v6.d/40_apache.sh
tags:
- configs
- firewall
- name: Register the apache service in Consul
template:
dest: /etc/consul.d/service-apache.hcl
src: etc_consul.d_service-apache.hcl.j2
when: apache_consul_service
notify: Reload consul
tags: configs

View File

@@ -0,0 +1,27 @@
---
- name: Install Apache mod_evasive
apt:
pkg:
- libapache2-mod-evasive
state: present
notify: Restart Apache
tags: packages
- name: Install Apache mod_evasive config
template:
dest: /etc/apache2/mods-available/evasive.conf
src: etc_apache2_mods-available_evasive.conf.j2
mode: 0644
owner: root
group: root
notify: Reload Apache
tags: configs
- name: Enable Apache mod_evasive
apache2_module:
name: evasive
state: present
force: yes
notify: Restart Apache
tags: configs

View File

@@ -0,0 +1,38 @@
---
- name: Install Apache mod_security
apt:
pkg:
- libapache2-mod-security2=2.9.*
- modsecurity-crs=3.*
state: present
notify: Restart Apache
tags: packages
- name: Install Apache mod_security config
template:
dest: /etc/modsecurity/modsecurity.conf
src: etc_modsecurity_modsecurity.conf.j2
mode: 0644
owner: root
group: root
notify: Reload Apache
tags: configs
- name: Install Apache mod_security ruleset config
template:
dest: /etc/modsecurity/crs/crs-setup.conf
src: etc_modsecurity_crs_crs-setup.conf.j2
mode: 0644
owner: root
group: root
notify: Reload Apache
tags: configs
- name: Enable Apache mod_security
apache2_module:
name: security2
state: present
force: yes
notify: Restart Apache
tags: configs

View File

@@ -0,0 +1,233 @@
# {{ ansible_managed }}
# This is the main Apache server configuration file. It contains the
# configuration directives that give the server its instructions.
# See http://httpd.apache.org/docs/2.4/ for detailed information about
# the directives and /usr/share/doc/apache2/README.Debian about Debian specific
# hints.
#
#
# Summary of how the Apache 2 configuration works in Debian:
# The Apache 2 web server configuration in Debian is quite different to
# upstream's suggested way to configure the web server. This is because Debian's
# default Apache2 installation attempts to make adding and removing modules,
# virtual hosts, and extra configuration directives as flexible as possible, in
# order to make automating the changes and administering the server as easy as
# possible.
# It is split into several files forming the configuration hierarchy outlined
# below, all located in the /etc/apache2/ directory:
#
# /etc/apache2/
# |-- apache2.conf
# | `-- ports.conf
# |-- mods-enabled
# | |-- *.load
# | `-- *.conf
# |-- conf-enabled
# | `-- *.conf
# `-- sites-enabled
# `-- *.conf
#
#
# * apache2.conf is the main configuration file (this file). It puts the pieces
# together by including all remaining configuration files when starting up the
# web server.
#
# * ports.conf is always included from the main configuration file. It is
# supposed to determine listening ports for incoming connections which can be
# customized anytime.
#
# * Configuration files in the mods-enabled/, conf-enabled/ and sites-enabled/
# directories contain particular configuration snippets which manage modules,
# global configuration fragments, or virtual host configurations,
# respectively.
#
# They are activated by symlinking available configuration files from their
# respective *-available/ counterparts. These should be managed by using our
# helpers a2enmod/a2dismod, a2ensite/a2dissite and a2enconf/a2disconf. See
# their respective man pages for detailed information.
#
# * The binary is called apache2. Due to the use of environment variables, in
# the default configuration, apache2 needs to be started/stopped with
# /etc/init.d/apache2 or apache2ctl. Calling /usr/bin/apache2 directly will not
# work with the default configuration.
# Global configuration
#
#
# ServerRoot: The top of the directory tree under which the server's
# configuration, error, and log files are kept.
#
# NOTE! If you intend to place this on an NFS (or otherwise network)
# mounted filesystem then please read the Mutex documentation (available
# at <URL:http://httpd.apache.org/docs/2.4/mod/core.html#mutex>);
# you will save yourself a lot of trouble.
#
# Do NOT add a slash at the end of the directory path.
#
#ServerRoot "/etc/apache2"
#
# The accept serialization lock file MUST BE STORED ON A LOCAL DISK.
#
#Mutex file:${APACHE_LOCK_DIR} default
#
# The directory where shm and other runtime files will be stored.
#
DefaultRuntimeDir ${APACHE_RUN_DIR}
#
# PidFile: The file in which the server should record its process
# identification number when it starts.
# This needs to be set in /etc/apache2/envvars
#
PidFile ${APACHE_PID_FILE}
#
# Timeout: The number of seconds before receives and sends time out.
#
Timeout {{ apache_timeout }}
#
# KeepAlive: Whether or not to allow persistent connections (more than
# one request per connection). Set to "Off" to deactivate.
#
KeepAlive On
#
# MaxKeepAliveRequests: The maximum number of requests to allow
# during a persistent connection. Set to 0 to allow an unlimited amount.
# We recommend you leave this number high, for maximum performance.
#
MaxKeepAliveRequests 100
#
# KeepAliveTimeout: Number of seconds to wait for the next request from the
# same client on the same connection.
#
KeepAliveTimeout 5
# These need to be set in /etc/apache2/envvars
User ${APACHE_RUN_USER}
Group ${APACHE_RUN_GROUP}
#
# HostnameLookups: Log the names of clients or just their IP addresses
# e.g., www.apache.org (on) or 204.62.129.132 (off).
# The default is off because it'd be overall better for the net if people
# had to knowingly turn this feature on, since enabling it means that
# each client request will result in AT LEAST one lookup request to the
# nameserver.
#
HostnameLookups Off
# ErrorLog: The location of the error log file.
# If you do not specify an ErrorLog directive within a <VirtualHost>
# container, error messages relating to that virtual host will be
# logged here. If you *do* define an error logfile for a <VirtualHost>
# container, that host's errors will be logged there and not here.
#
ErrorLog ${APACHE_LOG_DIR}/error.log
#
# LogLevel: Control the severity of messages logged to the error_log.
# Available values: trace8, ..., trace1, debug, info, notice, warn,
# error, crit, alert, emerg.
# It is also possible to configure the log level for particular modules, e.g.
# "LogLevel info ssl:warn"
#
LogLevel warn
# Include module configuration:
IncludeOptional mods-enabled/*.load
IncludeOptional mods-enabled/*.conf
# Include list of ports to listen on
Include ports.conf
# Sets the default security model of the Apache2 HTTPD server. It does
# not allow access to the root filesystem outside of /usr/share and /var/www.
# The former is used by web applications packaged in Debian,
# the latter may be used for local directories served by the web server. If
# your system is serving content from a sub-directory in /srv you must allow
# access here, or in any related virtual host.
<Directory />
Options FollowSymLinks
AllowOverride None
Require all denied
</Directory>
<Directory /usr/share>
AllowOverride None
Require all granted
</Directory>
<Directory /var/www/>
Options FollowSymLinks
AllowOverride None
Require all granted
</Directory>
<Directory /srv/www>
Options FollowSymLinks
AllowOverride None
Require all granted
</Directory>
<Directory /opt/kc>
Options FollowSymLinks
AllowOverride None
Require all granted
</Directory>
# AccessFileName: The name of the file to look for in each directory
# for additional configuration directives. See also the AllowOverride
# directive.
#
AccessFileName .htaccess
#
# The following lines prevent dot files from being
# viewed by Web clients.
#
<FilesMatch "^\.(?!well-known)">
Require all denied
</FilesMatch>
#
# The following directives define some format nicknames for use with
# a CustomLog directive.
#
# These deviate from the Common Log Format definitions in that they use %O
# (the actual bytes sent including headers) instead of %b (the size of the
# requested file), because the latter makes it impossible to detect partial
# requests.
#
# Note that the use of %{X-Forwarded-For}i instead of %h is not recommended.
# Use mod_remoteip instead.
#
LogFormat "%v:%p %h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" vhost_combined
LogFormat "%h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" combined
LogFormat "%h %l %u %t \"%r\" %>s %O" common
LogFormat "%{Referer}i -> %U" referer
LogFormat "%{User-agent}i" agent
# Include of directories ignores editors' and dpkg's backup files,
# see README.Debian for details.
# Include generic snippets of statements
IncludeOptional conf-enabled/*.conf
# Include the virtual host configurations:
IncludeOptional sites-enabled/*.conf
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet

View File

@@ -0,0 +1,7 @@
# {{ ansible_managed }}
# BufferedLogs On
LogFormat "%v:%p %R %m %>s %H conn=%X %D %O %I %k" metrics
GlobalLog ${APACHE_LOG_DIR}/metrics.log metrics

View File

@@ -0,0 +1,88 @@
# {{ ansible_managed }}
#
# Disable access to the entire file system except for the directories that
# are explicitly allowed later.
#
# This currently breaks the configurations that come with some web application
# Debian packages.
#
<Directory />
AllowOverride None
Require all denied
</Directory>
# Changing the following options will not really affect the security of the
# server, but might make attacks slightly more difficult in some cases.
#
# ServerTokens
# This directive configures what you return as the Server HTTP response
# Header. The default is 'Full' which sends information about the OS-Type
# and compiled in modules.
# Set to one of: Full | OS | Minimal | Minor | Major | Prod
# where Full conveys the most information, and Prod the least.
ServerTokens Prod
#ServerTokens OS
#ServerTokens Full
#
# Optionally add a line containing the server version and virtual host
# name to server-generated pages (internal error documents, FTP directory
# listings, mod_status and mod_info output etc., but not CGI generated
# documents or custom error documents).
# Set to "EMail" to also include a mailto: link to the ServerAdmin.
# Set to one of: On | Off | EMail
ServerSignature Off
#ServerSignature On
#
# Allow TRACE method
#
# Set to "extended" to also reflect the request body (only for testing and
# diagnostic purposes).
#
# Set to one of: On | Off | extended
TraceEnable Off
#TraceEnable On
#
# Forbid access to version control directories
#
# If you use version control systems in your document root, you should
# probably deny access to their directories. For example, for subversion:
#
<DirectoryMatch "/\.(git|svn|subversion)">
Require all denied
</DirectoryMatch>
#
# Setting this header will prevent MSIE from interpreting files as something
# else than declared by the content type in the HTTP headers.
# Requires mod_headers to be enabled.
#
#Header set X-Content-Type-Options: "nosniff"
#
# Setting this header will prevent other sites from embedding pages from this
# site as frames. This defends against clickjacking attacks.
# Requires mod_headers to be enabled.
#
#Header set X-Frame-Options: "sameorigin"
{% if apache_security_headers %}
#
# Security headers for PCI-DSS.
#
Header always set X-Content-Type-Options: "nosniff"
Header always set X-Frame-Options: "sameorigin"
Header always set X-XSS-Protection "1; mode=block"
{% endif %}
#
# Accept host names with _underscores_
#
HTTPProtocolOptions unsafe
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet

View File

@@ -0,0 +1,22 @@
# {{ ansible_managed }}
<IfModule mod_deflate.c>
<IfModule mod_filter.c>
# these are known to be safe with MSIE 6
AddOutputFilterByType DEFLATE text/html text/plain text/xml image/svg+xml
# everything else may cause problems with MSIE 6
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE application/x-javascript application/javascript application/ecmascript
AddOutputFilterByType DEFLATE application/rss+xml
AddOutputFilterByType DEFLATE application/xml
AddOutputFilterByType DEFLATE application/json
AddOutputFilterByType DEFLATE application/x-php-serialized-rpc
AddOutputFilterByType DEFLATE image/x-icon text/javascript
DeflateFilterNote Ratio ratio
</IfModule>
</IfModule>
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet

View File

@@ -0,0 +1,30 @@
# {{ ansible_managed }}
<IfModule mod_evasive20.c>
{% for key, value in apache_mod_evasive_settings | dictsort %}
{{ key }} {{ value }}
{% endfor %}
#DOSEmailNotify you@yourdomain.com
#DOSSystemCommand "su - someuser -c '/sbin/... %s ...'"
#DOSLogDir "/var/log/mod_evasive"
DOSWhitelist 10.*.*.*
DOSWhitelist 192.168.*.*
DOSWhitelist 63.254.74.*
DOSWhitelist 8.28.239.*
{% for ip in firewall_monitoring_ips|default([]) if ip|ipv4('public') %}
DOSWhitelist {{ ip }}
{% endfor %}
{% for ip in firewall_whitelist_office_ip|default([]) %}
DOSWhitelist {{ ip | regex_replace('[0-9]+/[0-9]+', '*') }}
{% endfor %}
{% for ip in apache_mod_evasive_whitelist|default([]) %}
DOSWhitelist {{ ip | regex_replace('[0-9]+/[0-9]+', '*') }}
{% endfor %}
</IfModule>

View File

@@ -0,0 +1,17 @@
# {{ ansible_managed }}
<IfModule http2_module>
{% if apache_http2_enabled %}
Protocols h2 h2c http/1.1
{% else %}
Protocols http/1.1 # http/2 disabled
{% endif %}
H2Push on
H2PushPriority * after
H2PushPriority text/css before
H2PushPriority image/jpeg after 32
H2PushPriority image/png after 32
H2PushPriority application/javascript interleaved
</IfModule>

View File

@@ -0,0 +1,91 @@
# {{ ansible_managed }}
<IfModule mod_ssl.c>
# Pseudo Random Number Generator (PRNG):
# Configure one or more sources to seed the PRNG of the SSL library.
# The seed data should be of good random quality.
# WARNING! On some platforms /dev/random blocks if not enough entropy
# is available. This means you then cannot use the /dev/random device
# because it would lead to very long connection times (as long as
# it requires to make more entropy available). But usually those
# platforms additionally provide a /dev/urandom device which doesn't
# block. So, if available, use this one instead. Read the mod_ssl User
# Manual for more details.
#
SSLRandomSeed startup builtin
SSLRandomSeed startup file:/dev/urandom 512
SSLRandomSeed connect builtin
SSLRandomSeed connect file:/dev/urandom 512
##
## SSL Global Context
##
## All SSL configuration in this context applies both to
## the main server and all SSL-enabled virtual hosts.
##
#
# Some MIME-types for downloading Certificates and CRLs
#
AddType application/x-x509-ca-cert .crt
AddType application/x-pkcs7-crl .crl
# Pass Phrase Dialog:
# Configure the pass phrase gathering process.
# The filtering dialog program (`builtin' is a internal
# terminal dialog) has to provide the pass phrase on stdout.
SSLPassPhraseDialog exec:/usr/share/apache2/ask-for-passphrase
# Inter-Process Session Cache:
# Configure the SSL Session Cache: First the mechanism
# to use and second the expiring timeout (in seconds).
# (The mechanism dbm has known memory leaks and should not be used).
#SSLSessionCache dbm:${APACHE_RUN_DIR}/ssl_scache
SSLSessionCache shmcb:${APACHE_RUN_DIR}/ssl_scache(512000)
SSLSessionCacheTimeout 300
# Semaphore:
# Configure the path to the mutual exclusion semaphore the
# SSL engine uses internally for inter-process synchronization.
# (Disabled by default, the global Mutex directive consolidates by default
# this)
#Mutex file:${APACHE_LOCK_DIR}/ssl_mutex ssl-cache
# SSL Cipher Suite:
# List the ciphers that the client is permitted to negotiate. See the
# ciphers(1) man page from the openssl package for list of all available
# options.
# Enable only secure ciphers:
SSLCipherSuite "{{ apache_mod_ssl_ciphers | join(':') }}"
#SSLOpenSSLConfCmd DHParameters /etc/apache2/ssl/dhparams.pem
# SSL server cipher order preference:
# Use server priorities for cipher algorithm choice.
# Clients may prefer lower grade encryption. You should enable this
# option if you want to enforce stronger encryption, and can afford
# the CPU cost, and did not override SSLCipherSuite in a way that puts
# insecure ciphers first.
# Default: Off
SSLHonorCipherOrder on
# The protocols to enable.
# Available values: all, SSLv3, TLSv1, TLSv1.1, TLSv1.2
# SSL v2 is no longer supported
SSLProtocol {{ apache_mod_ssl_protocols }}
# Allow insecure renegotiation with clients which do not yet support the
# secure renegotiation protocol. Default: Off
#SSLInsecureRenegotiation on
# Whether to forbid non-SNI clients to access name based virtual hosts.
# Default: Off
#SSLStrictSNIVHostCheck On
SSLStaplingCache shmcb:${APACHE_RUN_DIR}/ssl_stcache(512000)
SSLUseStapling on
</IfModule>
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet

View File

@@ -0,0 +1,29 @@
# {{ ansible_managed }}
<IfModule mod_status.c>
# Allow server status reports generated by mod_status,
# with the URL of http://servername/server-status
<Location /server-status>
SetHandler server-status
Require ip 127.0.0.1 ::1 {{ apache_monitoring_ips }}
</Location>
# Keep track of extended status information for each request
ExtendedStatus On
# Determine if mod_status displays the first 63 characters of a request or
# the last 63, assuming the request itself is greater than 63 chars.
# Default: Off
#SeeRequestTail On
<IfModule mod_proxy.c>
# Show Proxy LoadBalancer status in mod_status
ProxyStatus On
</IfModule>
</IfModule>
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet

View File

@@ -0,0 +1,11 @@
# {{ ansible_managed }}
service {
name = "apache"
port = 443
check {
http = "https://localhost/server-status?auto"
interval = "30s"
tlsSkipVerify = true
}
}

View File

@@ -0,0 +1,26 @@
# {{ ansible_managed }}
{% if apache_firewall_public %}
iptables -N apache-in
{% if apache_firewall_public_isolated %}
{% for ip in apache_firewall_acl %}
iptables -A apache-in -s {{ ip }} -j ACCEPT
{% endfor %}
{% for ip in datacenter_global_networks + datacenter_public_networks %}
iptables -A apache-in -s {{ ip }} -j RETURN
{% endfor %}
{% for ip in apache_firewall_drop_dst %}
iptables -A apache-in -d {{ ip }} -j RETURN
{% endfor %}
{% endif %}
iptables -A apache-in -j ACCEPT
iptables -A INPUT -p tcp --dport 80 -m comment --comment "apache-http" -j apache-in
iptables -A INPUT -p tcp --dport 443 -m comment --comment "apache-https" -j apache-in
{% else %}
iptables -A internal-in -p tcp --dport 80 -m comment --comment "apache-http" -j ACCEPT
iptables -A internal-in -p tcp --dport 443 -m comment --comment "apache-https" -j ACCEPT
{% endif %}
iptables -A monitoring-in -p tcp --dport 80 -m comment --comment "apache-http" -j ACCEPT
iptables -A monitoring-in -p tcp --dport 443 -m comment --comment "apache-https" -j ACCEPT

View File

@@ -0,0 +1,19 @@
# {{ ansible_managed }}
{% if apache_firewall_public %}
ip6tables -N apache-in
{% if apache_firewall_public_isolated %}
ip6tables -A apache-in -s fe80::/10 -j RETURN
ip6tables -A apache-in -s fc00::/7 -j RETURN
{% for ip in datacenter_public_ipv6_networks %}
ip6tables -A apache-in -s {{ ip }} -j RETURN
{% endfor %}
{% endif %}
ip6tables -A apache-in -j ACCEPT
ip6tables -A INPUT -p tcp --dport 80 -m comment --comment "apache-http" -j apache-in
ip6tables -A INPUT -p tcp --dport 443 -m comment --comment "apache-https" -j apache-in
{% else %}
ip6tables -A internal-in -p tcp --dport 80 -m comment --comment "apache-http" -j ACCEPT
ip6tables -A internal-in -p tcp --dport 443 -m comment --comment "apache-https" -j ACCEPT
{% endif %}

View File

@@ -0,0 +1,23 @@
/var/log/apache2/*.log {
dateext
dateformat .%Y%m%d
dateyesterday
daily
missingok
rotate 14
compress
delaycompress
notifempty
create 640 root adm
sharedscripts
postrotate
if /etc/init.d/apache2 status > /dev/null ; then \
/etc/init.d/apache2 reload > /dev/null; \
fi;
endscript
prerotate
if [ -d /etc/logrotate.d/httpd-prerotate ]; then \
run-parts /etc/logrotate.d/httpd-prerotate; \
fi; \
endscript
}

View File

@@ -0,0 +1,853 @@
# {{ ansible_managed }}
# ------------------------------------------------------------------------
# OWASP ModSecurity Core Rule Set ver.3.1.0
# Copyright (c) 2006-2018 Trustwave and contributors. All rights reserved.
#
# The OWASP ModSecurity Core Rule Set is distributed under
# Apache Software License (ASL) version 2
# Please see the enclosed LICENSE file for full details.
# ------------------------------------------------------------------------
#
# -- [[ Introduction ]] --------------------------------------------------------
#
# The OWASP ModSecurity Core Rule Set (CRS) is a set of generic attack
# detection rules that provide a base level of protection for any web
# application. They are written for the open source, cross-platform
# ModSecurity Web Application Firewall.
#
# See also:
# https://coreruleset.org/
# https://github.com/SpiderLabs/owasp-modsecurity-crs
# https://www.owasp.org/index.php/Category:OWASP_ModSecurity_Core_Rule_Set_Project
#
#
# -- [[ System Requirements ]] -------------------------------------------------
#
# CRS requires ModSecurity version 2.8.0 or above.
# We recommend to always use the newest ModSecurity version.
#
# The configuration directives/settings in this file are used to control
# the OWASP ModSecurity CRS. These settings do **NOT** configure the main
# ModSecurity settings (modsecurity.conf) such as SecRuleEngine,
# SecRequestBodyAccess, SecAuditEngine, SecDebugLog, and XML processing.
#
# The CRS assumes that modsecurity.conf has been loaded. It is bundled with
# ModSecurity. If you don't have it, you can get it from:
# 2.x: https://raw.githubusercontent.com/SpiderLabs/ModSecurity/v2/master/modsecurity.conf-recommended
# 3.x: https://raw.githubusercontent.com/SpiderLabs/ModSecurity/v3/master/modsecurity.conf-recommended
#
# The order of file inclusion in your webserver configuration should always be:
# 1. modsecurity.conf
# 2. crs-setup.conf (this file)
# 3. rules/*.conf (the CRS rule files)
#
# Please refer to the INSTALL file for detailed installation instructions.
#
#
# -- [[ Mode of Operation: Anomaly Scoring vs. Self-Contained ]] ---------------
#
# The CRS can run in two modes:
#
# -- [[ Anomaly Scoring Mode (default) ]] --
# In CRS3, anomaly mode is the default and recommended mode, since it gives the
# most accurate log information and offers the most flexibility in setting your
# blocking policies. It is also called "collaborative detection mode".
# In this mode, each matching rule increases an 'anomaly score'.
# At the conclusion of the inbound rules, and again at the conclusion of the
# outbound rules, the anomaly score is checked, and the blocking evaluation
# rules apply a disruptive action, by default returning an error 403.
#
# -- [[ Self-Contained Mode ]] --
# In this mode, rules apply an action instantly. This was the CRS2 default.
# It can lower resource usage, at the cost of less flexibility in blocking policy
# and less informative audit logs (only the first detected threat is logged).
# Rules inherit the disruptive action that you specify (i.e. deny, drop, etc).
# The first rule that matches will execute this action. In most cases this will
# cause evaluation to stop after the first rule has matched, similar to how many
# IDSs function.
#
# -- [[ Alert Logging Control ]] --
# In the mode configuration, you must also adjust the desired logging options.
# There are three common options for dealing with logging. By default CRS enables
# logging to the webserver error log (or Event viewer) plus detailed logging to
# the ModSecurity audit log (configured under SecAuditLog in modsecurity.conf).
#
# - To log to both error log and ModSecurity audit log file, use: "log,auditlog"
# - To log *only* to the ModSecurity audit log file, use: "nolog,auditlog"
# - To log *only* to the error log file, use: "log,noauditlog"
#
# Examples for the various modes follow.
# You must leave one of the following options enabled.
# Note that you must specify the same line for phase:1 and phase:2.
#
# Default: Anomaly Scoring mode, log to error log, log to ModSecurity audit log
# - By default, offending requests are blocked with an error 403 response.
# - To change the disruptive action, see RESPONSE-999-EXCEPTIONS.conf.example
# and review section 'Changing the Disruptive Action for Anomaly Mode'.
# - In Apache, you can use ErrorDocument to show a friendly error page or
# perform a redirect: https://httpd.apache.org/docs/2.4/custom-error.html
#
SecDefaultAction "phase:1,log,auditlog,pass"
SecDefaultAction "phase:2,log,auditlog,pass"
# Example: Anomaly Scoring mode, log only to ModSecurity audit log
# - By default, offending requests are blocked with an error 403 response.
# - To change the disruptive action, see RESPONSE-999-EXCEPTIONS.conf.example
# and review section 'Changing the Disruptive Action for Anomaly Mode'.
# - In Apache, you can use ErrorDocument to show a friendly error page or
# perform a redirect: https://httpd.apache.org/docs/2.4/custom-error.html
#
# SecDefaultAction "phase:1,nolog,auditlog,pass"
# SecDefaultAction "phase:2,nolog,auditlog,pass"
# Example: Self-contained mode, return error 403 on blocking
# - In this configuration the default disruptive action becomes 'deny'. After a
# rule triggers, it will stop processing the request and return an error 403.
# - You can also use a different error status, such as 404, 406, et cetera.
# - In Apache, you can use ErrorDocument to show a friendly error page or
# perform a redirect: https://httpd.apache.org/docs/2.4/custom-error.html
#
# SecDefaultAction "phase:1,log,auditlog,deny,status:403"
# SecDefaultAction "phase:2,log,auditlog,deny,status:403"
# Example: Self-contained mode, redirect back to homepage on blocking
# - In this configuration the 'tag' action includes the Host header data in the
# log. This helps to identify which virtual host triggered the rule (if any).
# - Note that this might cause redirect loops in some situations; for example
# if a Cookie or User-Agent header is blocked, it will also be blocked when
# the client subsequently tries to access the homepage. You can also redirect
# to another custom URL.
# SecDefaultAction "phase:1,log,auditlog,redirect:'http://%{request_headers.host}/',tag:'Host: %{request_headers.host}'"
# SecDefaultAction "phase:2,log,auditlog,redirect:'http://%{request_headers.host}/',tag:'Host: %{request_headers.host}'"
#
# -- [[ Paranoia Level Initialization ]] ---------------------------------------
#
# The Paranoia Level (PL) setting allows you to choose the desired level
# of rule checks that will add to your anomaly scores.
#
# With each paranoia level increase, the CRS enables additional rules
# giving you a higher level of security. However, higher paranoia levels
# also increase the possibility of blocking some legitimate traffic due to
# false alarms (also named false positives or FPs). If you use higher
# paranoia levels, it is likely that you will need to add some exclusion
# rules for certain requests and applications receiving complex input.
#
# - A paranoia level of 1 is default. In this level, most core rules
# are enabled. PL1 is advised for beginners, installations
# covering many different sites and applications, and for setups
# with standard security requirements.
# At PL1 you should face FPs rarely. If you encounter FPs, please
# open an issue on the CRS GitHub site and don't forget to attach your
# complete Audit Log record for the request with the issue.
# - Paranoia level 2 includes many extra rules, for instance enabling
# many regexp-based SQL and XSS injection protections, and adding
# extra keywords checked for code injections. PL2 is advised
# for moderate to experienced users desiring more complete coverage
# and for installations with elevated security requirements.
# PL2 comes with some FPs which you need to handle.
# - Paranoia level 3 enables more rules and keyword lists, and tweaks
# limits on special characters used. PL3 is aimed at users experienced
# at the handling of FPs and at installations with a high security
# requirement.
# - Paranoia level 4 further restricts special characters.
# The highest level is advised for experienced users protecting
# installations with very high security requirements. Running PL4 will
# likely produce a very high number of FPs which have to be
# treated before the site can go productive.
#
# Rules in paranoia level 2 or higher will log their PL to the audit log;
# example: [tag "paranoia-level/2"]. This allows you to deduct from the
# audit log how the WAF behavior is affected by paranoia level.
#
# It is important to also look into the variable
# tx.enforce_bodyproc_urlencoded (Enforce Body Processor URLENCODED)
# defined below. Enabling it closes a possible bypass of CRS.
#
# Uncomment this rule to change the default:
#
#SecAction \
# "id:900000,\
# phase:1,\
# nolog,\
# pass,\
# t:none,\
# setvar:tx.paranoia_level=1"
# It is possible to execute rules from a higher paranoia level but not include
# them in the anomaly scoring. This allows you to take a well-tuned system on
# paranoia level 1 and add rules from paranoia level 2 without having to fear
# the new rules would lead to false positives that raise your score above the
# threshold.
# This optional feature is enabled by uncommenting the following rule and
# setting the tx.executing_paranoia_level.
# Technically, rules up to the level defined in tx.executing_paranoia_level
# will be executed, but only the rules up to tx.paranoia_level affect the
# anomaly scores.
# By default, tx.executing_paranoia_level is set to tx.paranoia_level.
# tx.executing_paranoia_level must not be lower than tx.paranoia_level.
#
# Please notice that setting tx.executing_paranoia_level to a higher paranoia
# level results in a performance impact that is equally high as setting
# tx.paranoia_level to said level.
#
#SecAction \
# "id:900001,\
# phase:1,\
# nolog,\
# pass,\
# t:none,\
# setvar:tx.executing_paranoia_level=1"
#
# -- [[ Enforce Body Processor URLENCODED ]] -----------------------------------
#
# ModSecurity selects the body processor based on the Content-Type request
# header. But clients are not always setting the Content-Type header for their
# request body payloads. This will leave ModSecurity with limited vision into
# the payload. The variable tx.enforce_bodyproc_urlencoded lets you force the
# URLENCODED body processor in these situations. This is off by default, as it
# implies a change of the behaviour of ModSecurity beyond CRS (the body
# processor applies to all rules, not only CRS) and because it may lead to
# false positives already on paranoia level 1. However, enabling this variable
# closes a possible bypass of CRS so it should be considered.
#
# Uncomment this rule to change the default:
#
#SecAction \
# "id:900010,\
# phase:1,\
# nolog,\
# pass,\
# t:none,\
# setvar:tx.enforce_bodyproc_urlencoded=1"
#
# -- [[ Anomaly Mode Severity Levels ]] ----------------------------------------
#
# Each rule in the CRS has an associated severity level.
# These are the default scoring points for each severity level.
# These settings will be used to increment the anomaly score if a rule matches.
# You may adjust these points to your liking, but this is usually not needed.
#
# - CRITICAL severity: Anomaly Score of 5.
# Mostly generated by the application attack rules (93x and 94x files).
# - ERROR severity: Anomaly Score of 4.
# Generated mostly from outbound leakage rules (95x files).
# - WARNING severity: Anomaly Score of 3.
# Generated mostly by malicious client rules (91x files).
# - NOTICE severity: Anomaly Score of 2.
# Generated mostly by the protocol rules (92x files).
#
# In anomaly mode, these scores are cumulative.
# So it's possible for a request to hit multiple rules.
#
# (Note: In this file, we use 'phase:1' to set CRS configuration variables.
# In general, 'phase:request' is used. However, we want to make absolutely sure
# that all configuration variables are set before the CRS rules are processed.)
#
#SecAction \
# "id:900100,\
# phase:1,\
# nolog,\
# pass,\
# t:none,\
# setvar:tx.critical_anomaly_score=5,\
# setvar:tx.error_anomaly_score=4,\
# setvar:tx.warning_anomaly_score=3,\
# setvar:tx.notice_anomaly_score=2"
#
# -- [[ Anomaly Mode Blocking Threshold Levels ]] ------------------------------
#
# Here, you can specify at which cumulative anomaly score an inbound request,
# or outbound response, gets blocked.
#
# Most detected inbound threats will give a critical score of 5.
# Smaller violations, like violations of protocol/standards, carry lower scores.
#
# [ At default value ]
# If you keep the blocking thresholds at the defaults, the CRS will work
# similarly to previous CRS versions: a single critical rule match will cause
# the request to be blocked and logged.
#
# [ Using higher values ]
# If you want to make the CRS less sensitive, you can increase the blocking
# thresholds, for instance to 7 (which would require multiple rule matches
# before blocking) or 10 (which would require at least two critical alerts - or
# a combination of many lesser alerts), or even higher. However, increasing the
# thresholds might cause some attacks to bypass the CRS rules or your policies.
#
# [ New deployment strategy: Starting high and decreasing ]
# It is a common practice to start a fresh CRS installation with elevated
# anomaly scoring thresholds (>100) and then lower the limits as your
# confidence in the setup grows. You may also look into the Sampling
# Percentage section below for a different strategy to ease into a new
# CRS installation.
#
# [ Anomaly Threshold / Paranoia Level Quadrant ]
#
# High Anomaly Limit | High Anomaly Limit
# Low Paranoia Level | High Paranoia Level
# -> Fresh Site | -> Experimental Site
# ------------------------------------------------------
# Low Anomaly Limit | Low Anomaly Limit
# Low Paranoia Level | High Paranoia Level
# -> Standard Site | -> High Security Site
#
# Uncomment this rule to change the defaults:
#
#SecAction \
# "id:900110,\
# phase:1,\
# nolog,\
# pass,\
# t:none,\
# setvar:tx.inbound_anomaly_score_threshold=5,\
# setvar:tx.outbound_anomaly_score_threshold=4"
#
# -- [[ Application Specific Rule Exclusions ]] ----------------------------------------
#
# Some well-known applications may undertake actions that appear to be
# malicious. This includes actions such as allowing HTML or Javascript within
# parameters. In such cases the CRS aims to prevent false positives by allowing
# administrators to enable prebuilt, application specific exclusions on an
# application by application basis.
# These application specific exclusions are distinct from the rules that would
# be placed in the REQUEST-900-EXCLUSION-RULES-BEFORE-CRS configuration file as
# they are prebuilt for specific applications. The 'REQUEST-900' file is
# designed for users to add their own custom exclusions. Note, using these
# application specific exclusions may loosen restrictions of the CRS,
# especially if used with an application they weren't designed for. As a result
# they should be applied with care.
# To use this functionality you must specify a supported application. To do so
# uncomment rule 900130. In addition to uncommenting the rule you will need to
# specify which application(s) you'd like to enable exclusions for. Only a
# (very) limited set of applications are currently supported, please use the
# filenames prefixed with 'REQUEST-903' to guide you in your selection.
# Such filenames use the following convention:
# REQUEST-903.9XXX-{APPNAME}-EXCLUSIONS-RULES.conf
#
# It is recommended if you run multiple web applications on your site to limit
# the effects of the exclusion to only the path where the excluded webapp
# resides using a rule similar to the following example:
# SecRule REQUEST_URI "@beginsWith /wordpress/" setvar:tx.crs_exclusions_wordpress=1
#
# Modify and uncomment this rule to select which application:
#
#SecAction \
# "id:900130,\
# phase:1,\
# nolog,\
# pass,\
# t:none,\
# setvar:tx.crs_exclusions_drupal=1,\
# setvar:tx.crs_exclusions_wordpress=1,\
# setvar:tx.crs_exclusions_nextcloud=1,\
# setvar:tx.crs_exclusions_dokuwiki=1,\
# setvar:tx.crs_exclusions_cpanel=1"
#
# -- [[ HTTP Policy Settings ]] ------------------------------------------------
#
# This section defines your policies for the HTTP protocol, such as:
# - allowed HTTP versions, HTTP methods, allowed request Content-Types
# - forbidden file extensions (e.g. .bak, .sql) and request headers (e.g. Proxy)
#
# These variables are used in the following rule files:
# - REQUEST-911-METHOD-ENFORCEMENT.conf
# - REQUEST-912-DOS-PROTECTION.conf
# - REQUEST-920-PROTOCOL-ENFORCEMENT.conf
# HTTP methods that a client is allowed to use.
# Default: GET HEAD POST OPTIONS
# Example: for RESTful APIs, add the following methods: PUT PATCH DELETE
# Example: for WebDAV, add the following methods: CHECKOUT COPY DELETE LOCK
# MERGE MKACTIVITY MKCOL MOVE PROPFIND PROPPATCH PUT UNLOCK
# Uncomment this rule to change the default.
#SecAction \
# "id:900200,\
# phase:1,\
# nolog,\
# pass,\
# t:none,\
# setvar:'tx.allowed_methods=GET HEAD POST OPTIONS'"
# Content-Types that a client is allowed to send in a request.
# Default: application/x-www-form-urlencoded|multipart/form-data|text/xml|\
# application/xml|application/soap+xml|application/x-amf|application/json|\
# application/octet-stream|text/plain
# Uncomment this rule to change the default.
SecAction \
"id:900220,\
phase:1,\
nolog,\
pass,\
t:none,\
setvar:'tx.allowed_request_content_type=application/x-php-serialized-rpc|application/x-www-form-urlencoded|multipart/form-data|text/xml|application/xml|application/soap+xml|application/x-amf|application/json|application/octet-stream|text/plain'"
# Content-Types charsets that a client is allowed to send in a request.
# Default: utf-8|iso-8859-1|iso-8859-15|windows-1252
# Uncomment this rule to change the default.
# Use "|" to separate multiple charsets like in the rule defining
# tx.allowed_request_content_type.
#SecAction \
# "id:900270,\
# phase:1,\
# nolog,\
# pass,\
# t:none,\
# setvar:'tx.allowed_request_content_type_charset=utf-8|iso-8859-1|iso-8859-15|windows-1252'"
# Allowed HTTP versions.
# Default: HTTP/1.0 HTTP/1.1 HTTP/2 HTTP/2.0
# Example for legacy clients: HTTP/0.9 HTTP/1.0 HTTP/1.1 HTTP/2 HTTP/2.0
# Note that some web server versions use 'HTTP/2', some 'HTTP/2.0', so
# we include both version strings by default.
# Uncomment this rule to change the default.
#SecAction \
# "id:900230,\
# phase:1,\
# nolog,\
# pass,\
# t:none,\
# setvar:'tx.allowed_http_versions=HTTP/1.0 HTTP/1.1 HTTP/2 HTTP/2.0'"
# Forbidden file extensions.
# Guards against unintended exposure of development/configuration files.
# Default: .asa/ .asax/ .ascx/ .axd/ .backup/ .bak/ .bat/ .cdx/ .cer/ .cfg/ .cmd/ .com/ .config/ .conf/ .cs/ .csproj/ .csr/ .dat/ .db/ .dbf/ .dll/ .dos/ .htr/ .htw/ .ida/ .idc/ .idq/ .inc/ .ini/ .key/ .licx/ .lnk/ .log/ .mdb/ .old/ .pass/ .pdb/ .pol/ .printer/ .pwd/ .resources/ .resx/ .sql/ .sys/ .vb/ .vbs/ .vbproj/ .vsdisco/ .webinfo/ .xsd/ .xsx/
# Example: .bak/ .config/ .conf/ .db/ .ini/ .log/ .old/ .pass/ .pdb/ .sql/
# Uncomment this rule to change the default.
#SecAction \
# "id:900240,\
# phase:1,\
# nolog,\
# pass,\
# t:none,\
# setvar:'tx.restricted_extensions=.asa/ .asax/ .ascx/ .axd/ .backup/ .bak/ .bat/ .cdx/ .cer/ .cfg/ .cmd/ .com/ .config/ .conf/ .cs/ .csproj/ .csr/ .dat/ .db/ .dbf/ .dll/ .dos/ .htr/ .htw/ .ida/ .idc/ .idq/ .inc/ .ini/ .key/ .licx/ .lnk/ .log/ .mdb/ .old/ .pass/ .pdb/ .pol/ .printer/ .pwd/ .resources/ .resx/ .sql/ .sys/ .vb/ .vbs/ .vbproj/ .vsdisco/ .webinfo/ .xsd/ .xsx/'"
# Forbidden request headers.
# Header names should be lowercase, enclosed by /slashes/ as delimiters.
# Blocking Proxy header prevents 'httpoxy' vulnerability: https://httpoxy.org
# Default: /proxy/ /lock-token/ /content-range/ /translate/ /if/
# Uncomment this rule to change the default.
#SecAction \
# "id:900250,\
# phase:1,\
# nolog,\
# pass,\
# t:none,\
# setvar:'tx.restricted_headers=/proxy/ /lock-token/ /content-range/ /translate/ /if/'"
# File extensions considered static files.
# Extensions include the dot, lowercase, enclosed by /slashes/ as delimiters.
# Used in DoS protection rule. See section "Anti-Automation / DoS Protection".
# Default: /.jpg/ /.jpeg/ /.png/ /.gif/ /.js/ /.css/ /.ico/ /.svg/ /.webp/
# Uncomment this rule to change the default.
#SecAction \
# "id:900260,\
# phase:1,\
# nolog,\
# pass,\
# t:none,\
# setvar:'tx.static_extensions=/.jpg/ /.jpeg/ /.png/ /.gif/ /.js/ /.css/ /.ico/ /.svg/ /.webp/'"
#
# -- [[ HTTP Argument/Upload Limits ]] -----------------------------------------
#
# Here you can define optional limits on HTTP get/post parameters and uploads.
# This can help to prevent application specific DoS attacks.
#
# These values are checked in REQUEST-920-PROTOCOL-ENFORCEMENT.conf.
# Beware of blocking legitimate traffic when enabling these limits.
#
# Block request if number of arguments is too high
# Default: unlimited
# Example: 255
# Uncomment this rule to set a limit.
#SecAction \
# "id:900300,\
# phase:1,\
# nolog,\
# pass,\
# t:none,\
# setvar:tx.max_num_args=255"
# Block request if the length of any argument name is too high
# Default: unlimited
# Example: 100
# Uncomment this rule to set a limit.
#SecAction \
# "id:900310,\
# phase:1,\
# nolog,\
# pass,\
# t:none,\
# setvar:tx.arg_name_length=100"
# Block request if the length of any argument value is too high
# Default: unlimited
# Example: 400
# Uncomment this rule to set a limit.
#SecAction \
# "id:900320,\
# phase:1,\
# nolog,\
# pass,\
# t:none,\
# setvar:tx.arg_length=400"
# Block request if the total length of all combined arguments is too high
# Default: unlimited
# Example: 64000
# Uncomment this rule to set a limit.
#SecAction \
# "id:900330,\
# phase:1,\
# nolog,\
# pass,\
# t:none,\
# setvar:tx.total_arg_length=64000"
# Block request if the file size of any individual uploaded file is too high
# Default: unlimited
# Example: 1048576
# Uncomment this rule to set a limit.
#SecAction \
# "id:900340,\
# phase:1,\
# nolog,\
# pass,\
# t:none,\
# setvar:tx.max_file_size=1048576"
# Block request if the total size of all combined uploaded files is too high
# Default: unlimited
# Example: 1048576
# Uncomment this rule to set a limit.
#SecAction \
# "id:900350,\
# phase:1,\
# nolog,\
# pass,\
# t:none,\
# setvar:tx.combined_file_sizes=1048576"
#
# -- [[ Easing In / Sampling Percentage ]] -------------------------------------
#
# Adding the Core Rule Set to an existing productive site can lead to false
# positives, unexpected performance issues and other undesired side effects.
#
# It can be beneficial to test the water first by enabling the CRS for a
# limited number of requests only and then, when you have solved the issues (if
# any) and you have confidence in the setup, to raise the ratio of requests
# being sent into the ruleset.
#
# Adjust the percentage of requests that are funnelled into the Core Rules by
# setting TX.sampling_percentage below. The default is 100, meaning that every
# request gets checked by the CRS. The selection of requests, which are going
# to be checked, is based on a pseudo random number generated by ModSecurity.
#
# If a request is allowed to pass without being checked by the CRS, there is no
# entry in the audit log (for performance reasons), but an error log entry is
# written. If you want to disable the error log entry, then issue the
# following directive somewhere after the inclusion of the CRS
# (E.g., RESPONSE-999-EXCEPTIONS.conf).
#
# SecRuleUpdateActionById 901150 "nolog"
#
# ATTENTION: If this TX.sampling_percentage is below 100, then some of the
# requests will bypass the Core Rules completely and you lose the ability to
# protect your service with ModSecurity.
#
# Uncomment this rule to enable this feature:
#
#SecAction "id:900400,\
# phase:1,\
# pass,\
# nolog,\
# setvar:tx.sampling_percentage=100"
#
# -- [[ Project Honey Pot HTTP Blacklist ]] ------------------------------------
#
# Optionally, you can check the client IP address against the Project Honey Pot
# HTTPBL (dnsbl.httpbl.org). In order to use this, you need to register to get a
# free API key. Set it here with SecHttpBlKey.
#
# Project Honeypot returns multiple different malicious IP types.
# You may specify which you want to block by enabling or disabling them below.
#
# Ref: https://www.projecthoneypot.org/httpbl.php
# Ref: https://github.com/SpiderLabs/ModSecurity/wiki/Reference-Manual#wiki-SecHttpBlKey
#
# Uncomment these rules to use this feature:
#
#SecHttpBlKey XXXXXXXXXXXXXXXXX
#SecAction "id:900500,\
# phase:1,\
# nolog,\
# pass,\
# t:none,\
# setvar:tx.block_search_ip=1,\
# setvar:tx.block_suspicious_ip=1,\
# setvar:tx.block_harvester_ip=1,\
# setvar:tx.block_spammer_ip=1"
#
# -- [[ GeoIP Database ]] ------------------------------------------------------
#
# There are some rulesets that inspect geolocation data of the client IP address
# (geoLookup). The CRS uses geoLookup to implement optional country blocking.
#
# To use geolocation, we make use of the MaxMind GeoIP database.
# This database is not included with the CRS and must be downloaded.
# You should also update the database regularly, for instance every month.
# The CRS contains a tool to download it to util/geo-location/GeoIP.dat:
# util/upgrade.py --geoip
#
# This product includes GeoLite data created by MaxMind, available from:
# http://www.maxmind.com.
#
# Ref: http://blog.spiderlabs.com/2010/10/detecting-malice-with-modsecurity-geolocation-data.html
# Ref: http://blog.spiderlabs.com/2010/11/detecting-malice-with-modsecurity-ip-forensics.html
#
# Uncomment this rule to use this feature:
#
SecGeoLookupDB /usr/share/GeoIP/GeoIPCity.dat
#
# -=[ Block Countries ]=-
#
# Rules in the IP Reputation file can check the client against a list of high
# risk country codes. These countries have to be defined in the variable
# tx.high_risk_country_codes via their ISO 3166 two-letter country code:
# https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2#Officially_assigned_code_elements
#
# If you are sure that you are not getting any legitimate requests from a given
# country, then you can disable all access from that country via this variable.
# The rule performing the test has the rule id 910100.
#
# This rule requires SecGeoLookupDB to be enabled and the GeoIP database to be
# downloaded (see the section "GeoIP Database" above.)
#
# By default, the list is empty. A list used by some sites was the following:
# setvar:'tx.high_risk_country_codes=UA ID YU LT EG RO BG TR RU PK MY CN'"
#
# Uncomment this rule to use this feature:
#
#SecAction \
# "id:900600,\
# phase:1,\
# nolog,\
# pass,\
# t:none,\
# setvar:'tx.high_risk_country_codes='"
#
# -- [[ Anti-Automation / DoS Protection ]] ------------------------------------
#
# Optional DoS protection against clients making requests too quickly.
#
# When a client is making more than 100 requests (excluding static files) within
# 60 seconds, this is considered a 'burst'. After two bursts, the client is
# blocked for 600 seconds.
#
# Requests to static files are not counted towards DoS; they are listed in the
# 'tx.static_extensions' setting, which you can change in this file (see
# section "HTTP Policy Settings").
#
# For a detailed description, see rule file REQUEST-912-DOS-PROTECTION.conf.
#
# Uncomment this rule to use this feature:
#
#SecAction \
# "id:900700,\
# phase:1,\
# nolog,\
# pass,\
# t:none,\
# setvar:'tx.dos_burst_time_slice=60',\
# setvar:'tx.dos_counter_threshold=100',\
# setvar:'tx.dos_block_timeout=600'"
#
# -- [[ Check UTF-8 encoding ]] ------------------------------------------------
#
# The CRS can optionally check request contents for invalid UTF-8 encoding.
# We only want to apply this check if UTF-8 encoding is actually used by the
# site; otherwise it will result in false positives.
#
# Uncomment this rule to use this feature:
#
#SecAction \
# "id:900950,\
# phase:1,\
# nolog,\
# pass,\
# t:none,\
# setvar:tx.crs_validate_utf8_encoding=1"
#
# -- [[ Blocking Based on IP Reputation ]] ------------------------------------
#
# Blocking based on reputation is permanent in the CRS. Unlike other rules,
# which look at the indvidual request, the blocking of IPs is based on
# a persistent record in the IP collection, which remains active for a
# certain amount of time.
#
# There are two ways an individual client can become flagged for blocking:
# - External information (RBL, GeoIP, etc.)
# - Internal information (Core Rules)
#
# The record in the IP collection carries a flag, which tags requests from
# individual clients with a flag named IP.reput_block_flag.
# But the flag alone is not enough to have a client blocked. There is also
# a global switch named tx.do_reput_block. This is off by default. If you set
# it to 1 (=On), requests from clients with the IP.reput_block_flag will
# be blocked for a certain duration.
#
# Variables
# ip.reput_block_flag Blocking flag for the IP collection record
# ip.reput_block_reason Reason (= rule message) that caused to blocking flag
# tx.do_reput_block Switch deciding if we really block based on flag
# tx.reput_block_duration Setting to define the duration of a block
#
# It may be important to know, that all the other core rules are skipped for
# requests, when it is clear that they carry the blocking flag in question.
#
# Uncomment this rule to use this feature:
#
#SecAction \
# "id:900960,\
# phase:1,\
# nolog,\
# pass,\
# t:none,\
# setvar:tx.do_reput_block=1"
#
# Uncomment this rule to change the blocking time:
# Default: 300 (5 minutes)
#
#SecAction \
# "id:900970,\
# phase:1,\
# nolog,\
# pass,\
# t:none,\
# setvar:tx.reput_block_duration=300"
#
# -- [[ Collection timeout ]] --------------------------------------------------
#
# Set the SecCollectionTimeout directive from the ModSecurity default (1 hour)
# to a lower setting which is appropriate to most sites.
# This increases performance by cleaning out stale collection (block) entries.
#
# This value should be greater than or equal to:
# tx.reput_block_duration (see section "Blocking Based on IP Reputation") and
# tx.dos_block_timeout (see section "Anti-Automation / DoS Protection").
#
# Ref: https://github.com/SpiderLabs/ModSecurity/wiki/Reference-Manual#wiki-SecCollectionTimeout
# Please keep this directive uncommented.
# Default: 600 (10 minutes)
SecCollectionTimeout 600
#
# -- [[ Debug Mode ]] ----------------------------------------------------------
#
# To enable rule development and debugging, CRS has an optional debug mode
# that does not block a request, but instead sends detection information
# back to the HTTP client.
#
# This functionality is currently only supported with the Apache web server.
# The Apache mod_headers module is required.
#
# In debug mode, the webserver inserts "X-WAF-Events" / "X-WAF-Score"
# response headers whenever a debug client makes a request. Example:
#
# # curl -v 'http://192.168.1.100/?foo=../etc/passwd'
# X-WAF-Events: TX:930110-OWASP_CRS/WEB_ATTACK/DIR_TRAVERSAL-REQUEST_URI,
# TX:930120-OWASP_CRS/WEB_ATTACK/FILE_INJECTION-ARGS:foo,
# TX:932160-OWASP_CRS/WEB_ATTACK/RCE-ARGS:foo
# X-WAF-Score: Total=15; sqli=0; xss=0; rfi=0; lfi=10; rce=5; php=0; http=0; ses=0
#
# To enable debug mode, include the RESPONSE-981-DEBUG.conf file.
# This file resides in a separate folder, as it is not compatible with
# nginx and IIS.
#
# You must specify the source IP address/network where you will be running the
# tests from. The source IP will BYPASS all CRS blocking, and will be sent the
# response headers as specified above. Be careful to only list your private
# IP addresses/networks here.
#
# Tip: for regression testing of CRS or your own ModSecurity rules, you may
# be interested in using the OWASP CRS regression testing suite instead.
# View the file util/regression-tests/README for more information.
#
# Uncomment these rules, filling in your CRS path and the source IP address,
# to enable debug mode:
#
#Include /usr/share/modsecurity-crs/util/debug/RESPONSE-981-DEBUG.conf
#SecRule REMOTE_ADDR "@ipMatch 192.168.1.100" \
# "id:900980,\
# phase:1,\
# nolog,\
# pass,\
# t:none,\
# ctl:ruleEngine=DetectionOnly,\
# setvar:tx.crs_debug_mode=1"
#
# -- [[ End of setup ]] --------------------------------------------------------
#
# The CRS checks the tx.crs_setup_version variable to ensure that the setup
# has been loaded. If you are not planning to use this setup template,
# you must manually set the tx.crs_setup_version variable before including
# the CRS rules/* files.
#
# The variable is a numerical representation of the CRS version number.
# E.g., v3.0.0 is represented as 300.
#
SecAction \
"id:900990,\
phase:1,\
nolog,\
pass,\
t:none,\
setvar:tx.crs_setup_version=310"
# -- [[ Customization ]] -------------------------------------------------------
# triggers on user.profile for google login urls
SecRuleRemoveById 930120

View File

@@ -0,0 +1,230 @@
# {{ ansible_managed }}
# -- Rule engine initialization ----------------------------------------------
# Enable ModSecurity, attaching it to every transaction. Use detection
# only to start with, because that minimises the chances of post-installation
# disruption.
#
SecRuleEngine {{ 'On' if apache_mod_security_enabled else 'DetectionOnly' }}
# -- Request body handling ---------------------------------------------------
# Allow ModSecurity to access request bodies. If you don't, ModSecurity
# won't be able to see any POST parameters, which opens a large security
# hole for attackers to exploit.
#
SecRequestBodyAccess On
# Enable XML request body parser.
# Initiate XML Processor in case of xml content-type
#
SecRule REQUEST_HEADERS:Content-Type "(?:application(?:/soap\+|/)|text/)xml" \
"id:'200000',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=XML"
# Enable JSON request body parser.
# Initiate JSON Processor in case of JSON content-type; change accordingly
# if your application does not use 'application/json'
#
SecRule REQUEST_HEADERS:Content-Type "application/json" \
"id:'200001',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON"
# Maximum request body size we will accept for buffering. If you support
# file uploads then the value given on the first line has to be as large
# as the largest file you are willing to accept. The second value refers
# to the size of data, with files excluded. You want to keep that value as
# low as practical.
#
SecRequestBodyLimit 13107200
SecRequestBodyNoFilesLimit 131072
# Store up to 128 KB of request body data in memory. When the multipart
# parser reachers this limit, it will start using your hard disk for
# storage. That is slow, but unavoidable.
#
SecRequestBodyInMemoryLimit 131072
# What do do if the request body size is above our configured limit.
# Keep in mind that this setting will automatically be set to ProcessPartial
# when SecRuleEngine is set to DetectionOnly mode in order to minimize
# disruptions when initially deploying ModSecurity.
#
SecRequestBodyLimitAction Reject
# Verify that we've correctly processed the request body.
# As a rule of thumb, when failing to process a request body
# you should reject the request (when deployed in blocking mode)
# or log a high-severity alert (when deployed in detection-only mode).
#
SecRule REQBODY_ERROR "!@eq 0" \
"id:'200002', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2"
# By default be strict with what we accept in the multipart/form-data
# request body. If the rule below proves to be too strict for your
# environment consider changing it to detection-only. You are encouraged
# _not_ to remove it altogether.
#
SecRule MULTIPART_STRICT_ERROR "!@eq 0" \
"id:'200003',phase:2,t:none,log,deny,status:400, \
msg:'Multipart request body failed strict validation: \
PE %{REQBODY_PROCESSOR_ERROR}, \
BQ %{MULTIPART_BOUNDARY_QUOTED}, \
BW %{MULTIPART_BOUNDARY_WHITESPACE}, \
DB %{MULTIPART_DATA_BEFORE}, \
DA %{MULTIPART_DATA_AFTER}, \
HF %{MULTIPART_HEADER_FOLDING}, \
LF %{MULTIPART_LF_LINE}, \
SM %{MULTIPART_MISSING_SEMICOLON}, \
IQ %{MULTIPART_INVALID_QUOTING}, \
IP %{MULTIPART_INVALID_PART}, \
IH %{MULTIPART_INVALID_HEADER_FOLDING}, \
FL %{MULTIPART_FILE_LIMIT_EXCEEDED}'"
# Did we see anything that might be a boundary?
#
SecRule MULTIPART_UNMATCHED_BOUNDARY "!@eq 0" \
"id:'200004',phase:2,t:none,log,deny,msg:'Multipart parser detected a possible unmatched boundary.'"
# PCRE Tuning
# We want to avoid a potential RegEx DoS condition
#
SecPcreMatchLimit 500000
SecPcreMatchLimitRecursion 500000
# Some internal errors will set flags in TX and we will need to look for these.
# All of these are prefixed with "MSC_". The following flags currently exist:
#
# MSC_PCRE_LIMITS_EXCEEDED: PCRE match limits were exceeded.
#
SecRule TX:/^MSC_/ "!@streq 0" \
"id:'200005',phase:2,t:none,deny,msg:'ModSecurity internal error flagged: %{MATCHED_VAR_NAME}'"
# -- Response body handling --------------------------------------------------
# Allow ModSecurity to access response bodies.
# You should have this directive enabled in order to identify errors
# and data leakage issues.
#
# Do keep in mind that enabling this directive does increases both
# memory consumption and response latency.
#
SecResponseBodyAccess On
# Which response MIME types do you want to inspect? You should adjust the
# configuration below to catch documents but avoid static files
# (e.g., images and archives).
#
SecResponseBodyMimeType text/plain text/html text/xml
# Buffer response bodies of up to 512 KB in length.
SecResponseBodyLimit 524288
# What happens when we encounter a response body larger than the configured
# limit? By default, we process what we have and let the rest through.
# That's somewhat less secure, but does not break any legitimate pages.
#
SecResponseBodyLimitAction ProcessPartial
# -- Filesystem configuration ------------------------------------------------
# The location where ModSecurity stores temporary files (for example, when
# it needs to handle a file upload that is larger than the configured limit).
#
# This default setting is chosen due to all systems have /tmp available however,
# this is less than ideal. It is recommended that you specify a location that's private.
#
SecTmpDir /tmp/
# The location where ModSecurity will keep its persistent data. This default setting
# is chosen due to all systems have /tmp available however, it
# too should be updated to a place that other users can't access.
#
SecDataDir /tmp/
# -- File uploads handling configuration -------------------------------------
# The location where ModSecurity stores intercepted uploaded files. This
# location must be private to ModSecurity. You don't want other users on
# the server to access the files, do you?
#
#SecUploadDir /opt/modsecurity/var/upload/
# By default, only keep the files that were determined to be unusual
# in some way (by an external inspection script). For this to work you
# will also need at least one file inspection rule.
#
#SecUploadKeepFiles RelevantOnly
# Uploaded files are by default created with permissions that do not allow
# any other user to access them. You may need to relax that if you want to
# interface ModSecurity to an external program (e.g., an anti-virus).
#
#SecUploadFileMode 0600
# -- Debug log configuration -------------------------------------------------
# The default debug log configuration is to duplicate the error, warning
# and notice messages from the error log.
#
#SecDebugLog /opt/modsecurity/var/log/debug.log
#SecDebugLogLevel 3
# -- Audit log configuration -------------------------------------------------
# Log the transactions that are marked by a rule, as well as those that
# trigger a server error (determined by a 5xx or 4xx, excluding 404,
# level response status codes).
#
SecAuditEngine RelevantOnly
SecAuditLogRelevantStatus "^(?:5|4(?!04))"
# Log everything we know about a transaction.
SecAuditLogParts ABDEFHIJZ
# Use a single file for logging. This is much easier to look at, but
# assumes that you will use the audit log only ocassionally.
#
SecAuditLogType Serial
SecAuditLogFormat JSON
SecAuditLog /var/log/apache2/modsec_audit.log
#SecAuditLog "|/usr/bin/socat -u - tcp:127.0.0.1:5172"
# Specify the path for concurrent audit logging.
#SecAuditLogStorageDir /opt/modsecurity/var/audit/
# -- Miscellaneous -----------------------------------------------------------
# Use the most commonly used application/x-www-form-urlencoded parameter
# separator. There's probably only one application somewhere that uses
# something else so don't expect to change this value.
#
SecArgumentSeparator &
# Settle on version 0 (zero) cookies, as that is what most applications
# use. Using an incorrect cookie version may open your installation to
# evasion attacks (against the rules that examine named cookies).
#
SecCookieFormat 0
# Specify your Unicode Code Point.
# This mapping is used by the t:urlDecodeUni transformation function
# to properly map encoded data to your language. Properly setting
# these directives helps to reduce false positives and negatives.
#
SecUnicodeMapFile unicode.mapping 20127
# Improve the quality of ModSecurity by sharing information about your
# current ModSecurity version and dependencies versions.
# The following information will be shared: ModSecurity version,
# Web Server version, APR version, PCRE version, Lua version, Libxml2
# version, Anonymous unique id for host.
SecStatusEngine On

View File

@@ -0,0 +1,4 @@
# {{ ansible_managed }}
[Service]
PrivateTmp=false

View File

@@ -0,0 +1,21 @@
---
apache_phpfpm_php: "{{ 'php7.4' if ansible_distribution_release == 'focal' else 'php7.4' }}"
apache_phpfpm_etc_dir: "{{ '/etc/php/7.4/fpm' if ansible_distribution_release == 'focal' else '/etc/php/7.4/fpm' }}"
apache_phpfpm_max_workers: 30
apache_phpfpm_timeout: 120
apache_phpfpm_php_settings:
short_open_tag: on
display_errors: off
apache_phpfpm_php_admin_settings:
log_errors: on
error_log: /var/log/php-fpm.$pool.log
memory_limit: 512M
open_basedir: /srv/www:/var/www:/opt:/usr/share:/var/lib/{{ apache_phpfpm_php }}:/var/lib/php:/dev:/tmp:/var/log/kc:/var/spool/asterisk
apache_phpfpm_xcache_size: 128M

View File

@@ -0,0 +1,5 @@
---
- name: Reload PHP-FPM
service: name={{ apache_phpfpm_php }}-fpm state=reloaded

View File

@@ -0,0 +1,4 @@
---
dependencies:
- apache

View File

@@ -0,0 +1,65 @@
---
- name: Install PHP packages
apt:
pkg:
- "{{ apache_phpfpm_php }}-fpm"
- php-apcu
# check_php-fpm nagios plugin dependencies:
- libany-moose-perl
- libjson-perl
- libjson-xs-perl
state: present
tags: packages
- name: Disable Apache modules
apache2_module: name="{{ item }}" state=absent force=yes
with_items:
- "{{ apache_phpfpm_php }}"
notify: Restart Apache
tags: configs
- name: Enable Apache modules
apache2_module: name="{{ item }}" state=present force=yes
with_items:
- proxy_fcgi
notify: Restart Apache
tags: configs
- name: Ensure mod-php is not installed
apt:
pkg:
- libapache2-mod-{{ apache_phpfpm_php }}
- "{{ apache_phpfpm_php }}-cgi"
state: absent
purge: yes
notify: Restart Apache
tags: packages
- name: Install Apache other configs
template: src="etc_apache2_conf-available_php-fpm.conf.j2" dest="/etc/apache2/conf-available/{{ apache_phpfpm_php }}-fpm.conf"
notify: Reload Apache
tags: configs
- name: Install PHP-FPM pool config
template: src=etc_php_fpm_pool.d_www.conf.j2 dest={{ apache_phpfpm_etc_dir }}/pool.d/www.conf
notify: Reload PHP-FPM
tags: configs
- name: Install the FGCI client script
template: src=usr_local_bin_fcgi-client dest=/usr/local/bin/fcgi-client mode=0755
- name: Enable PHP-FPM
file: dest=/etc/apache2/conf-enabled/{{ apache_phpfpm_php }}-fpm.conf src=../conf-available/{{ apache_phpfpm_php }}-fpm.conf state=link
notify: Reload Apache
tags: configs
- name: Ensure PHP-FPM is running
service: name={{ apache_phpfpm_php }}-fpm state=started enabled=yes
tags: configs
- name: Register the php-fpm service in Consul
template: dest=/etc/consul.d/service-php-fpm.hcl src=etc_consul.d_service-php-fpm.hcl.j2
when: apache_consul_service
notify: Reload consul
tags: configs

View File

@@ -0,0 +1,12 @@
# {{ ansible_managed }}
<Proxy "unix:/run/php/{{ apache_phpfpm_php }}-fpm.sock|fcgi://{{ apache_phpfpm_php }}-fpm">
ProxySet max={{ apache_phpfpm_max_workers // 2 - 1 }}
ProxySet timeout={{ apache_phpfpm_timeout }}
ProxySet retry=0
</Proxy>
<FilesMatch "\.php$">
SetEnvIf ^Authorization$ "(.+)" HTTP_AUTHORIZATION=$1
SetHandler "proxy:fcgi://{{ apache_phpfpm_php }}-fpm"
</FilesMatch>

View File

@@ -0,0 +1,6 @@
# {{ ansible_managed }}
service {
name = "php-fpm"
port = 443
}

View File

@@ -0,0 +1,5 @@
# {{ ansible_managed }}
ARGS= \
--phpfpm.socket-directories=/run/php \
--phpfpm.status-path=/_fpm/status

View File

@@ -0,0 +1,2 @@
# {{ ansible_managed }}
command[check_php-fpm]={{ nagios_nrpe_tools_dir }}/plugins/check_php-fpm -s /run/php/{{ apache_phpfpm_php }}-fpm.sock -w active_workers:{{ (apache_phpfpm_max_workers * 80 / 100)|int }} -c active_workers:{{ (apache_phpfpm_max_workers * 90 / 100)|int }}

View File

@@ -0,0 +1,85 @@
; {{ ansible_managed }}
[www]
;prefix = /path/to/pools/$pool
user = www-data
group = www-data
listen = /run/php/{{ apache_phpfpm_php }}-fpm.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
;listen.allowed_clients = 127.0.0.1
; process.priority = -19
pm = dynamic
pm.max_children = {{ apache_phpfpm_max_workers }}
pm.start_servers = 3
pm.min_spare_servers = 2
pm.max_spare_servers = 7
;pm.process_idle_timeout = 10s
pm.max_requests = {{ apache_phpfpm_max_requests | default(50000) }}
pm.status_path = /_fpm/status
ping.path = /_fpm/ping
ping.response = pong
;access.log = /var/log/{{ apache_phpfpm_php }}-fpm.$pool.access.log
;access.format = "%R - %u %t \"%m %r%Q%q\" %s %f %{mili}d %{kilo}M %C%%"
;slowlog = /var/log/{{ apache_phpfpm_php }}-fpm.$pool.slow.log
;request_slowlog_timeout = 10s
;request_terminate_timeout = 0
;rlimit_files = 1024
;rlimit_core = 0
;chroot =
chdir = /
;catch_workers_output = yes
;clear_env = no
;security.limit_extensions = .php .php3 .php4 .php5 .php7
;env[HOSTNAME] = $HOSTNAME
;env[PATH] = /usr/local/bin:/usr/bin:/bin
;env[TMP] = /tmp
;env[TMPDIR] = /tmp
;env[TEMP] = /tmp
; Additional php.ini defines, specific to this pool of workers. These settings
; overwrite the values previously defined in the php.ini. The directives are the
; same as the PHP SAPI:
; php_value/php_flag - you can set classic ini defines which can
; be overwritten from PHP call 'ini_set'.
; php_admin_value/php_admin_flag - these directives won't be overwritten by
; PHP call 'ini_set'
; For php_*flag, valid values are on, off, 1, 0, true, false, yes or no.
; Defining 'extension' will load the corresponding shared extension from
; extension_dir. Defining 'disable_functions' or 'disable_classes' will not
; overwrite previously defined php.ini values, but will append the new value
; instead.
; Note: path INI options can be relative and will be expanded with the prefix
; (pool, global or /usr)
{% for key, value in apache_phpfpm_php_admin_settings|dictsort %}
{% if value in (True,False) %}
php_admin_flag[{{ key }}] = {{ 'on' if value else 'off' }}
{% else %}
php_admin_value[{{ key }}] = {{ value }}
{% endif %}
{% endfor %}
{% for key, value in apache_phpfpm_php_settings|dictsort %}
{% if value in (True,False) %}
php_flag[{{ key }}] = {{ 'on' if value else 'off' }}
{% else %}
php_value[{{ key }}] = {{ value }}
{% endif %}
{% endfor %}

View File

@@ -0,0 +1,88 @@
; {{ ansible_managed }}
; configuration for php Xcache module
[xcache-common]
;; non-Windows example:
extension = xcache.so
;; Windows example:
; extension = php_xcache.dll
[xcache.admin]
xcache.admin.enable_auth = On
; Configure this to use admin pages
; xcache.admin.user = "mOo"
; xcache.admin.pass = md5($your_password)
; xcache.admin.pass = ""
xcache.admin.user = "admin"
xcache.admin.pass = "726be9b7e6dea1ed28c70800d68be36c"
[xcache]
; ini only settings, all the values here is default unless explained
; select low level shm implemenation
xcache.shm_scheme = "mmap"
; to disable: xcache.size=0
; to enable : xcache.size=64M etc (any size > 0) and your system mmap allows
xcache.size = {{ apache_phpfpm_xcache_size }}
; set to cpu count (cat /proc/cpuinfo |grep -c processor)
xcache.count = 2
; just a hash hints, you can always store count(items) > slots
xcache.slots = 8K
; ttl of the cache item, 0=forever
xcache.ttl = 0
; interval of gc scanning expired items, 0=no scan, other values is in seconds
xcache.gc_interval = 0
; same as aboves but for variable cache
xcache.var_size = 64M
xcache.var_count = 1
xcache.var_slots = 8K
; default value for $ttl parameter of xcache_*() functions
xcache.var_ttl = 0
; hard limit ttl that cannot be exceed by xcache_*() functions. 0=unlimited
xcache.var_maxttl = 0
xcache.var_gc_interval = 300
; mode:0, const string specified by xcache.var_namespace
; mode:1, $_SERVER[xcache.var_namespace]
; mode:2, uid or gid (specified by xcache.var_namespace)
xcache.var_namespace_mode = 0
xcache.var_namespace = ""
; N/A for /dev/zero
xcache.readonly_protection = Off
; for *nix, xcache.mmap_path is a file path, not directory. (auto create/overwrite)
; Use something like "/tmp/xcache" instead of "/dev/*" if you want to turn on ReadonlyProtection
; different process group of php won't share the same /tmp/xcache
; for win32, xcache.mmap_path=anonymous map name, not file path
xcache.mmap_path = "/dev/zero"
; Useful when XCache crash. leave it blank(disabled) or "/tmp/phpcore/" (writable by php)
xcache.coredump_directory = ""
; Windows only. leave it as 0 (default) until you're told by XCache dev
xcache.coredump_type = 0
; disable cache after crash
xcache.disable_on_crash = Off
; enable experimental documented features for each release if available
xcache.experimental = Off
; per request settings. can ini_set, .htaccess etc
xcache.cacher = On
xcache.stat = On
xcache.optimizer = Off
[xcache.coverager]
; enabling this feature will impact performance
; enabled only if xcache.coverager == On && xcache.coveragedump_directory == "non-empty-value"
; per request settings. can ini_set, .htaccess etc
; enable coverage data collecting and xcache_coverager_start/stop/get/clean() functions
xcache.coverager = Off
xcache.coverager_autostart = On
; set in php ini file only
; make sure it's readable (open_basedir is checked) by coverage viewer script
xcache.coveragedump_directory = ""

View File

@@ -0,0 +1,46 @@
#!/usr/bin/perl
use strict;
use warnings;
use Pod::Usage;
use Getopt::Long;
use IO::Socket;
use IO::Socket::UNIX;
use lib '/usr/local/lib/nagios/plugins';
use FCGI::Client;
GetOptions(
'h|help' => \my $help,
) or pod2usage();
pod2usage() if $help;
pod2usage() if @ARGV < 2;
my ($fcgi_file, $uri, $query_string) = @ARGV;
my $sock = IO::Socket::UNIX->new(
Type => SOCK_STREAM(),
Peer => $fcgi_file
) or die $!;
my $client = FCGI::Client::Connection->new( sock => $sock );
my ( $stdout, $stderr ) = $client->request(
+{
REQUEST_METHOD => 'GET',
REQUEST_URI => $uri,
SCRIPT_FILENAME => "/a/b/c$uri",
SCRIPT_NAME => $uri,
QUERY_STRING => $query_string || '',
},
''
);
print STDERR $stderr if $stderr;
print $stdout;
__END__
=head1 NAME
fcgi-client -
=head1 SYNOPSIS
$ fcgi-client foo.fcgi URI [foo=bar&hoge=fuga]

View File

@@ -0,0 +1,19 @@
---
consul_version: 1.8.5
consul_url: https://releases.hashicorp.com/consul/{{ consul_version }}/consul_{{ consul_version }}_{{ ansible_system|lower }}_{{ ansible_userspace_architecture|replace('x86_64', 'amd64') }}.zip
consul_data_dir: /opt/consul
consul_config_dir: /etc/consul.d
consul_server: true
consul_bootstrap_expect: 2
consul_wan_peers: []
consul_encrypt_key: eRhnp22+c0bkV0wPolk6Mw==
consul_expose_apis: no
consul_client_addr: "{{ '0.0.0.0' if consul_expose_apis else '127.0.0.1' }}"
consul_stub_mode: no
consul_dns_forwarders: []
consul_firewall: yes

View File

@@ -0,0 +1,7 @@
---
- name: Restart consul
service: name=consul state=restarted
- name: Reload consul
service: name=consul state=reloaded

View File

@@ -0,0 +1,6 @@
---
dependencies:
- role: firewall
when: consul_firewall
- network

122
roles/consul/tasks/main.yml Normal file
View File

@@ -0,0 +1,122 @@
---
- name: Ensure the consul user exists
user:
name: consul
home: '{{ consul_data_dir }}'
system: yes
groups: ssl-cert
append: yes
shell: /bin/false
createhome: no
state: present
tags: packages
- name: Ensure the consul config dir exists
file:
dest: /etc/consul.d
owner: root
group: consul
mode: 0750
state: directory
tags: packages
- name: Ensure the consul data dir exists
file:
dest: /opt/consul
owner: consul
group: consul
mode: 0750
state: directory
tags: packages
- name: Remove old consul config
file:
dest: /etc/consul.d/00-base_config.json
state: absent
tags: configs
- name: Install consul config
template:
dest: /etc/consul.d/00-base_config.hcl
src: etc_consul.d_00-base_config.hcl.j2
#validate: 'consul validate %s'
mode: 0640
owner: root
group: consul
notify: Restart consul
tags:
- configs
- consul.conf
- name: Install consul service config
template:
dest: /etc/default/consul
src: etc_default_consul.j2
when: not consul_stub_mode
notify: Restart consul
tags: configs
- name: Install consul service
template:
dest: /etc/systemd/system/consul.service
src: etc_systemd_system_consul.service.j2
when: not consul_stub_mode
notify: Restart consul
tags: configs
- name: Enable the consul service
systemd:
name: consul
state: "{{ 'started' if not consul_stub_mode else 'stopped' }}"
enabled: "{{ not consul_stub_mode }}"
daemon_reload: yes
when: not consul_stub_mode
tags: configs
- name: Remove the master token if present
lineinfile:
dest: /root/.bashrc
regexp: '^export CONSUL_HTTP_TOKEN=.*'
state: absent
when: consul_acl_master_token is defined and consul_acl_master_token and not consul_stub_mode
tags: configs
- name: Install packages needed by consul-tag
apt:
pkg:
- python3
- python3-requests
state: present
when: not consul_stub_mode
tags: consul-tag
- name: Install consul-tag
template:
dest: /usr/local/bin/consul-tag
src: usr_local_bin_consul-tag.j2
mode: 0755
owner: root
group: root
when: not consul_stub_mode
tags: consul-tag
- name: Remove old firewall config
file: dest=/etc/firewall/rules-v4.d/28_consul.sh state=absent
when: consul_firewall and not consul_stub_mode
notify: Restart firewall
tags:
- configs
- firewall
- name: Install the consul firewall config
template:
dest: /etc/firewall/rules-v4.d/78_consul.sh
src: etc_firewall_rules-v4.d_78_consul.sh.j2
mode: 0600
when: consul_firewall
notify: Restart firewall
tags:
- configs
- firewall

View File

@@ -0,0 +1,83 @@
# {{ ansible_managed }}
# Logging
enable_syslog = true
log_level = "INFO"
disable_update_check = true
# Basics
data_dir = "{{ consul_data_dir }}"
datacenter = "{{ datacenter_id }}"
server = {{ 'false' if consul_server else 'true' }}
ui = true
# Network
{% if consul_bootstrap_expect > 0 %}
encrypt = "{{ consul_encrypt_key }}"
{% endif %}
client_addr = "{{ consul_client_addr }}"
bind_addr = "{{ network_private_ip }}"
advertise_addr = "{{ network_private_ip }}"
retry_join = [
{% for peer in consul_servers if peer != ansible_hostname and hostvars[peer].datacenter_id == datacenter_id %}
"{{ hostvars[peer].network_private_ip }}"{{ ',' if not loop.last else '' }}
{% endfor %}
]
{% if consul_server %}
{% if consul_bootstrap_expect > 0 %}
bootstrap_expect = {{ consul_bootstrap_expect }}
{% endif %}
rejoin_after_leave = true
retry_join_wan = [
{% for peer in consul_servers if hostvars[peer].datacenter_id != datacenter_id %}
"{{ hostvars[peer].network_private_ip }}"{{ ',' if not loop.last else '' }}
{% endfor %}
]
{% endif %}
# TLS
#ports {
# https = 8501
#}
#key_file = "/etc/letsencrypt/live/{{ ansible_hostname }}.maruntiel.net/privkey1.pem"
#cert_file = "/etc/letsencrypt/live/{{ ansible_hostname }}.maruntiel.net/fullchain1.pem"
#ca_file = "/etc/letsencrypt/live/{{ ansible_hostname }}.maruntiel.net/chain1.pem"
#verify_incoming = true
#verify_outgoing = true
#tls_min_version = "tls12"
# Features
enable_script_checks = true
disable_remote_exec = true
# ACLs
#{% if consul_acl_datacenter is defined and consul_acl_datacenter %}
#acl_datacenter = "{{ consul_acl_datacenter }}"
#acl_default_policy = "deny"
#acl_down_policy = "extend-cache"
#acl_agent_token = "{{ consul_acl_agent_token }}"
#acl_token = "{{ consul_acl_token }}"
#{% if datacenter_id != consul_acl_datacenter %}
#acl_replication_token = "{{ consul_acl_replication_token | default(consul_acl_master_token) }}"
#{% endif %}
#{% endif %}
# DNS
dns_config {
node_ttl = "60s"
service_ttl {
"*" = "15s"
}
}
# Metadata
node_meta {
architecture = "{{ ansible_userspace_architecture }}"
product_name = "{{ ansible_system_vendor|replace(' Inc.', '') }} {{ ansible_product_name }}"
virtualization_role = "{{ ansible_virtualization_role }}"
}
# Consul Stats
telemetry {
disable_hostname = true
}

View File

@@ -0,0 +1,5 @@
# {{ ansible_managed }}
{% if consul_ui_beta|default(False) %}
ui_config=enable
{% endif %}

View File

@@ -0,0 +1,25 @@
# {{ ansible_managed }}
{% if not consul_stub_mode %}
{% if consul_server %}
iptables -A internal-in -p tcp --dport 8300:8302 -m comment --comment "consul" -j ACCEPT
iptables -A internal-in -p udp --dport 8300:8302 -m comment --comment "consul" -j ACCEPT
{% else %}
{% for ip in datacenter_local_networks %}
iptables -A internal-in -s {{ ip }} -p tcp --dport 8300:8302 -m comment --comment "consul" -j ACCEPT
iptables -A internal-in -s {{ ip }} -p udp --dport 8300:8302 -m comment --comment "consul" -j ACCEPT
{% endfor %}
{% endif %}
{% if consul_expose_apis %}
iptables -A internal-in -p tcp --dport 8500:8501 -m comment --comment "consul-http" -j ACCEPT
iptables -A internal-in -p tcp --dport 8600 -m comment --comment "consul-dns" -j ACCEPT
iptables -A internal-in -p udp --dport 8600 -m comment --comment "consul-dns" -j ACCEPT
{% endif %}
iptables -A internal-out -p tcp --dport 8300:8302 -m comment --comment "consul" -j ACCEPT
iptables -A internal-out -p udp --dport 8300:8302 -m comment --comment "consul" -j ACCEPT
iptables -A internal-out -p tcp --dport 8500:8501 -m comment --comment "consul-http" -j ACCEPT
iptables -A internal-out -p tcp --dport 8600 -m comment --comment "consul-dns" -j ACCEPT
iptables -A internal-out -p udp --dport 8600 -m comment --comment "consul-dns" -j ACCEPT
{% endif %}

View File

@@ -0,0 +1,20 @@
# {{ ansible_managed }}
[Unit]
Description=Consul Agent
Requires=network-online.target
After=network-online.target
RequiresMountsFor={{ consul_data_dir }}
[Service]
EnvironmentFile=-/etc/default/consul
ExecStart=/usr/local/bin/consul agent $CONSUL_FLAGS -config-dir={{ consul_config_dir }} -config-dir={{ consul_data_dir }}
ExecReload=/bin/kill -HUP $MAINPID
KillSignal=SIGINT
StandardOutput=null
User=consul
Group=consul
Restart=on-failure
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,71 @@
#!/usr/bin/python3
# {{ ansible_managed }}
import os
import sys
import requests
CONSUL_API = 'http://localhost:8500'
def get_service(sess, service_id):
r = sess.get(CONSUL_API + '/v1/agent/services', timeout=2)
r.raise_for_status()
services = r.json()
for svc in services.values():
if svc['ID'] == service_id:
return svc
return None
def change_service_tags(service, tags_to_add, tags_to_remove):
with requests.Session() as sess:
sess.headers = {'X-Consul-Token': os.getenv('CONSUL_HTTP_TOKEN')}
svc = get_service(sess, service)
if svc:
new_tags = (set(svc.get('Tags', [])) | tags_to_add) - tags_to_remove
new_svc = {
'ID': svc['ID'],
'Name': svc['Service'],
'Address': svc.get('Address', ''),
'Port': svc.get('Port', 0),
'Meta': svc.get('Meta', {}),
'Tags': sorted(list(new_tags)),
'EnableTagOverride': svc.get('EnableTagOverride', False),
}
for k, v in new_svc.items():
print('{} = {}'.format(k, v))
r = sess.put(CONSUL_API + '/v1/agent/service/register', json=new_svc, timeout=2)
r.raise_for_status()
def main(argv):
if len(argv) < 3:
print("Usage: consul-tag service +tag -tag...")
return 1
service = argv[1]
tags_to_add = set()
tags_to_remove = set()
for tag in argv[2:]:
if tag.startswith('-'):
tags_to_remove.add(tag[1:])
elif tag.startswith('+'):
tags_to_add.add(tag[1:])
else:
tags_to_add.add(tag)
try:
change_service_tags(service, tags_to_add, tags_to_remove)
except Exception as exc:
print("Error: {}".format(exc))
return 2
return 0
if __name__ == '__main__':
sys.exit(main(sys.argv))

View File

@@ -0,0 +1,28 @@
---
firewall_enabled: yes
firewall_standard_rules: yes
firewall_log_prefix: "FW:"
firewall_whitelist_ip: []
firewall_whitelist_ipv6: []
firewall_late_whitelist_ip: []
firewall_late_whitelist_ipv6: []
firewall_input_default_drop: true
firewall_output_default_drop: true
firewall_output_whitelist_domains: []
firewall_output_whitelist_ipv4: []
firewall_output_whitelist_ipv6: []
firewall_output_learning: false
firewall_whitelist_office_ip: []
firewall_whitelist_office_ports: []
firewall_ssh_acl: []
firewall_ssh_acl_extra: []
firewall_influx_acl: []
firewall_influx_acl_extra: []
firewall_allow_internal_dns: true
firewall_custom_ipv4_rules: ""
firewall_custom_ipv6_rules: ""

View File

@@ -0,0 +1,4 @@
- name: Restart firewall
service:
name: firewall
state: restarted

View File

@@ -0,0 +1,122 @@
---
- name: Ensure iptables packages are installed
apt:
pkg:
- iptables
- ipset
- conntrack
- ipv6calc # Required by update-firewall-outbound
state: present
when: firewall_run is not defined
tags: packages
- name: Install the firewall init.d script
template:
dest: /etc/init.d/firewall
src: etc_init.d_firewall.j2
mode: 0755
owner: root
group: root
when: firewall_run is not defined and firewall_enabled
tags:
- configs
- firewall
- name: Enable the firewall init.d script
service:
name: firewall
enabled: yes
when: firewall_run is not defined and firewall_enabled
tags:
- configs
- firewall
- name: Ensure the rules directories exist
file:
path: "/etc/firewall/{{ item }}"
state: directory
owner: root
group: root
mode: 0700
with_items:
- rules-v4.d
- rules-v6.d
when: firewall_run is not defined
tags:
- configs
- firewall
- name: Install the firewall configs
template: dest=/etc/firewall/{{ item }} src={{ item }}.j2 mode=0600
with_items:
- rules-v4.d/10_conntrack.sh
- rules-v4.d/15_local.sh
- rules-v4.d/17_monitoring.sh
- rules-v4.d/18_internal.sh
- rules-v4.d/20_whitelist.sh
- rules-v4.d/22_ssh.sh
- rules-v4.d/24_influxdb.sh
- rules-v4.d/33_mariadb.sh
- rules-v4.d/85_whitelist.sh
- rules-v4.d/90_allow_outbound.sh
- rules-v4.d/90_drop_all.sh
- rules-v4.d/95_fail2ban.sh
- rules-v6.d/10_conntrack.sh
- rules-v6.d/15_local.sh
- rules-v6.d/18_internal.sh
- rules-v6.d/20_whitelist.sh
- rules-v4.d/24_influxdb.sh
- rules-v4.d/33_mariadb.sh
- rules-v4.d/85_whitelist.sh
- rules-v6.d/90_allow_outbound.sh
- rules-v6.d/90_drop_all.sh
when: firewall_run is not defined and firewall_enabled and firewall_standard_rules
notify: Restart firewall
tags:
- configs
- firewall
- name: Install the extra firewall configs
template: dest=/etc/firewall/{{ item }} src={{ item }}.j2 mode=0600
with_items:
- rules-v4.d/50_custom.sh
- rules-v6.d/50_custom.sh
when: firewall_run is not defined and firewall_enabled and (firewall_custom_ipv4_rules or firewall_custom_ipv6_rules)
notify: Restart firewall
tags:
- configs
- firewall
- name: Install the firewall outbound ACLs
template: dest=/etc/firewall/outbound_whitelist.acl src=etc_firewall_outbound_whitelist.acl.j2 mode=0600
when: firewall_run is not defined and firewall_enabled and firewall_output_whitelist_domains
notify: Restart firewall
tags:
- configs
- firewall
- whitelists
- name: Remove obsolete configs
file: dest=/etc/firewall/{{ item }} state=absent
with_items:
- rules-v4.d/19_monitoring.sh
when: firewall_run is not defined and firewall_enabled
notify: Restart firewall
tags:
- configs
- firewall
- name: Install the firewall outbound update script
template: dest=/usr/sbin/update-firewall-outbound src=usr_sbin_update-firewall-outbound.j2 mode=0700
when: firewall_run is not defined and firewall_enabled and firewall_output_whitelist_domains
notify: Restart firewall
tags:
- firewall
- scripts
- whitelists
- set_fact:
firewall_run: true
when: firewall_run is not defined

View File

@@ -0,0 +1,5 @@
# {{ ansible_managed }}
{% for name in firewall_output_whitelist_domains %}
{{ name }}
{% endfor %}

View File

@@ -0,0 +1,133 @@
#!/bin/sh
# {{ ansible_managed }}
### BEGIN INIT INFO
# Provides: firewall
# Required-Start: $network
# Required-Stop: $network
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Set up iptables rules
# Description: Loads current iptables rules from/to /etc/firewall
### END INIT INFO
. /lib/lsb/init-functions
PATH="/sbin:$PATH"
rc=0
flush_ipv4()
{
for chain in INPUT FORWARD OUTPUT; do
iptables -P $chain ACCEPT
done
for table in $(iptables-save | awk '/^\*/ { print substr($1,2) }'); do
iptables -t $table -F
iptables -t $table -X
iptables -t $table -Z
done
}
flush_ipv6()
{
for chain in INPUT FORWARD OUTPUT; do
ip6tables -P $chain ACCEPT
done
for table in $(ip6tables-save | awk '/^\*/ { print substr($1,2) }'); do
ip6tables -t $table -F
ip6tables -t $table -X
ip6tables -t $table -Z
done
}
load_rules()
{
log_action_begin_msg "Loading iptables rules"
# load IPv4 rules
if [ ! -d /etc/firewall/rules-v4.d ]; then
log_action_cont_msg " skipping IPv4 (no rules to load)"
else
log_action_cont_msg " IPv4"
flush_ipv4
for frag in /etc/firewall/rules-v4.d/*.sh; do
if [ -r "$frag" ]; then
. "$frag"
if [ $? -ne 0 ]; then
rc=1
fi
fi
done
fi
# load IPv6 rules
if [ ! -d /etc/firewall/rules-v6.d ]; then
log_action_cont_msg " skipping IPv6 (no rules to load)"
else
log_action_cont_msg " IPv6"
flush_ipv6
for frag in /etc/firewall/rules-v6.d/*.sh; do
if [ -r "$frag" ]; then
. "$frag"
if [ $? -ne 0 ]; then
rc=1
fi
fi
done
fi
log_action_end_msg $rc
}
flush_rules()
{
log_action_begin_msg "Flushing rules"
if [ ! -f /proc/net/ip_tables_names ]; then
log_action_cont_msg " skipping IPv4"
else
log_action_cont_msg " IPv4"
flush_ipv4
fi
if [ ! -f /proc/net/ip6_tables_names ]; then
log_action_cont_msg " skipping IPv6"
else
log_action_cont_msg " IPv6"
flush_ipv6
fi
log_action_end_msg 0
}
case "$1" in
start|restart|reload|force-reload)
load_rules
;;
stop)
echo "Automatic flushing disabled, use \"flush\" instead of \"stop\""
;;
flush)
flush_rules
;;
debug)
iptables() { echo "iptables $@"; }
ip6tables() { echo "ip6tables $@"; }
ipset() { echo "ipset $@"; }
log_action_begin_msg() { :; }
log_action_cont_msg() { :; }
log_action_end_msg() { :; }
load_rules
;;
*)
echo "Usage: $0 {start|restart|reload|force-reload|save|flush}" >&2
exit 1
;;
esac
exit $rc

View File

@@ -0,0 +1,5 @@
# {{ ansible_managed }}
# Allow established connections
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

View File

@@ -0,0 +1,5 @@
# {{ ansible_managed }}
# Allow all traffic from localhost
iptables -A INPUT -i lo -j ACCEPT
iptables -A OUTPUT -o lo -j ACCEPT

View File

@@ -0,0 +1,7 @@
# {{ ansible_managed }}
iptables -N monitoring-in
{% for srcip in firewall_monitoring_ips|default([]) %}
iptables -A INPUT -s {{ srcip }} -j monitoring-in
{% endfor %}

Some files were not shown because too many files have changed in this diff Show More