Initial commit

This commit is contained in:
Philip (a-0) 2022-07-13 12:52:07 +02:00
commit 0c5ee3d3d1
12 changed files with 292 additions and 0 deletions

18
README.md Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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

View 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

View 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 %}

View file

@ -0,0 +1 @@
{{ ssh_tunnel_pubkey }}

View 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
View 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/"