無能が苦悩したメモ

無能な著者が学んだことを忘れないための覚え書きです...

Ansible:ansible-galaxyコマンドでroleのひな型を作成

ansible-galaxyコマンドを使うことでroleのひな型を作成できます。
roleに関連するディレクトリやmain.ymlを作成する手間がすこし省けるかも。

以下がコマンドとその実行結果です。
tasksやhandllersなどのディレクトリ、そして、ディレクトリの下にmain.ymlが作成されています。ここから必要なディレクトリ以外は削除していけばよさそうです。

$ ansible-galaxy init  test_role
- Role test_role was created successfully
$
$ tree test_role/
test_role/
|-- README.md
|-- defaults
|   `-- main.yml
|-- files
|-- handlers
|   `-- main.yml
|-- meta
|   `-- main.yml
|-- tasks
|   `-- main.yml
|-- templates
|-- tests
|   |-- inventory
|   `-- test.yml
`-- vars
    `-- main.yml

8 directories, 8 files

ちなみに、main.ymlの中身は基本的にコメント行のみです。

---
# tasks file for test_role

---
# vars file for test_role


role-skeltonオプション

ansible-galaxy initに--role-skeletonオプションというのがありました。
このオプションを使えば、自身で作成したロールをひな型として新たにロールを作成することができるようです。

ためしに、test_roleというroleをひな型としてtest_role2というroleを作ることにします。


test_roleは以下のような構造になっています。

$ tree test_role
test_role
|-- README.md
|-- files
|   `-- hello.txt
|-- tasks
|   `-- main.yml
`-- vars

3 directories, 3 files
$
$ cat test_role/tasks/main.yml
---
- name: copied hello.txt
  copy:
    src: hello.txt
    dest: /tmp/hello.txt
    mode: 0644


ansible-galaxy initの実行結果が以下になります。
test_roleのディレクトリ構造と同じようにtest_role2が作られているのが確認できました。また、tasks/main.ymlの中身もまるっきり同じ内容になっています。(普通のディレクトリコピーと何か違うのだろうか…)

$ ansible-galaxy init --role-skeleton ./test_role test_role2
- Role test_role2 was created successfully
$
$ tree test_role2/
test_role2/
|-- README.md
|-- files
|   `-- hello.txt
|-- tasks
|   `-- main.yml
`-- vars

3 directories, 3 files
$
$ cat test_role2/tasks/main.yml
---
- name: copied hello.txt
  copy:
    src: hello.txt
    dest: /tmp/hello.txt
    mode: 0644


Ansible:fileモジュールによるディレクトリの作成

*ansible 2.9.6での設定を想定しています

ansibleでディレクトリを作成する場合、fileモジュールを使います。
fileモジュールのstateオプションにdirectoryを指定することでディレクトリが作成できます。

オプション 説明
path 作成するディレクトリのパス
owner 所有者
group 所有グループ
mode ディレクトリのパーミッション

以下は、/tmpディレクトリの下にtest1というディレクトリを作るプレイブックです。

---
- hosts: all
  become: yes
  tasks:
    - name: test directory created
      file:
        path: "/tmp/test1"
        state: directory
        owner: "root"
        group: "root"
        mode: "775"

複数のディレクトリを作成

loopを使えば複数のディレクトリを1つのtaskで作ることができます。

---
- hosts: all
  become: yes
  tasks:
    - name: test directory created
      file:
        path: "{{ item.path }}"
        state: directory
        owner: "{{ item.owner }}"
        group: "{{ item.group }}"
        mode: "{{ item.mode }}"
      loop:
        - path: "/tmp/test1"
          owner: "root"
          group: "root"
          mode: "775"
        - path: "/tmp/test2"
          owner: "root"
          group: "root"
          mode: "775"


リモートホスト(CentOS7)に対して実行した結果が以下になります。
test1、test2が作成されています。

$ ansible-playbook -i hosts make_dir/make_multi_dir.yml

PLAY [all] *********************************************************************

TASK [Gathering Facts] *********************************************************
ok: [CentOS7.6]

TASK [test directory created] **************************************************
changed: [CentOS7.6] => (item={u'owner': u'root', u'path': u'/tmp/test1', u'group': u'root', u'mode': u'775'})
changed: [CentOS7.6] => (item={u'owner': u'root', u'path': u'/tmp/test2', u'group': u'root', u'mode': u'775'})

PLAY RECAP *********************************************************************
CentOS7.6                  : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

[root@centos7 tmp]# ll /tmp
合計 0
drwxrwxr-x. 2 root root 6  11 04:58 test1
drwxrwxr-x. 2 root root 6  11 04:58 test2

再帰的なディレクトリ作成

recurseオプションにyesを指定することで再帰的にディレクトリを作成することができます。

---
- hosts: all
  become: yes
  tasks:
    - name: test directory created
      file:
        path: "/tmp/test1/test1_1/test1_1_1"
        state: directory
        owner: "root"
        group: "root"
        mode: "775"
        recurse: yes


リモートホストに対して実行した結果が以下になります。
test1、test1_1、test1_1_1が作成されていることが確認できました。

$ ansible-playbook -i hosts make_dir/make_dir_recurse.yml

PLAY [all] *********************************************************************

TASK [Gathering Facts] *********************************************************
ok: [CentOS7.6]

TASK [test directory created] **************************************************
changed: [CentOS7.6]

PLAY RECAP *********************************************************************
CentOS7.6                  : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

[root@centos7 tmp]# ll /tmp/
合計 0
drwxrwxr-x. 3 root root 21  11 04:50 test1
[root@centos7 tmp]#
[root@centos7 tmp]# ll /tmp/test1/
合計 0
drwxrwxr-x. 3 root root 23  11 04:50 test1_1
[root@centos7 tmp]#
[root@centos7 tmp]# ll /tmp/test1/test1_1/
合計 0
drwxrwxr-x. 2 root root 6  11 04:50 test1_1_1
[root@centos7 tmp]#


以下のようなプレイブックをつくりました。
プレイブックでは/tmp/test1/test1_1、/tmp/test1/test1_2を再帰的に作成します。

この時、ディレクトリはどのような順番で作成されるのでしょうか?
また、test1の所有者・権限はどうなるのでしょうか?

---
- hosts: all
  become: yes
  tasks:
    - name: test directory created
      file:
        path: "{{ item.path }}"
        state: directory
        owner: "{{ item.owner }}"
        group: "{{ item.group }}"
        mode: "{{ item.mode }}"
      loop:
        - path: "/tmp/test1/test1_1"
          owner: "root"
          group: "root"
          mode: "775"
        - path: "/tmp/test1/test1_2"
          owner: "muknow"
          group: "muknow"
          mode: "644"

$ ansible-playbook -i hosts make_dir/make_dir_recurse2.yml -v
Using /home/muknow/.ansible.cfg as config file

PLAY [all] *********************************************************************

TASK [Gathering Facts] *********************************************************
ok: [CentOS7.6]

TASK [test directory created] **************************************************
changed: [CentOS7.6] => (item={u'owner': u'root', u'path': u'/tmp/test1/test1_1', u'group': u'root', u'mode': u'775'}) => {"ansible_loop_var": "item", "changed": true, "gid": 0, "group": "root", "item": {"group": "root", "mode": "775", "owner": "root", "path": "/tmp/test1/test1_1"}, "mode": "0775", "owner": "root", "path": "/tmp/test1/test1_1", "secontext": "unconfined_u:object_r:user_tmp_t:s0", "size": 6, "state": "directory", "uid": 0}
changed: [CentOS7.6] => (item={u'owner': u'muknow', u'path': u'/tmp/test1/test1_2', u'group': u'muknow', u'mode': u'644'}) => {"ansible_loop_var": "item", "changed": true, "gid": 1001, "group": "muknow", "item": {"group": "muknow", "mode": "644", "owner": "muknow", "path": "/tmp/test1/test1_2"}, "mode": "0644", "owner": "muknow", "path": "/tmp/test1/test1_2", "secontext": "unconfined_u:object_r:user_tmp_t:s0", "size": 6, "state": "directory", "uid": 1001}

PLAY RECAP *********************************************************************
CentOS7.6                  : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

[root@centos7 tmp]# ll /tmp
合計 0
drwxrwxr-x. 4 root root 36  11 05:16 test1
[root@centos7 tmp]# ll /tmp/test1/
合計 0
drwxrwxr-x. 2 root   root   6  11 05:16 test1_1
drw-r--r--. 2 muknow muknow 6  11 05:16 test1_2

/tmp/test1の所有者・所有グループはrootとなりました。

実行ログを見ると、1回目の実行(1回目のchanged)で/tmp/test1、/tmp/test1_1が作成され、2回目の実行(2回目のchanged)で/tmp/test1/test1_2が作成されます。所有者やパーミッションディレクトリの作成時に設定されるようです。2回目の実行時は/tmp/test1は既に存在するため作成されず、その結果、/tmp/test1の所有者・所有グループはrootとなったようです。

Ansible:copyモジュールについてメモ

*ansible 2.9.6での設定を想定しています

copyモジュールはコントロールノードのファイルをリモートホストへコピーするモジュールです。
srcでコピー元のファイル、destでコピー先を指定します。

---
- hosts: all
  tasks:
    - name: copied hello.txt
      copy: 
        src: hello.txt
        dest: /tmp/hello.txt


mode:ファイルの権限設定

modeオプションをつけることでコピー先のファイルの権限を設定できます。
modeオプションをつけない場合、コピー元のファイルと同じ権限でコピー先のファイルは作られます。

linuxのchmodコマンドのように8進数で権限を指定できます。ansibleに8進数として認識させるため「0644」のように先頭に0をつけるのがポイントです。先頭に0をつけないとansibleに10進数として認識されてしまい予期しない動作をする可能性があります。

---
- hosts: all
  tasks:
    - name: copied hello.txt
      copy: 
        src: hello.txt
        dest: /tmp/hello.txt
        mode: 0644


ちなみに、'644'のようにシングルクォートで囲んでもOKです。

---
- hosts: all
  tasks:
    - name: copied hello.txt
      copy: 
        src: hello.txt
        dest: /tmp/hello.txt
        mode: '644'


force:コピーする条件を指定

forceオプションでyesを指定すれば、コピー先に既にファイルが存在する場合、ファイルの中身を比較して差分がある場合はコピーを実施します。noを指定した場合、コピー先にファイルが存在しない場合にのみ、コピーを実施します。なお、forceオプションはデフォルトでyesが指定されています。

---
- hosts: all
  tasks:
    - name: copied hello.txt
      copy: 
        src: hello.txt
        dest: /tmp/hello.txt
        mode: 0644
        force: no


backup:コピー時にバックアップを取得

backupオプションでyesを指定すると、コピー先に既にファイルが存在する場合、そのファイルのバックアップを取得します。

バックアップはコピー先のファイルが存在するディレクトリと同じディレクトリに作成されます。バックアップのファイル名は「(元のファイル名).(タイムスタンプ)」となります。

下の例であればバックアップとして「/tmp/hello.txt.2020-12-31@12:00:11~」のようなファイルが作成されます。

---
- hosts: all
  tasks:
    - name: copied hello.txt
      copy: 
        src: hello.txt
        dest: /tmp/hello.txt
        mode: 0644
        backup: yes


remote_src:リモートホスト内でのファイルコピー

remote_srcオプションでyesを指定すると、リモートホスト内でファイルのコピーができます。
以下の例では、/tmp/hello.txtをコピーして/tmp/hello2.txtを作成します。

---
- hosts: all
  tasks:
    - name: copied hello.txt
      copy: 
        src: /tmp/hello.txt
        dest: /tmp/hello2.txt
        mode: 0644
        remote_src: yes

GitLabからgit cloneできない

VPSを借りてGitLabサーバを構築してみました。構築も終わり、テスト用プロジェクトを作ってgit cloneしようとしたところ以下のようなエラーがでてクローンできませんでした。

$ git clone https://gitlab.●●●●●●●●/muknow/test.git
Cloning into 'test'...
fatal: unable to access 'https://gitlab.●●●●●●●●/muknow/test.git/': server certificate verification failed. CAfile: /etc/ssl/certs/ca-certificates.crt CRLfile: none

原因

調べてみるとサーバ構築時に自己証明書(オレオレ証明書)を使っていたことが原因でした。サーバ側の証明書の妥当性が確認できず、アクセスできていなかったようです。(エラーメッセージにもserver certificate verification failedと書かれていますね)

解決策

サーバ側の自己証明書をクライアント側に登録します。
以下はUbuntuで実施した内容です。

手順1:自己証明書をダンプする

登録する必要のある証明書をダンプします。ダンプにはopensslコマンドを使います。

$ HOST=gitlab.●●●●●●●●
$ PORT=443
$ openssl s_client -connect ${HOST}:${PORT} -showcerts </dev/null 2>/dev/null |  sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > my_gitlab.crt

my_gitlab.crtは以下のようになっています。

-----BEGIN CERTIFICATE-----
(省略)
-----END CERTIFICATE-----
コマンドについてざっくり説明
  • opensslコマンドのs_clientはSSL/TLS通信をおこなうクライアントプログラムです。
  • s_clientでは-quietをつけなければ接続時にサーバ証明書は出力されます。-showcertsをつけることで証明書チェインをすべて出力できます。
  • そのままだとサーバに接続されっぱなしの状態となり、コンソールも入力待ちになります。そのため</dev/nullで通信を終了します。2>/dev/nullで標準エラー出力を破棄しています。
  • opensslコマンドで得られた結果からsedコマンドにより証明書を抜き出します。
  • 証明書はmy_gitlab.crtというファイルに保存します。


手順2:証明書の登録

ダンプした証明書を/usr/share/ca-certificates/に移動します。ファイルの所有者もrootに変更します。

$ mv my_gitlab.crt /usr/share/ca-certificates/
$ chown root:root /usr/share/ca-certificates/my_gitlab.crt

/etc/ca-certificates.confの末尾に「my_gitlab.crt」を追記します。(正確には追加したい証明書の/usr/share/ca-certificatesからの相対パスです)

$ sudo echo "my_gitlab.crt" >> /etc/ca-certificates.conf

最後にupdate-ca-certificatesを実行します。

$ sudo update-ca-certificates  

/etc/ssl/certsに/usr/share/ca-certificates/my_gitlab.crtのシンボリックリンクが存在すればOKです。

$ ls /etc/ssl/certs/| grep my_gitlab
my_gitlab.pem
$ ls -l /etc/ssl/certs/my_gitlab.pem
lrwxrwxrwx 1 root root 40 Dec 30 15:03 /etc/ssl/certs/my_gitlab.pem -> /usr/share/ca-certificates/my_gitlab.crt

無事、クローンできました。

$ git clone https://gitlab.●●●●●●●●/muknow/test.git
Cloning into 'test'...
remote: Enumerating objects: 6, done.
remote: Counting objects: 100% (6/6), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 6 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (6/6), done.


おわりに

git cloneすることはできましたが、クライアントごとにこの設定をしなければいけないのが面倒ですね…

Powershell:ファイル内の文字列を検索する

linuxだとgrepコマンドを使えば文字列検索できますが、PowerShellだどうするのか知らなかったのでメモです。どうやらSelect-Stringという命令(PowerShellではコマンドレットと呼ぶ)でできそうです。

試しにtest1.txtというファイルを用意して、「あああ」という文字を検索してみました。が検索ヒットせず…

PS C:\test> Get-Content .\test1.txt
あああ
かかか
さささ
たたた
ななな
PS C:\test>
PS C:\test> Select-String "あああ" .\test1.txt
PS C:\test>

どうやら日本語の場合は以下のようにEncodingでdefaultを指定しなければならないようです。指定したところ想定通り結果が得られました。

PS C:\test> Select-String "あああ" .\test1.txt -Encoding default
test1.txt:1:あああ


PS C:\test>


以下のようにすればカレントフォルダ以下のすべてのtxtファイルを検索できます。

PS C:\test> ls *.txt -Recurse -Force | Select-String "あああ"  -Encoding default

Ubuntu:管理者権限でのコマンド実行

*本記事はUbuntu 18.04LTSでの設定を想定しています

suコマンドが使えない

OSインストール直後のUbuntuでsuコマンドを実行すると以下のような結果になり、rootユーザーになれない。Ubuntuではデフォルトでrootユーザーのパスワードが設定されていないため、それが原因らしいです。

muknow@Ubuntu18:~$ su
Password: 
su: Authentication failure

新規ユーザーでsudoを使えるようにする

adduserコマンドやuseraddコマンドを使うことで新規にユーザーを作成できます。

muknow@Ubuntu18:~$ sudo adduser test1

しかし、新規ユーザーにおいてsudo権限でコマンドを実行しようとしてもできません。

test1@Ubuntu18:~$ sudo adduser test2
[sudo] password for test1: 
test1 is not in the sudoers file.  This incident will be reported.

上の出力では「test1はsudoresファイルに記載がない」と怒られています。sudoコマンドを実行するためには/etc/sudoresファイルを編集して、ユーザーに権限を付与しなければならないのです。なお、編集は直接sudoresを書き換えるのではなく、visudoというsudoresを編集する専用のコマンドを使います。


ただ、sudoresを編集せずとも、ユーザーをsudoグループに追加すればsudoコマンドを実行できるようになります。sudoグループはデフォルトで作成されており、sudoresファイル内にもsudoグループに関する設定が記載されています。ちなみに設定の意味は「sudoグループに属するユーザーは、他のすべてのユーザー権限・グループ権限で、すべてのコマンドを実行できる」といった感じです。

muknow@Ubuntu18:~$ sudo cat /etc/sudoers
  (省略)
# Allow members of group sudo to execute any command
%sudo	ALL=(ALL:ALL) ALL
  (省略)

ユーザーをグループに追加するにはgpasswdコマンドを使用します。-aオプションがグループに追加するためのオプションです。

muknow@Ubuntu18:~$ sudo gpasswd -a test1 sudo
Adding user test1 to group sudo

idコマンドを実行するとtest1がsudoグループに属していることが確認できます。

muknow@Ubuntu18:~$ id test1
uid=1003(test1) gid=1004(test1) groups=1004(test1),27(sudo)

tshark:出力フォーマットをfieldsに変更

通常、tsharkコマンドを実行すると以下のような形式で出力が得られます。

$ tshark -r test.pcap -Y "http.response"
   12   0.270305 104.20.31.112192.168.164.128 HTTP 1442 HTTP/1.1 200 OK  (text/html)
   55   0.329930 104.20.31.112192.168.164.128 HTTP 789 HTTP/1.1 200 OK  (GIF89a)
   65   0.333970 104.20.31.112192.168.164.128 HTTP 1134 HTTP/1.1 200 OK  (GIF89a)
   73   0.351571 104.20.31.112192.168.164.128 HTTP 1074 HTTP/1.1 200 OK  (text/css)
  109   0.573289 104.31.66.122192.168.164.128 HTTP 640 HTTP/1.1 200 OK  (text/html)
  137   0.622275 202.32.15.217192.168.164.128 HTTP 1026 HTTP/1.1 200 OK  (GIF87a)
  149   0.675307 52.196.10.155192.168.164.128 HTTP 1328 HTTP/1.1 200 OK  (text/javascript)
 (省略)


tsharkの-Tオプションでfieldsを指定すれば自分が必要な情報だけを出力するようにフォーマットを変更できます。この時、-eオプションにフィールド名を指定することで出力する情報を設定します。具体的なコマンドは以下のようになります。

$ tshark -r pcapファイル -T fields -e フィールド名1 -e フィールド名2


フィールド名はフィルタに設定できる「ip.addr」や「tcp.dstport」などです。ちなみに、パケット中に指定したフィールドが存在しない場合、その部分には何も出力されません。



具体例として、HTTPレスポンスの送信元IPアドレス、Content-Type、Content-Lengthだけを出力させるようにしてみます。この場合はtsharkの出力結果をtsvファイルとして保存しています。

$ tshark -r test.pcap -Y "http.response" -T fields -e ip.src -e http.content_type -e http.content_length > test.tsv


test.tsvの中身は以下のようになります。

104.20.31.112	text/html	
104.20.31.112	image/gif	1787
104.20.31.112	image/gif	19990
104.20.31.112	text/css	
104.31.66.122	text/html	
202.32.15.217	image/gif	15782
52.196.10.155	text/javascript	966
 (省略)


-Eオプションでheaderを指定すればフィールド名をヘッダとして出力させることもできます。また、区切り文字をタブ以外の文字にしたい場合は-Eオプションでseparatorを指定します。下記コマンドではヘッダの出力と区切り文字に「,」を使うことを指定し、出力結果をtest.csvファイルに保存しています。

$ tshark -r test.pcap -Y "http.response" -T fields -E header=y -E separator=, -e ip.src -e http.content_type -e http.content_length > test.csv


test.csvの中身は以下のようになります。

ip.src,http.content_type,http.content_length
104.20.31.112,text/html,
104.20.31.112,image/gif,1787
104.20.31.112,image/gif,19990
104.20.31.112,text/css,
104.31.66.122,text/html,
202.32.15.217,image/gif,15782
52.196.10.155,text/javascript,966
 (省略)