aboutsummaryrefslogtreecommitdiff
path: root/roles/tor/tasks/ssh_hidden_service.yaml
blob: d2415f2f749e185efc610b43a4efffbd60b8bb82 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
- name: create directory for ssh hidden service
  file:
    path: /var/lib/tor/ssh
    state: directory
    owner: debian-tor
    group: debian-tor
    mode: '0700'

- name: ensure ssh hidden service is configured in torrc
  blockinfile:
    path: /etc/tor/torrc
    marker: "# {mark} ANSIBLE MANAGED SSH HIDDEN SERVICE"
    block: |
      HiddenServiceDir /var/lib/tor/ssh
      HiddenServicePort 22 127.0.0.1:22

- name: restart tor
  systemd:
    name: tor
    state: restarted
    enabled: true
  when: ansible_service_mgr == 'systemd'

- name: wait for hidden service hostname file
  wait_for:
    path: /var/lib/tor/ssh/hostname
    timeout: 120

- name: wait for hidden service secret key file
  wait_for:
    path: /var/lib/tor/ssh/hs_ed25519_secret_key
    timeout: 120

- name: read hidden service hostname
  slurp:
    src: /var/lib/tor/ssh/hostname
  register: hs_hostname_raw

- name: set fact with cleaned .onion hostname
  set_fact:
    onion_address: "{{ hs_hostname_raw.content | b64decode | trim }}"

- name: ensure ssh key for onion access exists on target
  openssh_keypair:
    path: /root/.ssh/id_ed25519_onion
    type: ed25519
    owner: root
    group: root
    mode: '0600'
  register: onion_ssh_key

- name: read onion ssh public key from target
  slurp:
    src: /root/.ssh/id_ed25519_onion.pub
  register: onion_ssh_pubkey_raw

- name: add onion ssh public key to authorized_keys
  authorized_key:
    user: root
    state: present
    key: "{{ onion_ssh_pubkey_raw.content | b64decode }}"

- name: create local directory for ssh config.d
  file:
    path: "./{{ onion_address }}/config.d"
    state: directory
    mode: '0700'
  become: false
  delegate_to: localhost
  run_once: true

- name: generate modular SSH config file
  copy:
    dest: "./{{ onion_address }}/ssh_config"
    content: |
      Include config.d/*
      Host *.onion
        ProxyCommand nc -x 127.0.0.1:9050 -X 5 %h %p
  become: false
  delegate_to: localhost
  run_once: true

- name: fetch generated onion ssh private key
  fetch:
    src: /root/.ssh/id_ed25519_onion
    dest: "./{{ onion_address }}/{{ onion_address }}_id_ed25519"
    flat: true

- name: set correct permissions on fetched hidden service private key
  file:
    path: "./{{ onion_address }}/{{ onion_address }}_id_ed25519"
    mode: '0600'
  delegate_to: localhost

- name: fetch generated onion ssh public key
  fetch:
    src: /root/.ssh/id_ed25519_onion.pub
    dest: "./{{ onion_address }}/{{ onion_address }}_id_ed25519.pub"
    flat: true

- name: fetch hidden service private key
  fetch:
    src: "/var/lib/tor/ssh/hs_ed25519_secret_key"
    dest: "./{{ onion_address }}/hs_ed25519_secret_key"
    flat: true
    mode: '0600'

- name: generate onion-specific ssh config snippet
  copy:
    dest: "./{{ onion_address }}/config.d/{{ onion_address }}"
    content: |
      Host {{ onion_address }}
          HostName {{ onion_address }}
          Port 22
          User {{ ansible_user | default('root') }}
          PubkeyAuthentication yes
          IdentityFile ../{{ onion_address }}_id_ed25519
          VerifyHostKeyDNS no
  become: false
  delegate_to: localhost
  run_once: true

- name: generate ssh wrapper script
  copy:
    dest: "./{{ onion_address }}/ssh_{{ onion_address }}"
    content: |
      #!/bin/bash

      SOCKS_PORT=9050
      SOCKS_HOST=127.0.0.1
      SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
      CONFIG_FILE="$SCRIPT_DIR/ssh_config"
      KEY_FILE="$SCRIPT_DIR/{{ onion_address }}_id_ed25519"
      ONION_HOST="{{ onion_address }}"
      USER="root"

      if ! pgrep -x tor >/dev/null; then
        printf "%s\n" "[err] tor process is not running"
        exit 1
      fi

      if ! nc -z "$SOCKS_HOST" "$SOCKS_PORT"; then
        printf "%s\n" "[err] tor socks proxy $SOCKS_HOST:$SOCKS_PORT is not reachable"
        exit 1
      fi

      if ! test -f "$CONFIG_FILE"; then
        printf "%s\n" "[err] ssh config not found: $CONFIG_FILE"
        exit 1
      fi

      if ! test -f "$KEY_FILE"; then
        printf "%s\n" "[err] identity file not found: $KEY_FILE"
        exit 1
      fi

      exec ssh \
        -F "$CONFIG_FILE" \
        -i "$KEY_FILE" \
        "$USER@$ONION_HOST" \
        "$@"
    mode: '0755'
  become: false
  delegate_to: localhost
  run_once: true