とーますメモ

Ruby on Rails / Goなどの学習メモ

【Chrome】httpのアドレスを入力してるのに、強制的にhttpsでリクエストされてしまう現象を止める方法

以下参考
How to stop an automatic redirect from “http://” to “https://” in Chrome - Super User

chrome://net-internals/#hstsを開いて、
「Delete domain security policies」の項目に、対象のURLを入力して、Deleteボタンを押せば強制的にhttpsリクエストにされるなくなる。
また「chrome://settings/clearBrowserData」を開いて、キャッシュを消しておく。

自分の場合は、キャッシュだけ消せば、httpsでリクエストされなくなった。

【Ansible】Ubuntu 18.04でのMySQL設定

MySQLの設定ファイルであるmy.cnfの場所の確認方法

$ mysql --help | grep my.cnf
/etc/my.cnf /etc/mysql/my.cnf ~/.my.cnf

左から順に読み込まれる。

Default options are read from the following files in the given order:
/etc/my.cnf /etc/mysql/my.cnf ~/.my.cnf

by mysql --help

要は以下の順

① /etc/my.cnf
② /etc/mysql/my.cnf
③ ~/.my.cnf

ただし、同じ設定値が後続のファイルにある場合、後続の設定値で上書きされるので注意!
my.cnf の読み込む順番でハマったのでまとめる - Qiita
第31回 MySQLのオプションファイル my.cnfの豆知識[その1]:MySQL道普請便り|gihyo.jp … 技術評論社
my.cnfに設定したsql_modeが効かなくて困った話

※1 設定ファイルを見つけるコマンド

$ sudo find / -name "*my*cnf"

※2 有効な設定オプション一覧を確認するコマンド

$ my_print_defaults mysqld


MySQLをインストール後、①と③は存在していないが②は存在している。
シンプルに②の内容を気にせず設定をするのであれば、①に新規で設定を書いてもよいが
自分の場合は、既にある設定をベースに、必要な設定を上書きする方式で設定を行いため
②のファイルを変更するやり方を選択する。

※ ちなみに~/.my.cnfについては以下のような情報を見つけた。

To keep the things simple place all custom server settings into the /etc/my.cnf file. Some user-specific options can be stored in the ~/.my.cnf where ~/ means "the homedir for the given system user". Also note the leading dot in the user-specific file name. That file is used for command-line client utilities like mysql or mysqldump only, not for server. Therefore the [mysqld] section can be completely omitted in the user-specific config file.

mysql - Where should I store a custom ".my.cnf" file? - Database Administrators Stack Exchange
要は、~/.my.cnfの設定は、mysqlやmysqldumpなどのコマンド使用時のみ有効であり、mysql全体の設定としては有効ではないとのことらしい。
そして設定の中で比重が多い[mysqld]の設定は、完全に除外されるとのこと。まあなぜこの設定が各ユーザごとに設定が可能なホームディレクトリに設定できるのかを
考えれば納得が行く。じゃあどういう場合に使用するのかというと、以下のリンクのようなIDやパスワードを省略してログインしたい場合などがある。
.my.cnf - mysql user & password


②のファイルの中身を見ると以下の記述がある。

#
# The MySQL database server configuration file.
#

.....

# * IMPORTANT: Additional settings that can override those from this file!
#   The files must end with '.cnf', otherwise they'll be ignored.
#

!includedir /etc/mysql/conf.d/
!includedir /etc/mysql/mysql.conf.d/

「!includedir」というディレクティブがあるが、これは指定されたディレクトリ内の.cnfファイルを設定ファイルとして読み込むという意味。
各ディレクトリを見たが、通常設定ファイルが書かれているファイルは「/etc/mysql/mysql.conf.d/mysqld.cnf」だった。
ただし設定を変更または追記するのに、このファイルを弄るのではなく、設定が後続の設定で上書きされる仕組みを活かし
「/etc/mysql/my.cnf」に追記する方式で設定を記述する。

tasks/main.yml

---
# @see https://github.com/bennojoy/mysql/blob/master/tasks/main.yml

# @see https://stackoverflow.com/questions/42267299/ansible-install-mysql-5-7-set-root-user-password?rq=1
- name: Specify MySQL root password before installing
  debconf: name='mysql-server' question='mysql-server/root_password' value='{{mysql_root_user_pass | quote}}' vtype='password'
  tags: mysql

- name: Confirm MySQL root password before installing
  debconf: name='mysql-server' question='mysql-server/root_password_again' value='{{mysql_root_user_pass | quote}}' vtype='password'
  tags: mysql

- name: Install the mysql packages in Debian derivatives
  apt:
    name: ['mysql-server']
    state: present
  tags: mysql

# select user,host,authentication_string from mysql.user;
#
# @see http://docs.ansible.com/ansible/latest/modules/mysql_user_module.html
# check_implicit_admin: Check if mysql allows login as root/nopassword before trying supplied credentials.
- name: remove anonymous users by empty name
  mysql_user: name='' host_all=yes login_user=root login_password={{ mysql_root_user_pass }} check_implicit_admin=yes state=absent

- name: Create the database's
  mysql_db: name={{ item.name }} login_user=root login_password={{ mysql_root_user_pass }} encoding=utf8 state=present
  with_items:
    - "{{ mysql_db }}"
  when: mysql_db|lower() != 'none'
  tags: mysql

- name: Create the database users
  mysql_user:
    name:                 "{{ item.name }}"
    password:             "{{ item.pass }}"
    priv:                 '{{ item.priv|default("*.*:ALL") }}'
    state:                present
    host:                 '{{ item.host | default("localhost") }}'
    login_password:       "{{ mysql_root_user_pass }}"
    check_implicit_admin: yes
  with_items:
    - "{{ mysql_users }}"
  when: mysql_users|lower() != 'none'
  tags: [mysql, mysql_user]

- name: Append my.cnf into {{ mysql_config_file }}
  blockinfile:
    dest: "{{ mysql_config_file }}"
    block: "{{ lookup('template', 'cnf.j2') }}"
    marker: "# {mark} ANSIBLE MANAGED BLOCK"
  notify: restart mysql
  tags: [mysql, mysql_config]

- name: Create slow query log file (if configured).
  command: "touch {{ mysql_slow_query_log_file }}"
  args:
    creates: "{{ mysql_slow_query_log_file }}"
    warn: false
  when: mysql_slow_query_log_enabled
  tags: [mysql, mysql_config]

- name: Set ownership on slow query log file (if configured).
  file:
    path: "{{ mysql_slow_query_log_file }}"
    state: file
    owner: mysql
    group: mysql
    mode: 0640
  when: mysql_slow_query_log_enabled
  tags: [mysql, mysql_config]

- name: Start the mysql services
  service: name=mysql state=started enabled=yes
  tags: mysql

# @see: https://qiita.com/park-jh/items/34e6434d71e685a48f07#mysqld---initialize-insecure%E3%81%AE%E5%A0%B4%E5%90%88
- name: run mysql_secure_installation
  command: mysql_secure_installation -u root -p'{{ mysql_root_user_pass }}' -D
  changed_when: false
  tags: mysql

defaults/main.yml

---

# @see: https://qiita.com/mamy1326/items/9c5eaee3c986cff65a55
# @see: https://github.com/geerlingguy/ansible-role-mysql

mysql_config_file: /etc/mysql/my.cnf

mysql_sql_mode: ''

# Slow query log settings.
mysql_slow_query_log_enabled: true
mysql_slow_query_log_file: /var/log/mysql-slow.log
mysql_slow_query_time: "2"



# Memory settings (default values optimized ~512MB RAM).
mysql_key_buffer_size: "256M"
mysql_max_allowed_packet: "64M"
mysql_table_open_cache: "256"
mysql_sort_buffer_size: "1M"
mysql_read_buffer_size: "1M"
mysql_read_rnd_buffer_size: "4M"
mysql_myisam_sort_buffer_size: "64M"
mysql_thread_cache_size: "8"
mysql_query_cache_type: "0"
mysql_query_cache_size: "16M"
mysql_query_cache_limit: "1M"
mysql_max_connections: "151"              # default: 151
mysql_tmp_table_size: "16M"
mysql_max_heap_table_size: "16M"
mysql_group_concat_max_len: "1024"
mysql_join_buffer_size: "262144"



# InnoDB settings.
mysql_innodb_file_per_table: "1"
# Set .._buffer_pool_size up to 80% of RAM but beware of setting too high.    ex) server memory: 8G => 6G
mysql_innodb_buffer_pool_size: "1600M"
# Set .._log_file_size to 25% of buffer pool size. but maximum size is 64M    ex) innodb_buffer_pool_size: 6G => 1.25G
mysql_innodb_log_file_size: "64M"
mysql_innodb_log_buffer_size: "16M"
mysql_innodb_flush_log_at_trx_commit: "1"
mysql_innodb_lock_wait_timeout: "50"

templates/cnf.j2

[mysqld]
{% if mysql_sql_mode %} sql_mode = {{ mysql_sql_mode }} {% endif %}

{% if mysql_slow_query_log_enabled %}
# Slow query log configuration.
slow_query_log = 1
slow_query_log_file = {{ mysql_slow_query_log_file }}
long_query_time = {{ mysql_slow_query_time }}
{% endif %}



# Memory settings.
# key_buffer_size = {{ mysql_key_buffer_size }}
# max_allowed_packet = {{ mysql_max_allowed_packet }}
# table_open_cache = {{ mysql_table_open_cache }}
# sort_buffer_size = {{ mysql_sort_buffer_size }}
# read_buffer_size = {{ mysql_read_buffer_size }}
# read_rnd_buffer_size = {{ mysql_read_rnd_buffer_size }}
# myisam_sort_buffer_size = {{ mysql_myisam_sort_buffer_size }}
# thread_cache_size = {{ mysql_thread_cache_size }}
# query_cache_type = {{ mysql_query_cache_type }}
# query_cache_size = {{ mysql_query_cache_size }}
# query_cache_limit = {{ mysql_query_cache_limit }}
# max_connections = {{ mysql_max_connections }}
# tmp_table_size = {{ mysql_tmp_table_size }}
# max_heap_table_size = {{ mysql_max_heap_table_size }}
# group_concat_max_len = {{ mysql_group_concat_max_len }}
# join_buffer_size = {{ mysql_join_buffer_size }}



# InnoDB settings.
innodb_file_per_table = {{ mysql_innodb_file_per_table }}
innodb_buffer_pool_size = {{ mysql_innodb_buffer_pool_size }}
innodb_log_file_size = {{ mysql_innodb_log_file_size }}
innodb_log_buffer_size = {{ mysql_innodb_log_buffer_size }}
innodb_flush_log_at_trx_commit = {{ mysql_innodb_flush_log_at_trx_commit }}
innodb_lock_wait_timeout = {{ mysql_innodb_lock_wait_timeout }}


[参考]
How do I identify the MySQL my.cnf file?

【Ansible】Githubのプライベート・リポジトリからGit cloneができない!!!

結論から言うと以下の流れで解決。

1)ssh-agentの設定
2)ansible.cfgの設定
3)ansible taskの設定

1) ssh-agentの設定

以下のサイトにまとまっているので、そのとおりに対応。
qiita.com

この設定を行うことで、ssh接続時のパスフレーズ入力を省略することができる。
ただし「ssh-agent」を使用すると、パスフレーズの入力を省略できるため非常に便利だが、その分セキュリティレベルは当然低下する。
作業端末から離れる場合は「ssh-agent」を停止させたり、重要なサーバ(本番サーバ等)への接続には使用しないなどのルールが推奨される。

[参考]
laboradian.com
https://32imuf.com/ssh/command/ssh-add/
https://qiita.com/naoki_mochizuki/items/93ee2643a4c6ab0a20f5

2)ansible.cfgの設定

以下をansible.cfgに追記する

[ssh_connection]
ssh_args = -o ForwardAgent=yes

[ssh_connection]ヘッダの下で、ssh_argsを設定することで、特定のsshオプションを設定できる。大体は以下のように渡すオブション毎に、「-o」を設定する

[ssh_connection]
ssh_args = -o ControlMaster=auto -o ControlPersist=60s

上記の設定では、ssh-agentのForwardAgent(エージェント転送機能)をonにしている。

3)ansible taskの設定

- name: Git clone project
  git:
    repo: "{{ project_git_repo }}"
    dest: "{{ working_direcory }}"
    version: master
    accept_hostkey: yes
  become_flags: '-E'
  tags: git


すごく大事なのは「become_flags: '-E'」

'-E'オプションの意味は「現在の環境変数を保持してコマンドを実行する」という意味になる。
【 sudo 】コマンド――スーパーユーザー(rootユーザー)の権限でコマンドを実行する:Linux基本コマンドTips(68) - @IT
Githubからcloneするため、SSHエージェント転送機能を使用する時に、サーバに環境変数として保存される「SSH_AUTH_SOCK」を
使用する必要があるためと見られる。

The agent outputs environment variable settings that this puts in place. The SSH_AUTH_SOCK environment variable is set to point to a unix-domain socket used for communicating with the agent

ssh-agent - How to configure, forwarding, protocol.

This fails if you do

> sudo git clone git@github.com:my-github-account/my-repo.git

because your environment variables are not available to the
commands running under sudo.

However, you can set the SSH_AUTH_SOCK variable for the command by
passing it on the command line like so

> sudo SSH_AUTH_SOCK=$SSH_AUTH_SOCK git clone git@github.com:my-github-account/my-repo.git

and all is well.

Git clone using ssh agent forwarding and sudo · GitHub

※1 ancible.cfg内の[privilege_escalation]ヘッダ内で以下の設定を入れても良い。

[privilege_escalation]
become_flags = -E

※2 昔の記事を探すと「sudo_flags = -E」を設定する記事も出てくるが
これは新しいバージョンでは廃止になるものなので、上記でやったほうが良い。

[参考]
Deprecation warning when using "sudo_flags" in ansible.cfg · Issue #47006 · ansible/ansible · GitHub
How do I use remote machine's SSH keys in ansible git module - Stack Overflow
Ansible and Git Permission denied (publickey) at Git Clone - Stack Overflow
Configuration file — Ansible Documentation
Understanding privilege escalation: become — Ansible Documentation

【Ansible】Nginx + Gunicornの設定

Nginx + Gunicorn + Flaskの環境構築を行った際のメモ。

流れは、Client => Nginx => Gunicorn => Flask

NginxとGunicornの接続については以下のサイトが参考になった。
Nginx + Gunicorn + Django + Aurora (MySQL) の本番環境をAnsible Playbookで構成する - Qiita
NginxとGunicornの接続をソケットからHTTPに変更した - Qiita
Gunicorn用のSystemdソケットとサービスファイルの作成 - Qiita

Socket接続とHTTP接続があるが、
NginxとGunicornを別々のサーバ上に分けて設定しない限りは、Socket接続のほうが早いので、Socket接続を採用する。

Nginxの設定

やり方は省略するが、nginx用のユーザである「nginx」ユーザを作成する。
そして以下のページと同じ設定を行い、

thoames.hatenadiary.jp

upstreamの箇所を以下のように変更するだけ。

upstream app_server {
  server unix:/run/gunicorn/socket fail_timeout=0;
}

Gunicornの設定

Pythonの環境は以下のページを参考に「pyenv + pipenv」で構築
thoames.hatenadiary.jp

defaults/main.yml (※gunicorn_app_pathは任意)

---

gunicorn_version: "20.0.4"
gunicorn_bin: "{{ working_direcory }}/.venv/bin/gunicorn"

gunicorn_app_name: "gunicorn"
gunicorn_app_path: "run:app"  # app module in run.py

gunicorn_run_dir: "/run/gunicorn"
gunicorn_config_dir: "/etc/gunicorn"
gunicorn_socket: "gunicorn.socket"
gunicorn_bind: "unix:/run/{{gunicorn_app_name}}/socket"
gunicorn_user: "{{ admin_user }}"
gunicorn_dynamic_workers: False
gunicorn_cpu_coefficient: 2
gunicorn_workers: 1

# [Log]: use nginx log functions
gunicorn_loglevel: False
gunicorn_errorlog: False
gunicorn_accesslog: False
# gunicorn_loglevel: "info"
# gunicorn_errorlog: "/var/log/gunicorn/error.log"
# gunicorn_accesslog: "/var/log/gunicorn/access.log"

gunicorn_reload: False    # only for 'development'
gunicorn_timeout: 600   # because of scraping

tasks/main.yml

---

- name: Check exist pipenv command
  shell: type pipenv
  register: exist_pipenv
  failed_when: false
  tags: gunicorn

- name: Install Gunicorn
  shell: bash -lc "pipenv install gunicorn=={{ gunicorn_version }}"
  args:
    chdir: "{{ working_direcory }}"
  when: exist_pipenv is success
  tags: gunicorn

- name: Make gunicorn config directory
  file: path={{ gunicorn_config_dir }} state=directory
  tags: [gunicorn, gunicorn_config]

- name: Configure gunicorn
  template: src=gunicorn.py.j2 dest={{ gunicorn_config_dir }}/{{ gunicorn_app_name }}.py
  notify:
    - restart socket
    - restart gunicorn
  tags: [gunicorn, gunicorn_config]

- name: Configure gunicorn socket
  template: src=gunicorn.socket.j2 dest=/etc/systemd/system/{{ gunicorn_socket }}
  notify:
    - restart socket
    - restart gunicorn
  tags: [gunicorn, gunicorn_config]

- name: Configure systemd service
  template: src=systemd.conf.j2 dest=/etc/systemd/system/{{ gunicorn_app_name }}.service mode="0755"
  notify:
    - restart socket
    - restart gunicorn
  tags: [gunicorn, gunicorn_config]

- name: Enable gunicorn service
  service: name="{{ gunicorn_app_name }}.service" enabled=yes
  tags: [gunicorn, gunicorn_config]

handlers/main.yml

---
  
- name: restart gunicorn
  service: name={{ gunicorn_app_name }} state=restarted daemon_reload=yes

- name: restart socket
  systemd: name={{gunicorn_app_name}}.socket enabled=yes state=restarted daemon_reload=yes

gunicorn.socket

[Unit]
Description=gunicorn socket

[Socket]
ListenStream={{ gunicorn_run_dir }}/socket

[Install]
WantedBy=sockets.target

gunicorn.py (gunicorn用の設定ファイル)

import multiprocessing

bind = '{{ gunicorn_bind }}'
timeout = {{gunicorn_timeout}}

{% if gunicorn_loglevel %}
loglevel = '{{ gunicorn_loglevel }}'
{% endif %}
{% if gunicorn_accesslog %}
accesslog = '{{ gunicorn_accesslog }}'
{% endif %}
{% if gunicorn_errorlog %}
errorlog = '{{ gunicorn_errorlog }}'
{% endif %}

reload = {% if gunicorn_reload %}True{% else %}False{% endif %}

{% if gunicorn_dynamic_workers %}
workers = ({{gunicorn_cpu_coefficient}} * multiprocessing.cpu_count()) + 1
{% else %}
workers = {{gunicorn_workers}}
{% endif %}

gunicorn.service (systemd用のサービス設定)

[Unit]
Description = {{ gunicorn_app_name }}
Requires = {{ gunicorn_socket }}
After = network.target

[Service]
PermissionsStartOnly = true
User = {{ gunicorn_user }}
Group = {{ gunicorn_user }}
RuntimeDirectory = {{ gunicorn_app_name }}
PIDFile = {{ gunicorn_run_dir }}/{{ gunicorn_app_name }}.pid
WorkingDirectory = {{ working_direcory }}

ExecStart = {{ gunicorn_bin }} --pid {{ gunicorn_run_dir }}/{{ gunicorn_app_name }}.pid \
                               --config {{ gunicorn_config_dir }}/{{ gunicorn_app_name }}.py \
                               {{ gunicorn_app_path }}

ExecReload = /bin/kill -s HUP $MAINPID
ExecStop = /bin/kill -s TERM $MAINPID
PrivateTmp = true

[Install]
WantedBy = multi-user.target


[その他参考]
GitHub - azavea/ansible-gunicorn: An Ansible role for installing and configuring gunicorn.
Django + Nginx + Gunicorn でアプリケーションを立ち上げる | WEBカーテンコール
ansible/gunicorn.service.j2 at master · UTNkar/ansible · GitHub
Deploying Gunicorn — Gunicorn 20.0.4 documentation

【SSH】VPS上でGithubからPullするときの設定

CI/CDの設定とかは、とりあえず忘れて単純にGithubにSSH経由で接続して、リポジトリをPullするまでの設定
ちなみに以下の環境があり、ローカルでの秘密鍵(id_rsa)と公開鍵(id_rsa.pub)がある前提。

[開発マシン(Mac)] <=> [本番サーバ] <=> [Github]

いろんなサイトを見ると、本番サーバ上に秘密鍵を置くのは危険度Maxということなので
開発マシン上の秘密鍵を用いてGithubにアクセスできる方法の「ssh-agent」という機能を使うことにする。

1)Githubに公開鍵を登録する。

id_rsa.pubの中身をコピーして、以下のサイトで公開鍵を登録するだけ。
Sign in to GitHub · GitHub
「New SSH Key」ボタンを押し、Titleには何のSSH接続のための公開鍵なのか入力し、Keyには公開鍵の中身を貼り付けて登録

ちなみにMac上で公開鍵の中身をコピーするコマンドは以下。

$ pbcopy < ~/.ssh/id_rsa.pub

2)ssh-agentへ秘密鍵の設定

鍵をssh-agentに登録

$ ssh-add -K [鍵の場所]

※「-K」 をつけると電源を落としても消えない。利便性は上がるが、セキュリティ度は下がる。

現在登録されている鍵

$ ssh-add -l

ssh-agentに鍵を登録しておけば、ssh接続時に毎回パスフレーズを入力することも回避できる。

3)ssh-agentに登録した鍵を使ってログイン

通常だと以下のコマンドでOK

$ ssh -A [鍵の名前]

ただ毎回、「-A」を叩くのがめんどくさいので「~/.ssh/config」を以下のように設定する。

$ vi ~/.ssh/config
Host prod
    ...
    ForwardAgent yes

※prodは(任意のホスト名)

以上の設定を行えば、以下のコマンドだけでssh-agentを有効にし、サーバにログインできる。

$ ssh prod

サーバのコンソールから以下のコマンドを叩けば、Githubと疎通ができることが確認できる

$ ssh -T git@github.com