RaspberryPi3にSSHVPNクライアント

ここにメモしていた内容の、AnsiblePlaybook化とRasipberryPi3にインストールしたUbuntuへの適用。

OpenVPNや、IPSecを導入するのは設定も難しく、かつ端末に新たにソフトを入れなければならないので、もともとLinuxには入っているOpenSSHを使うことで、RasiberryPi3等の小さい機器でも負担を軽くしている(つもり)。

適用するPlaybook remotehost.yml

RaspberryPi3(Ansilbe上はremotehost扱いとしている)に割り当てるIPアドレスが192.168.0.2、VPNサーバに割り当てるIPアドレスが192.168.0.1とした構成。

- hosts: remotehost

  become: yes
  become_user: root
  become_method: sudo

  roles:
    - role: service/sshvpn-client
      server: ${IPADDR_WHERE_SSHVPN_SERVER_EXISTS}
      port: 22
      local_tunnel_id: 0
      local_ip_address: 192.168.0.2
      remote_tunnel_id: 1
      remote_ip_address: 192.168.0.1

service/sshvpn-clientロールの主なタスク setup-Ubuntu16.yml

service/sshvpn-client/tasks/main.ymlからincludeされるファイル。
依存関係のロール2つ(os/serviceとcommand/file-creation)を実行している。
os/serviceロールは、シンプルなコマンドを実行するsystemdのサービスを定義するろロール。
OpenSSHでトンネルインタフェースを作成し、ifconfigでアドレスを付与するコマンドをあらかじめ作っておき、os/serviceロールでそのコマンドをサービス化する。
command/file-creationロールは、コマンドベースで作成されるファイルのロール。
SSH接続するときに秘密鍵が必要なので、専用の秘密鍵がなかった場合生成する。

- set_fact:
    __u: "{{ user | default('root') }}"

- set_fact:
    __s: "{{ server }}"
    __p: "{{ port | default(22) }}"
    __lti: "{{ local_tunnel_id }}"
    __rti: "{{ remote_tunnel_id }}"
    __lip: "{{ local_ip_address }}"
    __rip: "{{ remote_ip_address }}"
    __enabled: "{{ enabled | default(true) }}"
    __state: "{{ state | default('started') }}"
    __keyfile: "{{ keyfile | default('~' + __u + '/.ssh/id_rsa.sshvpn') }}"


- modprobe:
    name: tun
    state: present


- set_fact:
    __service_name: sshvpn-client
    __description: "SSH VPN for {{ __s }}:{{ __p }}"
    __start_command: "/usr/bin/ssh -T -w{{ __lti }}:{{ __rti }} -oPermitLocalCommand=yes -oLocalCommand=\"ifconfig tun{{ __lti }} {{ __lip }} pointopoint {{ __rip }}\" -oPort={{ __p }} -i {{ __keyfile }} {{ __u}}@{{ __s }} \"ifconfig tun{{ __rti }} {{ __rip }} pointopoint {{ __lip }}\""
    __started: false


- include_role:
    name: os/service
  vars:
    service_name: "{{ __service_name }}"
    description: "{{ __description }}"
    start_command: "{{ __start_command }}"
    started: "{{ __started }}"
    enabled: "{{ __enabled }}"


- include_role:
    name: command/file-creation
  vars:
    command: "ssh-keygen -N '' -b 4096 -f {{ __keyfile }}"
    path: "{{ __keyfile }}"


- name: "Systemd 設定有効化"
  systemd:
    daemon_reload: yes
    name: "{{ __service_name }}"
    enabled: "{{ __enabled }}"


- systemd:
    name: "{{ __service_name }}"
    enabled: "{{ __enabled }}"
    state: "{{ __state }}"
  notify: service_sshvpn_client_ubuntu16_systemd_restart_service

os/serviceロールの主なタスク tasks/setup-Ubuntu16.yml

汎用的なロール。include_role非常にありがたい。

- set_fact:
    __service_name: "{{ service_name }}"
    __description: "{{ description }}"
    __start_command: "{{ start_command }}"
    __stop_command: "{{ stop_command | default('/bin/kill -INT $MAINPID') }}"
    __enabled: "{{ enabled | default(true) }}"
    __started: "{{ started | default(true) }}"


- name: "Systemd 設定ファイル生成"
  copy:
    src: files/systemd/system/template.service
    dest: /etc/systemd/system/{{ __service_name }}.service.ansible
    owner: root
    group: root
    mode: 0644
  changed_when: false


- replace:
    path: /etc/systemd/system/{{ __service_name }}.service.ansible
    regexp: "{{ item.regexp }}"
    replace: "{{ item.replace }}"
  changed_when: false
  with_items:
    - { regexp: '\${DESCRIPTION}', replace: "{{ __description }}" }
    - { regexp: '\${START_COMMAND}', replace: "{{ __start_command }}" }
    - { regexp: '\${STOP_COMMAND}', replace: "{{ __stop_command }}" }


- copy:
    remote_src: yes
    src: /etc/systemd/system/{{ __service_name }}.service.ansible
    dest: /etc/systemd/system/{{ __service_name }}.service
    mode: 0644
  notify: os_service_ubuntu16_systemd_restart_service


- file:
    path: /etc/systemd/system/{{ __service_name }}.service.ansible
    state: absent
  changed_when: false


- name: "Systemd 設定有効化"
  systemd:
    daemon_reload: yes
    name: "{{ __service_name }}.service"
    enabled: "{{ __enabled }}"


- name: "Systemd サービス起動"
  systemd:
    name: "{{ __service_name }}.service"
    state: started
  when: __started

os/serviceロールで参照するテンプレートファイル systemd/system/template.service

[Unit]
Description = ${DESCRIPTION}

[Service]
ExecStart=${START_COMMAND}
ExecStop=${STOP_COMMAND}
Restart=always
Type=simple
PrivateTmp=no

[Install]
WantedBy=multi-user.target

command/file-creationロールの主なタスク setup-Ubunt16.yml

- set_fact:
    __command: "{{ command | default('/usr/bin/touch ' + path) }}"
    __path: "{{ path }}"
    __changed_when: "{{ changed_when | default(true) }}"


- shell: |
    pwd
  register: __pwd_result
  changed_when: false


- set_fact:
    __chdir: "{{ chdir }}"
  when: chdir is defined


- set_fact:
    __chdir: "{{ __pwd_result.stdout_lines[0] }}"
  when: chdir is not defined


- stat:
    path: "{{ __path }}"
  register:  __stat_result


- shell: "{{ __command }}"
  args:
    chdir: "{{ __chdir }}"
  when: not __stat_result.stat.exists
  register: __shell_result

適用すると、(サーバ側の設定は済んでいるもとして)VPNが構成される。
ここでAnsibleで接続できるようにしていたSSHポート転送は不要になったので削除しておく。

本当は本サービスを有効にした状態でRaspberryPi3の電源入れるだけでVPN接続、を期待していたのだが、実際にはそうはできなかった。
詳細を確認していないが、誰もログインしていない状態ではLAN(無線LAN)につながらないようで、TTYで誰かログインしっぱなしにする必要がある。
ログアウトしてしまうと再度ネットワークから孤立する。
Ubuntu起動時にWLANも起動しっぱなしにする設定をどこかで調査する必要がありそう。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です