Initial commit
This commit is contained in:
commit
0c5ee3d3d1
12 changed files with 292 additions and 0 deletions
18
README.md
Normal file
18
README.md
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
An ansible role to set up an ssh tunnel and port forwardings from the remote machine to local machines
|
||||||
|
|
||||||
|
# Caution
|
||||||
|
Since the current debian version of sshd does not yet support dynamic configuration files (as in `/etc/ssh/sshd_config.d/*`), this role will **overwrite** your current sshd configuration! Normal ssh access on port 22 is still possible, but custom modifications will be lost on the remote machine.
|
||||||
|
|
||||||
|
# Variables you need to set
|
||||||
|
... e.g. in `group_vars`
|
||||||
|
- `ssh_tunnel_pubkey`: The complete line to be used in `authorized_keys`, e.g. "ssh-ed25519 AAAA[...]aU root@mylocalmachine"
|
||||||
|
- `ssh_tunnel_privkey`: The content of the corresponding private key file, including the BEGIN and END tags. It is highly recommended to put this inside an encrypted ansible vault.
|
||||||
|
- `tunneled_ports`: A list of port forwardings. Example:
|
||||||
|
```
|
||||||
|
tunneled_ports:
|
||||||
|
- exposed_port: 80 # public port at the remote machine
|
||||||
|
ephemeral_port: 10080 # internal port at remote machine's localhost address. The ssh tunnel will fetch traffic from there
|
||||||
|
dest_host: my-internal-http-server.local.domain.tld # domain or IP address of the destination machine. Must be reachable form the local machine.
|
||||||
|
dest_port: 80 # open port at the destination machine
|
||||||
|
protocols: ["tcp"] # list of protocols for this forwarding. "tcp" and "udp" are supported.
|
||||||
|
```
|
5
defaults/main.yml
Normal file
5
defaults/main.yml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
ssh_tunnel_os_supported: False
|
||||||
|
|
||||||
|
ssh_tunnel_autossh_system_user: autossh
|
||||||
|
ssh_tunnel_sshd_unprivileged_user: ssh-tunnel
|
12
example_playbook.yml
Normal file
12
example_playbook.yml
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
---
|
||||||
|
- hosts: my-remote-vps.domain.tld
|
||||||
|
roles:
|
||||||
|
- ssh_tunnel
|
||||||
|
vars:
|
||||||
|
location: "remote"
|
||||||
|
|
||||||
|
- hosts: my-local-machine.domain.tld
|
||||||
|
roles:
|
||||||
|
- ssh_tunnel
|
||||||
|
vars:
|
||||||
|
location: "local"
|
18
handlers/main.yml
Normal file
18
handlers/main.yml
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
---
|
||||||
|
- name: restart autossh
|
||||||
|
service:
|
||||||
|
name: autossh
|
||||||
|
state: restarted
|
||||||
|
|
||||||
|
- name: restart sshd
|
||||||
|
service:
|
||||||
|
name: sshd
|
||||||
|
state: restarted
|
||||||
|
|
||||||
|
- name: save iptables v4 rules
|
||||||
|
shell: iptables-save > /etc/iptables/rules.v4
|
||||||
|
listen: persist iptables
|
||||||
|
|
||||||
|
- name: save iptables v6 rules
|
||||||
|
shell: ip6tables-save > /etc/iptables/rules.v6
|
||||||
|
listen: persist iptables
|
45
tasks/local.yml
Normal file
45
tasks/local.yml
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
---
|
||||||
|
- name: Install autossh
|
||||||
|
apt:
|
||||||
|
name: autossh
|
||||||
|
state: present
|
||||||
|
update_cache: yes
|
||||||
|
|
||||||
|
- name: Ensure unprivileged ssh user exists
|
||||||
|
user:
|
||||||
|
name: "{{ ssh_tunnel_autossh_system_user }}"
|
||||||
|
system: true
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Set user's ssh config
|
||||||
|
template:
|
||||||
|
src: local/ssh_config.j2
|
||||||
|
dest: "{{ ssh_tunnel_local_sshdir }}config"
|
||||||
|
owner: "{{ ssh_tunnel_autossh_system_user }}"
|
||||||
|
mode: 0644
|
||||||
|
|
||||||
|
- name: Set private key
|
||||||
|
copy:
|
||||||
|
dest: "{{ ssh_tunnel_local_sshdir }}tunnel-key"
|
||||||
|
content: "{{ ssh_tunnel_privkey }}"
|
||||||
|
owner: "{{ ssh_tunnel_autossh_system_user }}"
|
||||||
|
mode: 0600
|
||||||
|
|
||||||
|
- name: Set public key
|
||||||
|
copy:
|
||||||
|
dest: "{{ ssh_tunnel_local_sshdir }}tunnel-key.pub"
|
||||||
|
content: "{{ ssh_tunnel_pubkey }}"
|
||||||
|
owner: "{{ ssh_tunnel_autossh_system_user }}"
|
||||||
|
mode: 0644
|
||||||
|
|
||||||
|
- name: Set systemd service file
|
||||||
|
become: yes
|
||||||
|
template:
|
||||||
|
src: local/autossh.service.j2
|
||||||
|
dest: "{{ ssh_tunnel_autossh_service_file }}"
|
||||||
|
|
||||||
|
- name: Enable service and run it
|
||||||
|
service:
|
||||||
|
name: autossh
|
||||||
|
state: restarted
|
||||||
|
enabled: yes
|
28
tasks/main.yml
Normal file
28
tasks/main.yml
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
---
|
||||||
|
- name: Set OS dependent variables
|
||||||
|
ansible.builtin.include_vars: "{{ lookup('first_found', params) }}"
|
||||||
|
vars:
|
||||||
|
params:
|
||||||
|
files:
|
||||||
|
- "{{ ansible_distribution | lower }}_{{ ansible_distribution_version | lower }}.yml"
|
||||||
|
- "{{ ansible_distribution | lower }}_{{ ansible_distribution_major_version | lower }}.yml"
|
||||||
|
- "{{ ansible_distribution | lower }}.yml"
|
||||||
|
- "{{ ansible_os_family | lower }}.yml"
|
||||||
|
- "{{ ansible_system | lower }}.yml"
|
||||||
|
paths:
|
||||||
|
- '{{ role_path }}/vars'
|
||||||
|
ignore_errors: True
|
||||||
|
|
||||||
|
- name: OS is supported
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that: __os_supported
|
||||||
|
quiet: True
|
||||||
|
vars:
|
||||||
|
__os_supported: "{{ lookup('vars', '{}_os_supported'.format(role_name)) | bool }}"
|
||||||
|
|
||||||
|
|
||||||
|
- include_tasks: local.yml
|
||||||
|
when: location == "local"
|
||||||
|
|
||||||
|
- include_tasks: remote.yml
|
||||||
|
when: location == "remote"
|
125
tasks/remote.yml
Normal file
125
tasks/remote.yml
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
---
|
||||||
|
- name: Ensure unprivileged ssh user exists
|
||||||
|
user:
|
||||||
|
name: "{{ ssh_tunnel_sshd_unprivileged_user }}"
|
||||||
|
system: true
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Set authorized_keys for unprivileged user
|
||||||
|
template:
|
||||||
|
src: remote/authorized_keys.j2
|
||||||
|
dest: "{{ ssh_tunnel_remote_sshdir }}authorized_keys"
|
||||||
|
owner: "{{ ssh_tunnel_sshd_unprivileged_user }}"
|
||||||
|
mode: 0600
|
||||||
|
|
||||||
|
- name: Set sshd_config
|
||||||
|
become: yes
|
||||||
|
template:
|
||||||
|
src: remote/sshd_config.j2
|
||||||
|
dest: "{{ ssh_tunnel_sshd_conf_dir }}tunnel.conf"
|
||||||
|
mode: 0644
|
||||||
|
owner: root
|
||||||
|
|
||||||
|
- name: Enable IPv4 forwarding
|
||||||
|
sysctl:
|
||||||
|
name: net.ipv4.ip_forward
|
||||||
|
value: '1'
|
||||||
|
sysctl_set: yes
|
||||||
|
state: present
|
||||||
|
reload: yes
|
||||||
|
|
||||||
|
- name: Enable IPv6 forwarding
|
||||||
|
sysctl:
|
||||||
|
name: net.ipv6.conf.all.forwarding
|
||||||
|
value: '1'
|
||||||
|
sysctl_set: yes
|
||||||
|
state: present
|
||||||
|
reload: yes
|
||||||
|
|
||||||
|
- name: Enable IPv4 local network forwarding
|
||||||
|
sysctl:
|
||||||
|
name: net.ipv4.conf.all.route_localnet
|
||||||
|
value: '1'
|
||||||
|
sysctl_set: yes
|
||||||
|
state: present
|
||||||
|
reload: yes
|
||||||
|
|
||||||
|
- name: Install iptables-persistent
|
||||||
|
apt:
|
||||||
|
name: iptables-persistent
|
||||||
|
update_cache: yes
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Flush existing iptables entries (IPv4)
|
||||||
|
become: yes
|
||||||
|
iptables:
|
||||||
|
ip_version: ipv4
|
||||||
|
table: nat
|
||||||
|
flush: yes
|
||||||
|
|
||||||
|
- name: Flush existing iptables entries (IPv6)
|
||||||
|
become: yes
|
||||||
|
iptables:
|
||||||
|
ip_version: ipv6
|
||||||
|
table: nat
|
||||||
|
flush: yes
|
||||||
|
|
||||||
|
- name: Forward privileged ports to ephemeral localhost ports (IPv4, TCP)
|
||||||
|
become: yes
|
||||||
|
iptables:
|
||||||
|
ip_version: ipv4
|
||||||
|
table: nat
|
||||||
|
chain: PREROUTING
|
||||||
|
in_interface: eth0
|
||||||
|
protocol: tcp
|
||||||
|
destination_port: "{{ item.exposed_port }}"
|
||||||
|
jump: DNAT
|
||||||
|
to_destination: "127.0.0.1:{{ item.ephemeral_port }}"
|
||||||
|
loop: "{{ tunneled_ports }}"
|
||||||
|
when: "'tcp' in item.protocols"
|
||||||
|
notify: persist iptables
|
||||||
|
|
||||||
|
- name: Forward privileged ports to ephemeral localhost ports (IPv4, UDP)
|
||||||
|
become: yes
|
||||||
|
iptables:
|
||||||
|
ip_version: ipv4
|
||||||
|
table: nat
|
||||||
|
chain: PREROUTING
|
||||||
|
in_interface: eth0
|
||||||
|
protocol: udp
|
||||||
|
destination_port: "{{ item.exposed_port }}"
|
||||||
|
jump: DNAT
|
||||||
|
to_destination: "127.0.0.1:{{ item.ephemeral_port }}"
|
||||||
|
loop: "{{ tunneled_ports }}"
|
||||||
|
when: "'udp' in item.protocols"
|
||||||
|
notify: persist iptables
|
||||||
|
|
||||||
|
- name: Forward privileged ports to ephemeral localhost ports (IPv6, TCP)
|
||||||
|
become: yes
|
||||||
|
iptables:
|
||||||
|
ip_version: ipv6
|
||||||
|
table: nat
|
||||||
|
chain: PREROUTING
|
||||||
|
in_interface: eth0
|
||||||
|
protocol: tcp
|
||||||
|
destination_port: "{{ item.exposed_port }}"
|
||||||
|
jump: DNAT
|
||||||
|
to_destination: "[::1]:{{ item.ephemeral_port }}"
|
||||||
|
loop: "{{ tunneled_ports }}"
|
||||||
|
when: "'tcp' in item.protocols"
|
||||||
|
notify: persist iptables
|
||||||
|
|
||||||
|
- name: Forward privileged ports to ephemeral localhost ports (IPv6, UDP)
|
||||||
|
become: yes
|
||||||
|
iptables:
|
||||||
|
ip_version: ipv6
|
||||||
|
table: nat
|
||||||
|
chain: PREROUTING
|
||||||
|
in_interface: eth0
|
||||||
|
protocol: udp
|
||||||
|
destination_port: "{{ item.exposed_port }}"
|
||||||
|
jump: DNAT
|
||||||
|
to_destination: "[::1]:{{ item.ephemeral_port }}"
|
||||||
|
loop: "{{ tunneled_ports }}"
|
||||||
|
when: "'udp' in item.protocols"
|
||||||
|
notify: persist iptables
|
12
templates/local/autossh.service.j2
Normal file
12
templates/local/autossh.service.j2
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
[Unit]
|
||||||
|
Description=AutoSSH tunnel service
|
||||||
|
After=network-online.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=autossh
|
||||||
|
Environment="AUTOSSH_GATETIME=0"
|
||||||
|
ExecStart=/usr/bin/autossh -M 0 gateway -N
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
12
templates/local/ssh_config.j2
Normal file
12
templates/local/ssh_config.j2
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
Host gateway
|
||||||
|
HostName tunnel-end.a-0.me
|
||||||
|
User ssh-tunnel
|
||||||
|
Port 22
|
||||||
|
IdentityFile /home/autossh/.ssh/tunnel-key
|
||||||
|
IdentitiesOnly yes
|
||||||
|
ExitOnForwardFailure yes
|
||||||
|
ServerAliveInterval 5
|
||||||
|
ServerAliveCountMax 3
|
||||||
|
{% for forwarding in tunneled_ports %}
|
||||||
|
RemoteForward localhost:{{ forwarding.ephemeral_port }} {{ forwarding.dest_host }}:{{ forwarding.dest_port }}
|
||||||
|
{% endfor %}
|
1
templates/remote/authorized_keys.j2
Normal file
1
templates/remote/authorized_keys.j2
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{{ ssh_tunnel_pubkey }}
|
9
templates/remote/sshd_config.j2
Normal file
9
templates/remote/sshd_config.j2
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
ClientAliveInterval 30
|
||||||
|
ClientAliveCountMax 3
|
||||||
|
|
||||||
|
Match User ssh-tunnel
|
||||||
|
AllowTcpForwarding yes
|
||||||
|
X11Forwarding no
|
||||||
|
PermitTunnel no
|
||||||
|
GatewayPorts clientspecified
|
||||||
|
ForceCommand echo "Only forwarding"
|
7
vars/debian.yml
Normal file
7
vars/debian.yml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
---
|
||||||
|
ssh_tunnel_os_supported: True
|
||||||
|
|
||||||
|
ssh_tunnel_local_sshdir: "/home/{{ ssh_tunnel_autossh_system_user }}/.ssh/"
|
||||||
|
ssh_tunnel_remote_sshdir: "/home/{{ ssh_tunnel_sshd_unprivileged_user }}/.ssh/"
|
||||||
|
ssh_tunnel_autossh_service_file: "/etc/systemd/system/autossh.service"
|
||||||
|
ssh_tunnel_sshd_conf_dir: "/etc/ssh/sshd_config/"
|
Loading…
Add table
Add a link
Reference in a new issue