Carrierwave から ActiveStrage に乗り換えたときに、予想外だったこと

route の一番下に、404用のアクションを定義していると、画像が表示されない

$ config/route.rb

# ...

get '*path', to: 'application#error_404'

これは、active storage が定義する 以下よりも先に、上記が処理されているため。

get "/rails/active_storage/blobs/:signed_id/*filename" => "active_storage/blobs#show", as: :rails_service_blob, internal: true

host を指定してファイルパスを取得したい

url_for を使用してフルパスを取得するように書かれていることが多いが、このメソッドはオプションでhostを指定できない。

polymorphic_url を利用すると、以下のようにすれば設定できる。

polymorphic_url(hoge.image, host: "http://*****/")

同時にActiveJob を使っていると、データのアップロードが非同期に自動で切り替わる

config.active_job.queue_adapter を inline ではなく、別の例えば active_elastic_job などにしていると、
内部で ActiveStorage::AnalyzeJob が利用されているため、勝手に非同期となる。
そのときの デフォルトの quere 名が default のため、このままではエラーになってしまう。

config.active_storage.queue で 別の quere名を設定する。

Alexa 開発時、スキルの呼び出し名の変更が反映されない

2018/09/21現在、スキルの呼び出し名を変更しても、それだけでは反映されない。
ストアに公開するための認定時にも、古い呼び出し名のまま動作確認が行われてしまう。

これを反映させるには、一度テストを無効にして、再度有効にすることで反映される。

f:id:unching-star:20180921202922p:plain

それにしても、メールで送られてくる、Alexaスキル 認定に関するフィードバックは、
指摘に関する再現方法も細かく記載され、修正方法まで書かれているため、とてもわかりやすい。
Apple も見習って欲しいですね。

Mysql8 に接続できないとき

ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: NO)

MariaDBと同じく、ログイン認証プラグインがかわり、現在のユーザーとmysqlにログインしようとしているユーザーが異なるとログインできない状態になっている。
そのままの状態でも、rootのLinuxユーザーから実行すれば、mysql -u root で問題なく接続できる。

現在のプラグイン確認
use mysql;
SELECT user, host, plugin FROM user;

+------------------+-----------+-----------------------+
| user             | host      | plugin                |
+------------------+-----------+-----------------------+
| mysql.infoschema | localhost | caching_sha2_password |
| mysql.session    | localhost | caching_sha2_password |
| mysql.sys        | localhost | caching_sha2_password |
| root             | localhost | auth_socket           |
+------------------+-----------+-----------------------+
プラグインを変更する
ALTER USER 'root'@'localhost' identified WITH mysql_native_password;

Your password has expired. To log in you must change it using a client that supports expired passwords.

パスワードを再設定する
ALTER USER `root`@`localhost` IDENTIFIED BY '', `root`@`localhost` PASSWORD EXPIRE NEVER;

UPS Eaton IPP を利用する

Daitron の製品、無停電電源装置(UPS) イートンCentOS 7 で利用するときの設定方法です。
Lenovo UPS 1000VA、UPS 1500VA は、EATON社製UPSです。

CentOS 7.4 インストール手順 | 無停電電源装置(UPS) | イートン
ここの説明で大体は大丈夫ですが、もっと良い情報を以下に書きます。

RPMインストール

rpm Download

rpm は、こちらからダウンロードする。CentOSなので、Red Hat と書かれているもの。

Intelligent Power® Protector v1.53
rpm Software Installer
x86_64 for Red Hat / Fedora / (Open) Suse
https://powerquality.eaton.com/support/software-drivers/downloads/de-intelligent-power-protector.asp

install は通常通り
$ rpm -ivh ipp-linux-1.53.150-1.x86_64.rpm


これで起動まで出来ています。

ファイヤーウォールの設定変更

firewall-cmd --permanent --add-port=80/tcp
firewall-cmd --permanent --add-port=5000/tcp
firewall-cmd --permanent --add-port=4679/tcp
firewall-cmd --permanent --add-port=4680/tcp
firewall-cmd --permanent --add-port=4679/udp
firewall-cmd --permanent --add-port=4680/udp
firewall-cmd --reload

これで設定は大方完了です。

設定ファイルを編集する

http://CentOSのIPアドレス:4679/

こちらにアクセスすると、IPP設定ページが表示されます。
初期のログイン、パスワードは、 admin、admin です。

この画面で編集した内容は、以下の場所に保存されています。

/usr/local/Eaton/IntelligentPowerProtector/configs/config.js

chefなどで管理する場合は、GUIで編集後に、設定ファイルをコミットするのがよいと思います。
設定編集後は、 再起動します。

$ service Eaton-IPP restart

以上です。

Serverless Framework で、functions block に定義した 関数名を参照する

$ serverless.yml

service: aws-nodejs # NOTE: update this with your service name

provider:
  name: aws
  runtime: nodejs8.10
  region: ap-northeast-1
  stage: ${opt:stage, 'development'}

...

functions:
  webApp:
    handler: 'handler.main'
    #name を指定しない
    ...
  
  WriteS3:
    handler: 'hoge.handle'

  ...

resources:
  Resources:
    ...

このように定義した時、lambda function の名前や、Arn を取得したい場合、Resources blockに定義したわけでは無いため、 `{Ref: webApp}` や `{ "Fn::GetAtt": ["webApp", "Arn"] }` では取得できません。

webApp の場合は、 WebAppLambdaFunction とすると、参照できます。
WriteS3 の場合は、WriteS3LambdaFunction となります。

AWS 利用時に、HTTPS 通信する方法いくつか

ドメイン認証でよいのであれば、https で通信する時は、AWS Certificate Managerで証明書を取得し、CloudFront で選択すれば良いが、いろんな条件でそれが出来ないことがあるため、そのときの対処方法と問題点です。

AWS Certificate Manager で、CNAME を利用した、DNS検証を使う

特に制限が無く、自身でRoute53やお名前.comなどのアカウントがある場合は、この方法で証明書を取得すればよい。
ただしこれの問題点は、文字の先頭に _ を使えないサービスでドメインを取得している場合は、利用できない。

AWS Certificate Manager で、Eメール検証を使う

WHOIS クエリで返された連絡先情報を使用するそのドメインの登録者、技術担当者、管理者の連絡先、ドメイン名の前に admin@、administrator@、hostmaster@、webmaster@、および postmaster@ のEメールが受信できるのであれば、検証できる。

受信を確認する担当者が別にいるような場合で、認証を行うまで3日以上かかる場合は、タイムアウトになってしまい、再度検証メールを送る必要がある。

そもそもメールが受信できない場合は利用出来ない。

AWS Certificate Manager で取得したリージョンを us-east-1 以外で取得してしまった場合

何らかの理由で、us-east-1 で取得し直すことが不可能な場合は、取得した証明書を Elastic Load Balancing に割り当てるしかない。
Elastic Load Balancing の後ろに EC2 を用いれば、あとはどうにか出来る。

ただしサーバー費が無駄に必要になる。

認証するドメインをNSレコードで登録

Route53で Hosted Zone を作成し、NSレコードとして別DNSサービス側に登録する。
これが出来れば、ドメインを所持している時と同じように設定できる。
サブドメイン単位でも登録出来るため、かなり自由がきく。

Let's Encrypt を用いて、証明書を取得する。

現在利用されているデバイスで、証明書が無効で通信できないようなことは起こっていないので、Let's Encryptは実用的に使えます。

これの利点は、利用するドメインの設定が終わっていて、特定のパスにファイルを置き、httpでアクセスできる状態なら、検証が出来るところ。
90日間で証明書の期限が切れてしまう。

# mac では実行できない。
$ git clone https://github.com/letsencrypt/letsencrypt.git
$ cd letsencrypt
$ ./certbot-auto certonly -m <EMAIL> -d <DOMAIN>

途中で、指定のURL、ファイルの内容で、ファイルをアップロードするように指示があるので、その通りにファイルを置く。
続けると、証明書が生成される。必要なのは、cert.pem (証明書), privkey.pem (プライベートキー), chain.pem (中間証明書)。



これで何かがおきた時、乗り切れるはず。

AWS Mysql に IAM Role で接続する

MySQL 5.6 マイナーバージョン 5.6.34 以降、MySQL 5.7 マイナーバージョン 5.7.16 以降、Amazon Aurora 1.10 以降 で対応している。

ただし、1秒間あたりの接続開始の認証回数が20回と少ないため、アクセスの多いシステムでは採用できない。
IAM データベース認証の制限

Lambda をVPC内に入れてしまうと、接続まで時間がかかるため、それを回避するために使ったりする。

1. RDS で Mysql を起動

  • パブリックアクセスを有効に変更
  • SSL 証明書をダウンロードする ( rds-combined-ca-bundle.pem )
  • セキュリティーグループは、0.0.0.0/0 などに変える。

パブリックアクセスを有効にする代わりに、SSL接続することになる。

2. Mysql にユーザーを作る

RDS 作成時の Root ユーザーで、IAM 用のユーザーを作る。

プラグインを有効にしユーザーを作成

CREATE USER 'iam_user'@'%' IDENTIFIED WITH AWSAuthenticationPlugin as 'RDS';

SSL 接続を強制する

GRANT SELECT,INSERT,UPDATE,DELETE ON dbdayo.* to 'iam_user'@'%' REQUIRE SSL;

3. IAM Policy を作成

作ったポリシーは、Roleなどにアタッチする。
太字の部分は固定値です。rds-db の部分も、これで間違いなく、Webコンソール上はエラーが出るが、あなたが正しい。

arn:aws:rds-db:{RDSのリージョン}:{AWS アカウントID}:dbuser:{RDSのリソースID}/{DBのUser名}

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "rds-db:connect"
            ],
            "Resource": [
                "arn:aws:rds-db:ap-northeast-1:1234567890:dbuser:db-AAAAARRRRRR/iam_user"
            ]
        }
    ]
}

4. Lambda や EC2 にRole を関連付ける

作ったPolicyをRoleにアタッチして、さらにそのRoleをLambdaやEC2に関連付ける。

5a. EC2などからプログラムで接続

RDS_HOST = 'dbdayo.xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com'
RDS_USER = 'iam_user'
RDS_REGION = 'ap-northeast-1'

def lambda_handler(event, context):
    rds = boto3.client('rds')

    password = rds.generate_db_auth_token(
        DBHostname=RDS_HOST,
        Port=3306,
        DBUsername=RDS_USER
    )
 
    conn = mysql.connector.connect(
        user=RDS_USER,
        password=password,
        host=RDS_HOST,
        database='mysql',
        charset='utf8',
        ssl_verify_cert=True,
        ssl_ca='rds-combined-ca-bundle.pem' # ダウンロードしたファイルを指定
    )

5b. EC2からコマンドで接続する

aws rds generate-db-auth-token でパスワードを生成している、認証部分がポイント

mysql -u iam_user -h dbdayo.xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com \
-p`aws rds generate-db-auth-token --hostname dbdayo.xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com \
--port 3306 \
--username iam_user \
--region ap-northeast-1` \
--ssl-ca=rds-combined-ca-bundle.pem \
--enable-cleartext-plugin