├── README.md
├── defaults
└── main.yml
├── handlers
└── main.yml
├── meta
└── main.yml
├── tasks
└── main.yml
├── templates
├── znc-base.conf
├── znc-child.conf
└── znc-monit.conf
└── vars
└── main.yml
/README.md:
--------------------------------------------------------------------------------
1 | znc-on-znc
2 | ==========
3 |
4 | This role makes it easy to create a set of connected ZNC bouncer
5 | instances, so that each IRC client has its own scrollback.
6 |
7 | This work is based on the work of Sean Dague and Dan Smith, as
8 | described in https://dague.net/2014/09/13/my-irc-proxy-setup/
9 |
10 | A base ZNC instance is configured to talk to the IRC servers
11 | upstream. Then separate child servers are created and exposed for
12 | clients to connect to. Each child server has its own self-signed SSL
13 | certificate and ZNC configuration file. A monit service is configured
14 | for each ZNC server to ensure that the bouncer itself stays running.
15 |
16 | Requirements
17 | ------------
18 |
19 | You need accounts on whatever IRC server(s) you use.
20 |
21 | You need to generate hashed password values for the user connecting to
22 | the ZNC servers. Generate these values using "znc --makepass".
23 |
24 | You will need to manage the firewall settings for the host you run
25 | this on to expose the ports configured for each client service.
26 |
27 | Role Variables
28 | --------------
29 |
30 | * znc_system_user
31 |
32 | User to use on the system to run znc. By default it will be the same user as
33 | what Ansible uses to connect to the system.
34 |
35 | * znc_system_group
36 |
37 | Group to use on the system to run znc. By default it will be the same group
38 | as what Ansible uses to connect to the system.
39 |
40 | * znc_config_dir
41 |
42 | Base directory for the personal ZNC configuration files. A separate
43 | subdirectory is created under this path for each server. Must be
44 | writable by the user running the ZNC services. Must be a full
45 | path. Defaults to ~/znc.
46 |
47 | * znc_base_port
48 |
49 | The TCP port for the base ZNC server to listen on. Clients do not
50 | usually connect to this server, so the port doesn't really matter,
51 | but it's configurable anyway. Defaults to 6666.
52 |
53 | * znc_max_buffer_size
54 |
55 | The MaxBufferSize for the server.
56 |
57 | * znc_user
58 |
59 | Dictionary containing credentials to be used to connect to the ZNC
60 | bouncer. Generate these values using "znc --makepass".
61 |
62 | * name
63 |
64 | The user name.
65 |
66 | * hash
67 |
68 | The hashed password.
69 |
70 | * method
71 |
72 | The hash encryption method used. Defaults to SHA256.
73 |
74 | * salt
75 |
76 | The salt value used for the encryption.
77 |
78 | * password
79 |
80 | The unencrypted password value, used to let the child servers
81 | connect to the base server.
82 |
83 | * znc_nick
84 |
85 | The nickname to use on the IRC server.
86 |
87 | * znc_alt_nick
88 |
89 | The alternate nickname to fall back to if znc_nick is
90 | taken. Defaults to znc_nick + "_".
91 |
92 | * znc_buffer
93 |
94 | The number of lines to buffer. Defaults to 500.
95 |
96 | * znc_quit_msg
97 |
98 | Message to use when ZNC shuts down.
99 |
100 | * znc_real_name
101 |
102 | A fuller name than the nick or ident.
103 |
104 | * znc_extra_modules
105 |
106 | A list of module names to be added to the base server. For example,
107 | include the "log" module to log all conversations and include
108 | "webadmin" to enable the web UI.
109 |
110 | * znc_networks
111 |
112 | The IRC networks to connect to. For each network, specify:
113 |
114 | * name
115 |
116 | The unique name of the network. For example, "freenode".
117 |
118 | * ident
119 |
120 | The confirmed identity on the IRC service. Frequently this is the
121 | same as the nick, but multiple nicks can be associated with a single
122 | identity. (Changed from the single value "znc_ident" as part of 2.x.)
123 |
124 | * server_name
125 |
126 | The hostname or IP of the IRC server. For example,
127 | "chat.freenode.net".
128 |
129 | * server_port
130 |
131 | The port on which to connect. For SSL connections, append "+" to
132 | the port number. For example, "6667" or "6667+".
133 |
134 | * znc_server_passwords
135 |
136 | Mapping of network names to passwords for connecting to them as the
137 | confirmed identity in the ident field. Replaces the
138 | "server_password" parameter to allow the passwords to be stored
139 | separately in a file managed with ansible-vault.
140 |
141 | * znc_channels
142 |
143 | List of names of channels to join by default.
144 |
145 | * znc_clients
146 |
147 | List of ZNC instances to run for different clients.
148 |
149 | * name
150 |
151 | The name of the client. Avoid spaces and punctuation because the
152 | name is used to identify the service and name configuration files
153 | and directories.
154 |
155 | * host
156 |
157 | The host/IP on which the client should listen.
158 |
159 | * port
160 |
161 | The port on which the client should listen. This port needs to be
162 | exposed through your firewall. The service runs as the the user
163 | ansible is using, so the port shouldn't be privileged. (See
164 | znc_firewall_bypass_port.)
165 |
166 | * ipv4
167 |
168 | Enable IPv4. Defaults to true.
169 |
170 | * ipv6
171 |
172 | Enable IPv6. Defaults to false.
173 |
174 | * buffer
175 |
176 | Override znc_buffer for this connection. Optional, defaults to
177 | value of znc_buffer.
178 |
179 | * use_ssl
180 |
181 | Boolean flag to control whether SSL is used. Defaults to true.
182 |
183 | * ssl_protocols
184 |
185 | String passed to the SSLProtocols variable in the ZNC
186 | config. Defaults to empty, which does not set the value and uses
187 | the default for SSLProtocols.
188 |
189 | * znc_firewall_bypass_port
190 |
191 | Many corporate and public wifi networks block outgoing connections
192 | to "arbitrary" or IRC ports. To bypass these, many users configure
193 | their IRC bouncer to listen on a port that is more likely to be
194 | open, such as 443. Because this playbook configures services to run
195 | as a regular non-root user, the services cannot be bound directly to
196 | port 443. Instead, this option can be used to specify one port
197 | number that should be mapped to 443 using rinetd.
198 |
199 | Configuring Your IRC Client
200 | ---------------------------
201 |
202 | Configure your client to connect to your server using one of the
203 | settings from the `znc_clients` variable. SSL is always enabled for
204 | all connections.
205 |
206 | Dependencies
207 | ------------
208 |
209 | None
210 |
211 | Example Playbook
212 | ----------------
213 |
214 | Including an example of how to use your role (for instance, with
215 | variables passed in as parameters) is always nice for users too:
216 |
217 | - hosts: znc
218 | roles:
219 | - znc-on-znc
220 | vars:
221 | # By default we will run znc as the same user/group Ansible uses to
222 | # connect to the system. If you prefer to specify a different
223 | # user/group or if Ansible uses the root user you can specify the
224 | # user/group to run znc. To run znc as the user 'znc' and group 'znc'
225 | # uncomment the following two lines:
226 | #znc_system_user: znc
227 | #znc_system_group: znc
228 | # znc_user and znc_server_passwords can go into a vault-encrypted file.
229 | znc_user:
230 | name: dhellmann
231 | hash: hashhashhash
232 | method: SHA256
233 | salt: "saltsaltsalt"
234 | password: unencryptedpass
235 | znc_server_passwords:
236 | freenode: supersecretvalue
237 | tech404: evenmoresecretvalue
238 | # The remaining values are safe to leave in the playbook in clear text.
239 | znc_nick: dhellmann
240 | znc_quit_msg: disconnecting
241 | znc_real_name: Doug Hellmann
242 | znc_networks:
243 | - name: freenode
244 | server_name: chat.freenode.net
245 | server_port: 6667
246 | ident: dhellmann
247 | channels:
248 | - "#openstack"
249 | - "#openstack-dev"
250 | - "#openstack-infra"
251 | - "#openstack-meeting"
252 | - "#openstack-meeting-3"
253 | - "#openstack-meeting-alt"
254 | - "#openstack-oslo"
255 | - "#wsme"
256 | - name: tech404
257 | ident: dhellmann
258 | server_name: tech404.irc.slack.com
259 | server_port: "+6697"
260 | channels:
261 | - "#python"
262 | - "#openstack"
263 | znc_firewall_bypass_port: 6672
264 | znc_clients:
265 | - name: hubert
266 | port: 6667
267 | - name: lrrr
268 | port: 6672
269 | auto_clear_chan_buffer: true
270 | - name: phone
271 | port: 6673
272 | buffer: 100
273 | auto_clear_chan_buffer: true
274 | - name: ipad
275 | port: 6677
276 | buffer: 100
277 | auto_clear_chan_buffer: true
278 |
279 | License
280 | -------
281 |
282 | BSD
283 |
284 | Author Information
285 | ------------------
286 |
287 | Doug Hellmann
288 |
289 |
290 | TODO
291 | ----
292 |
293 | * Restart ZNC instances when their configuration files change.
294 | * Support using vault for passwords.
295 |
--------------------------------------------------------------------------------
/defaults/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # defaults file for multiznc
3 | znc_system_user: "{{ansible_env.USER}}"
4 | znc_system_group: "{{ansible_env.USER}}"
5 | znc_system_user_dir: "/home/{{znc_system_user}}"
6 | znc_config_dir: "{{znc_system_user_dir|expanduser}}/znc"
7 | znc_base_port: 6666
8 | znc_max_buffer_size: 5000
9 | znc_auto_clear_chan_buffer: "false"
10 | znc_buffer: 500
11 | znc_alt_nick: "{{znc_nick}}_"
12 | znc_extra_modules: []
13 |
--------------------------------------------------------------------------------
/handlers/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: znc-restart-monit
3 | service: name=monit state=restarted
4 | become: yes
5 |
6 | - name: restart rinetd
7 | service: name=rinetd state=restarted
8 | become: yes
9 |
10 | - name: znc-hup-instances
11 | shell: for f in {{ znc_config_dir }}/*/znc.pid; do /bin/kill -HUP `cat $f`; done
12 | ignore_errors: yes
13 | become: yes
14 | become_user: "{{ znc_system_user }}"
15 |
--------------------------------------------------------------------------------
/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | galaxy_info:
3 | author: Doug Hellmann
4 | description: "Deploy ZNC bouncer for IRC, configured for multiple clients."
5 | license: BSD
6 | min_ansible_version: 1.8.2
7 | platforms:
8 | - name: Ubuntu
9 | versions:
10 | - precise
11 | - trusty
12 | categories:
13 | - cloud
14 | dependencies: []
15 | # List your role dependencies here, one per line. Only
16 | # dependencies available via galaxy should be listed here.
17 | # Be sure to remove the '[]' above if you add dependencies
18 | # to this list.
19 |
--------------------------------------------------------------------------------
/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | - name: Ensure not installing znc to root user
4 | fail: msg="Trying to install znc to the root user. znc refuses to run as root. Please set values for znc_system_user and znc_system_group"
5 | when: "'znc_system_user' == 'root'"
6 |
7 | - name: Install packages
8 | package:
9 | state: latest
10 | name:
11 | - znc
12 | - znc-dev
13 | - znc-perl
14 | - znc-python
15 | - znc-tcl
16 | - openssl
17 | - monit
18 | become: yes
19 | tags:
20 | - zncinstall
21 | - monitinstall
22 |
23 | - name: Install znc-dbg packages
24 | become: yes
25 | package:
26 | state: latest
27 | name:
28 | - znc-dbg
29 | tags:
30 | - zncinstall
31 | - monitinstall
32 | when: ansible_distribution == 'Debian' and ansible_distribution_release == 'stretch'
33 |
34 | # Create the ZNC user if needed
35 | - name: Create znc group
36 | group: name={{ znc_system_group }}
37 | become: yes
38 | - name: Create znc user
39 | user: name={{ znc_system_user }} group={{ znc_system_group }}
40 | become: yes
41 | tags:
42 | - zncconfig
43 |
44 | - name: "Base config directory"
45 | file: path={{ znc_config_dir }}/base/configs state=directory mode=0755
46 | become: yes
47 | become_user: "{{ znc_system_user }}"
48 | tags:
49 | - zncconfig
50 |
51 | - name: "Child config directories"
52 | file: path={{ znc_config_dir }}/{{ item.name }}/configs state=directory mode=0755
53 | with_items: "{{ znc_clients }}"
54 | become: yes
55 | become_user: "{{ znc_system_user }}"
56 | tags:
57 | - zncconfig
58 |
59 | # based on https://github.com/willshersystems/ansible-znc/blob/master/tasks/ssl.yml
60 | - name: "Base SSL certificate"
61 | command: znc --datadir={{ znc_config_dir }}/base --makepem
62 | args:
63 | creates: "{{ znc_config_dir }}/base/znc.pem"
64 | become: yes
65 | become_user: "{{ znc_system_user }}"
66 | tags:
67 | - zncconfig
68 | - ssl
69 |
70 | - name: "Base SSL certificate fingerprint" # noqa 301
71 | become: yes
72 | become_user: "{{ znc_system_user }}"
73 | shell: |
74 | set -o pipefail
75 | cat {{ znc_config_dir }}/base/znc.pem \
76 | | openssl x509 -fingerprint -sha256 \
77 | | grep Fingerprint= \
78 | | cut -f2 -d=
79 | args:
80 | executable: /bin/bash
81 | register: znc_base_ssl_fingerprint
82 | tags:
83 | - zncconfig
84 | - ssl
85 |
86 | # based on https://github.com/willshersystems/ansible-znc/blob/master/tasks/ssl.yml
87 | - name: "SSL certificates"
88 | command: znc --datadir={{ znc_config_dir }}/{{ item.name }} --makepem
89 | args:
90 | creates: "{{ znc_config_dir }}/{{ item.name }}/znc.pem"
91 | with_items: "{{ znc_clients }}"
92 | become: yes
93 | become_user: "{{ znc_system_user }}"
94 | tags:
95 | - zncconfig
96 | - ssl
97 |
98 | - name: "Base ZNC config file"
99 | template:
100 | src: znc-base.conf
101 | dest: "{{ znc_config_dir }}/base/configs/znc.conf"
102 | mode: 0644
103 | notify:
104 | - znc-hup-instances
105 | become: yes
106 | become_user: "{{ znc_system_user }}"
107 | tags:
108 | - zncconfig
109 |
110 | - name: "Child ZNC config files"
111 | template:
112 | src: znc-child.conf
113 | dest: "{{ znc_config_dir }}/{{ item.name }}/configs/znc.conf"
114 | mode: 0644
115 | with_items: "{{ znc_clients }}"
116 | notify:
117 | - znc-hup-instances
118 | become: yes
119 | become_user: "{{ znc_system_user }}"
120 | tags:
121 | - zncconfig
122 |
123 | - name: "Base monit config"
124 | template: src=znc-monit.conf
125 | dest=/etc/monit/conf.d/znc-{{ item.name }}.conf
126 | notify:
127 | - znc-restart-monit
128 | with_items:
129 | - name: base
130 | become: yes
131 | tags:
132 | - monitconfig
133 |
134 | - name: "Child monit configs"
135 | become: yes
136 | template: src=znc-monit.conf
137 | dest=/etc/monit/conf.d/znc-{{ item.name }}.conf
138 | notify:
139 | - znc-restart-monit
140 | with_items: "{{ znc_clients }}"
141 | tags:
142 | - monitconfig
143 |
144 | - name: Make sure monit is running
145 | service: name=monit state=started enabled=yes
146 | become: yes
147 | tags:
148 | - monitconfig
149 |
150 | # Use rinetd to bypass firewalls that block regular IRC ports (or
151 | # arbitrary ports) by connecting *one* client to port 443.
152 | - name: Install rinetd
153 | become: yes
154 | apt: name=rinetd
155 | when: znc_firewall_bypass_port is defined
156 | tags:
157 | - rinetdinstall
158 |
159 | - name: Configure rinetd
160 | become: yes
161 | lineinfile: dest=/etc/rinetd.conf
162 | insertafter="# bindadress bindport connectaddress connectport"
163 | line="0.0.0.0 443 127.0.0.1 {{ znc_firewall_bypass_port }}"
164 | when: znc_firewall_bypass_port is defined
165 | notify:
166 | - restart rinetd
167 | tags:
168 | - rinedtconfig
169 |
--------------------------------------------------------------------------------
/templates/znc-base.conf:
--------------------------------------------------------------------------------
1 | // WARNING
2 | //
3 | // Do NOT edit this file while ZNC is running!
4 | // Use webadmin or *controlpanel instead.
5 | //
6 | // Altering this file by hand will forfeit all support.
7 | //
8 | // But if you feel risky, you might want to read help on /znc saveconfig and /znc rehash.
9 | // Also check http://en.znc.in/wiki/Configuration
10 |
11 | AnonIPLimit = 10
12 | ConnectDelay = 5
13 | MaxBufferSize = {{znc_max_buffer_size}}
14 | ProtectWebSessions = true
15 | SSLCertFile = {{znc_config_dir}}/base/znc.pem
16 | ServerThrottle = 30
17 | Version = 1.6
18 | HideVersion = false
19 | PidFile = {{znc_config_dir}}/base/znc.pid
20 |
21 |
22 | AllowIRC = true
23 | AllowWeb = true
24 | IPv4 = true
25 | IPv6 = true
26 | Port = {{znc_base_port}}
27 | SSL = true
28 |
29 |
30 |
31 | Admin = true
32 | AltNick = {{znc_alt_nick}}
33 | AppendTimestamp = false
34 | AutoClearChanBuffer = {{znc_auto_clear_chan_buffer}}
35 | Buffer = {{znc_buffer}}
36 | ChanModes = +stn
37 | DenyLoadMod = false
38 | DenySetBindHost = false
39 | JoinTries = 10
40 | LoadModule = chansaver
41 | LoadModule = controlpanel
42 | LoadModule = perform
43 | {% for mod in znc_extra_modules %}
44 | LoadModule = {{mod}}
45 | {% endfor %}
46 | MaxJoins = 0
47 | MaxNetworks = 1
48 | MultiClients = true
49 | Nick = {{znc_nick}}
50 | PrependTimestamp = true
51 | QuitMsg = {{znc_quit_msg}}
52 | RealName = {{znc_real_name}}
53 | StatusPrefix = *
54 | TimestampFormat = [%H:%M:%S]
55 |
56 | {% for net in znc_networks %}
57 |
58 | FloodBurst = 4
59 | FloodRate = 1.00
60 | IRCConnectEnabled = true
61 | LoadModule = chansaver
62 | LoadModule = keepnick
63 | LoadModule = kickrejoin
64 | LoadModule = nickserv
65 | LoadModule = perform
66 | Ident = {{net.ident}}
67 | Server = {{net.server_name}} {{net.server_port}} {{znc_server_passwords[net.name]}}
68 |
69 | {% for c in net.channels %}
70 |
71 |
72 | {% endfor %}
73 |
74 |
75 | {% endfor %}
76 |
77 |
78 | Hash = {{znc_user.hash}}
79 | Method = {{znc_user.method | default('SHA256')}}
80 | Salt = {{znc_user.salt}}
81 |
82 |
83 |
--------------------------------------------------------------------------------
/templates/znc-child.conf:
--------------------------------------------------------------------------------
1 | // WARNING
2 | //
3 | // Do NOT edit this file while ZNC is running!
4 | // Use webadmin or *controlpanel instead.
5 | //
6 | // Altering this file by hand will forfeit all support.
7 | //
8 | // But if you feel risky, you might want to read help on /znc saveconfig and /znc rehash.
9 | // Also check http://en.znc.in/wiki/Configuration
10 |
11 | AnonIPLimit = 10
12 | ConnectDelay = 5
13 | MaxBufferSize = {{znc_max_buffer_size}}
14 | ProtectWebSessions = true
15 | {% if item.get('use_ssl', True) %}
16 | SSLCertFile = {{znc_config_dir}}/{{item.name}}/znc.pem
17 | {% endif %}
18 | ServerThrottle = 30
19 | Version = 1.6
20 | HideVersion = false
21 | PidFile = {{znc_config_dir}}/{{item.name}}/znc.pid
22 | {% if item.get('ssl_protocols') %}
23 | SSLProtocols = {{item['ssl_protocols']}}
24 | {% endif %}
25 |
26 |
27 | AllowIRC = true
28 | AllowWeb = true
29 | {% if item.get('ipv4', True) %}
30 | IPv4 = true
31 | {% endif %}
32 | {% if item.get('ipv6', False) %}
33 | IPv6 = true
34 | {% endif %}
35 | Port = {{item.port}}
36 | SSL = {{item.get('use_ssl', 'true')}}
37 | {% if item.get('host', False) %}
38 | Host = {{ item.host }}
39 | {% endif %}
40 |
41 |
42 |
43 | Admin = true
44 | AltNick = {{znc_alt_nick}}
45 | AppendTimestamp = false
46 | AutoClearChanBuffer = {{item.auto_clear_chan_buffer | default(znc_auto_clear_chan_buffer)}}
47 | Buffer = {{item.buffer | default(znc_buffer)}}
48 | ChanModes = +stn
49 | DenyLoadMod = false
50 | DenySetBindHost = false
51 | JoinTries = 10
52 | LoadModule = chansaver
53 | LoadModule = controlpanel
54 | LoadModule = perform
55 | MaxJoins = 0
56 | MaxNetworks = 1
57 | MultiClients = true
58 | Nick = {{znc_nick}}
59 | PrependTimestamp = true
60 | QuitMsg = {{znc_quit_msg}}
61 | RealName = {{znc_real_name}}
62 | StatusPrefix = *
63 | TimestampFormat = [%H:%M:%S]
64 |
65 | {% for net in znc_networks %}
66 |
67 | FloodBurst = 4
68 | FloodRate = 1.00
69 | IRCConnectEnabled = true
70 | LoadModule = chansaver
71 | LoadModule = keepnick
72 | LoadModule = kickrejoin
73 | LoadModule = nickserv
74 | LoadModule = perform
75 | // Combine the ZNC username and the network name to ensure the
76 | // connection is for the specific network.
77 | // http://wiki.znc.in/Connecting_to_ZNC
78 | Ident = {{znc_user.name}}/{{net.name}}
79 | Server = 127.0.0.1 +{{znc_base_port}} {{znc_user.password}}
80 | // We generated this SSL certificate ourselves, so set the
81 | // flag to trust it.
82 | TrustedServerFingerprint = {{znc_base_ssl_fingerprint.stdout}}
83 |
84 | {% for c in net.channels %}
85 |
86 |
87 | {% endfor %}
88 |
89 |
90 | {% endfor %}
91 |
92 |
93 | Hash = {{znc_user.hash}}
94 | Method = {{znc_user.method | default('SHA256')}}
95 | Salt = {{znc_user.salt}}
96 |
97 |
98 |
--------------------------------------------------------------------------------
/templates/znc-monit.conf:
--------------------------------------------------------------------------------
1 | check process znc-{{item.name}} with pidfile {{znc_config_dir}}/{{item.name}}/znc.pid
2 | start program = "/usr/bin/znc -d {{znc_config_dir}}/{{item.name}}"
3 | as uid {{znc_system_user}} and gid {{znc_system_group}}
4 | stop program = "/bin/bash -c 'kill -s SIGTERM `cat {{znc_config_dir}}/{{item.name}}/znc.pid`'"
5 | if totalmem is greater than 300 MB for 10 cycles then restart
6 |
--------------------------------------------------------------------------------
/vars/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 |
--------------------------------------------------------------------------------