ConoHaでサーバ立ててユーザ追加したりSSHの設定したり
inabaです。
ConoHaが安くてサーバ立ち上げが早くて良さげです。
なのでVPSを借りて、いじって遊ぶ用のサーバを作ります。
参考記事:
大体は上記に書いてある通りに進めます。
使用するOSはCentOS 7です。
ここには気になった事や追加でやった事等を書いていきます。
wheelグループって何?
wheelグループに追加と書かれている箇所があります。
# wheelグループに追加 $ gpasswd -a newuser wheel
wheelグループとはデフォルトでsudoができるグループです。
所属しているとユーザ個別に設定しなくてもsudoができます。
設定は/etc/sudoers
に記述されています。
$ cat /etc/sudoers ... ## Allows people in group wheel to run all commands %wheel ALL=(ALL) ALL ...
gpasswd
いつもグループへの追加はusermod
でやっていて、gpasswd
は知りませんでした。
usermod
は-a
忘れるとグループが置き換えられる事がありますが、gpasswd
はその心配が無いので良さげです。
パスワード無しでsudo
先程の/etc/sudoers
のwheelグループの設定の下にパスワード無し用の設定がコメントアウトされているのでそのコメントアウトを外し、元の設定をコメントアウトします。
sudo
は結構重要な操作なので自分は一旦パスワード有りで使ってみて、面倒になってきたら外そうと思います。
## Allows people in group wheel to run all commands # %wheel ALL=(ALL) ALL ## Same thing without a password %wheel ALL=(ALL) NOPASSWD: ALL
SSHログイン許可の設定はデフォルトを使う
参考サイトのSSHログインを許可の項目ですが、設定ファイル(/etc/ssh/sshd_config)はデフォルトだとこうなっていました。
#RSAAuthentication yes #PubkeyAuthentication yes AuthorizedKeysFile .ssh/authorized_keys
RSAAuthentication
とPubkeyAuthentication
がコメントアウトされています。
コメントアウトされているという事はデフォルト値が使われるという事。
ここにデフォルト値が書いてあるのですが、
RSAAuthentication
とPubkeyAuthentication
のデフォルト値はyes
です。
自分は極力デフォルトで行けるならデフォルトを使いたい人間なので、設定はいじらず、コメントアウトのまま使います。
SSHキーはローカルにあるものを使う
SSH鍵を作成の項目でホスト側でキーを新たに作ってローカルに秘密鍵をコピーしていますが、自分はローカルにSSHキーが生成してあるのでこれを使います。
ローカル:
$ cat ~/.ssh/id_rsa.pub # 公開鍵 ssh-rsa foobarbaz... # 出た文字列をコピー
サーバ(rootではなく、新しく作ったユーザで実行。):
$ ssh-keygen # ~/.sshディレクトリを手で作るのが面倒なのでコマンドで作る。 # デフォルトで良いのでEnter3回 $ echo 'ssh-rsa foobarbaz...' > ~/.ssh/authorized_keys # ローカルでコピーした公開鍵を登録 $ chmod 600 ~/.ssh/authorized_keys # パーミッションを設定
ssh-keygen
を使っちゃってますが、これは補完が効くコマンドの方が打つのが楽だから使ってます。
作った鍵は今は使わないので、普通にmkdir ~/.ssh
でもいいです。
authorized_keys
のパーミッションの設定が重要
これで結構ハマった。
SSHキーを使ってでログインしようとしても、パスワードを聞かれるので、sshのログが書かれる /var/log/secure
あたりを調べて
Authentication refused: bad ownership or modes for file /home/newuser/.ssh/authorized_keys
と出てきたのでこれで検索。
authorized_keys
のパーミッションにオーナー以外の書き込み権限がついているとダメなようだった。
実際にauthorized_keys
のパーミッションを見てみるとrw-rw-r–になっていたので上記の通りchmod 600 ~/.ssh/authorized_keys
でパーミッションを設定してやると、パスワードを聞かれなくなった!
ログイン攻撃がヤバい
lastb
でログイン失敗履歴が見られるのだが、身に覚えのない失敗が約1000回くらいあった。
怖くなってlast
でログイン成功履歴を見たが、そっちには身に覚えの無い履歴は無かったので良かったが、こんなに攻撃されるのかとビビるね。
精神衛生上良くない。
なので、rootからのログインを禁止とパスワードによるログインを禁止は絶対やるべき。
PermitRootLogin no PasswordAuthentication no
まとめ
何点か書きましたが、sshのauthorized_keys
のパーミッション以外は特にハマること無く進められました。
このサーバで何しよっかな〜。
今日はここまで。
bufio.Scannerのエラーと解決策 #golang
こんばんわ。
inabaです。
今日はAtCoder Beginner Contest 055に参加していました。
その時に起こったエラーを調査したので参考までに。
Runtime Error
ある問題を解いて提出したら何件かでRuntime Errorが出た。
Runtime Errorの内容は見られないのでコードをちょこちょこ変えながらデバッグ。
原因
読み込み文字列が長いのが原因。
その問題の前提条件には10万文字が最大で入ってくるという事が書いてあったが、bufio.Scannerがデフォルトで最大サイズが決まっている事を知らずハマってた。
最大サイズの定数:
MaxScanTokenSize = 64 * 1024
64 * 1024
だから約6万5千文字がデフォルトで読み込める最大文字数。
10万文字なので余裕で超える。
デフォルトを超えるサイズの文字列を(*Scanner).Scan
すると(*Scanner).Err
でbufio.ErrTooLong
が返される。
var sc = bufio.NewScanner(os.Stdin) sc.Split(bufio.ScanWords) sc.Scan() if err := sc.Err(); err == bufio.ErrTooLong { panic(err) }
みたいな感じで調査してやっと見つけた。
解決策
(*Scanner).Buffer
で最大サイズを指定すると65536文字より大きい文字列も読み込んでくれた。
var sc = bufio.NewScanner(os.Stdin) sc.Buffer([]byte{}, math.MaxInt64)
こんな感じです。
とりあえずint64の最大値を指定してますが、いきなり指定サイズまでアロケーションされるという事は無く、現行のサイズ*2で読み込みを再試行していくようなのでまぁいいかなと。
src/bufio/scan.go - The Go Programming Language
今日はここまで。
外部キーが設定されているとTRUNCATEできない #MariaDB #MySQL
inabaです。
最近はgolangでDBをいじっています。
テストもテスト用DBを作り、実際に書き込んで行うようにしていました。
テストをするたびにAUTO_INCREMENT
の値が増えていく
まぁそうでしょう。
なので、テストデータのクリアをするためにTRUNCATE
しようと思ってエラー出てハマりました。
エラー
2つのエラーが出ました。
プレースホルダ
_, err = db.Exec("TRUNCATE TABLE ?", tableName)
上記コードの時は下記のエラーが出ました。
Error 1064: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '?' at line 1
テーブル名にプレースホルダは使えないらしい。
元々プレースホルダはキャッシュ目的だからFROM句は変えないですし、
セキュリティ的な使用でも、ユーザの入力をFROM句には使わないから妥当な動きですよね。
外部キー制約
プレースホルダをやめてfmt.Sprintfを使ってやるようにしてみたらエラー内容が変わりました。
_, err = db.Exec(fmt.Sprintf("TRUNCATE TABLE %s", tableName))
下記がエラー。
Error 1701: Cannot truncate a table referenced in a foreign key constraint ...
これは、外部キー制約によってTRUNCATE
ができないというエラーでした。
外部キー制約の回避
外部キー制約を回避する方法があります。
_, err = db.Exec("SET FOREIGN_KEY_CHECKS = 0") _, err = db.Exec(fmt.Sprintf("TRUNCATE TABLE %s", tableName)) _, err = db.Exec("SET FOREIGN_KEY_CHECKS = 1")
上記コードではエラーは出ません。
FOREIGN_KEY_CHECKS
を0
に設定すると、外部キー制約の回避ができます。
別の解決法
AUTO_INCREMENT
が初期化されればよいのであれば、DELETEしてからAUTO_INCREMENT
をリセットすることもできます。
_, err = db.Exec(fmt.Sprintf("DELETE FROM %s", tableName)) _, err = db.Exec(fmt.Sprintf("ALTER TABLE %s AUTO_INCREMENT = 1", tableName))
これなら2行で済みます。
ただ、子テーブルにデータが入っているとエラーになるので、テストで子テーブルも使っている場合は子テーブルの方から消していってください。
今日はここまで。
【Go】structをJSONにする時、ゼロ値を含む/含まないを切り替える方法と注意点 #golang
inabaです。
Go言語でstructをJSONにする時、ゼロ値を含まない場合はタグにomitempty
をつけますよね。
type stampCard struct { Stamp int `json:"stamp,omitempty"` }
しかし、同じstructを使っている時にゼロ値を含む/含まないを切り替えたい時があります。
具体的には下記のようなstructです。
type stampCard struct { InStampRally bool `json:"in_stamp_rally"` Stamp int `json:"stamp,omitempty"` }
上記のstructではInStampRally
がtrueの時だけStamp
の値をJSONに含めたいです。
しかし、このstructのままだとInStampRally
がtrueでもStamp
が0なら含まれません。
package main import ( "encoding/json" "fmt" log "github.com/Sirupsen/logrus" ) type stampCard struct { InStampRally bool `json:"in_stamp_rally"` Stamp int `json:"stamp,omitempty"` } func main() { sc := stampCard{InStampRally: true, Stamp: 0} jsc, err := json.Marshal(pc) if err != nil { log.Error(err) } fmt.Println(string(jsc)) // {"in_stamp_rally":true} }
解決策
解決策はゼロ値を含む/含まないを切り替えたい値をポインタ化します。
type stampCard struct { InStampRally bool `json:"in_stamp_rally"` Stamp *int `json:"stamp,omitempty"` }
これで
という切り替えができます。
package main import ( "encoding/json" "fmt" log "github.com/Sirupsen/logrus" ) type stampCard struct { InStampRally bool `json:"in_stamp_rally"` Stamp *int `json:"stamp,omitempty"` } func main() { scs := []stampCard{ {InStampRally: false}, {InStampRally: true}, } for i, sc := range scs { if sc.InStampRally { scs[i].Stamp = intPointer(0) } } jscs, err := json.Marshal(scs) if err != nil { log.Error(err) } fmt.Println(string(jscs)) // [{"in_stamp_rally":false},{"in_stamp_rally":true,"stamp":0}] } func intPointer(v int) *int { return &v }
上記サンプルコードではstampCard
のスライスをループさせてInStampRally
がtrueなら値が0のintポインターをStamp
に代入しています。
こうすることで下記のようにomitempty
が指定されていてもゼロ値をJSONに出力することができます。
[ { "in_stamp_rally": false }, { "in_stamp_rally": true, "stamp": 0 } ]
注意点
rangeループのvalueからポインターを取ると、ループでvalueの値が変わってもポインターの値は同じなので、注意しないと意図しない値が入ってしまいます。
(ポインターの値が同じ理由は、ループのたびに新しいメモリを確保しないためでしょう。)
DBから取得した値をループさせてJSON化する時等、注意が必要です。
例として、下記は順番に0, 1, 2, 3
のスタンプ数のスタンプカードができる事を期待しているコードです。
package main import ( "encoding/json" "fmt" log "github.com/Sirupsen/logrus" ) type stampCard struct { InStampRally bool `json:"in_stamp_rally"` Stamp *int `json:"stamp,omitempty"` } func main() { stamps := []int{0, 1, 2, 3} var scs []stampCard for _, stamp := range stamps { log.Infof("%p", &stamp) sc := stampCard{InStampRally: true, Stamp: &stamp} scs = append(scs, sc) } jscs, err := json.Marshal(scs) if err != nil { log.Error(err) } fmt.Println(string(jscs)) }
しかし、上記の結果は下記になります。
$ go run main.go | jq INFO[0000] 0xc42000a3d0 INFO[0000] 0xc42000a3d0 INFO[0000] 0xc42000a3d0 INFO[0000] 0xc42000a3d0 [ { "in_stamp_rally": true, "stamp": 3 }, { "in_stamp_rally": true, "stamp": 3 }, { "in_stamp_rally": true, "stamp": 3 }, { "in_stamp_rally": true, "stamp": 3 } ]
まず、ループ中のvalueのポインター値を表示していますが、すべて同じになっています。
その後のJSONは全てのstamp
が3になってしまいました。
全ての値が最後の値に書き換わってしまっています。
修正後のコード
ループのvalueを一旦新しい変数に入れてその変数のポインターを取れば正確な値をJSONに出力できます。
package main import ( "encoding/json" "fmt" log "github.com/Sirupsen/logrus" ) type stampCard struct { InStampRally bool `json:"in_stamp_rally"` Stamp *int `json:"stamp,omitempty"` } func main() { stamps := []int{0, 1, 2, 3} var scs []stampCard for _, stamp := range stamps { tmp := stamp // important!! sc := stampCard{InStampRally: true, Stamp: &tmp} scs = append(scs, sc) } jscs, err := json.Marshal(scs) if err != nil { log.Error(err) } fmt.Println(string(jscs)) }
結果:
[ { "in_stamp_rally": true, "stamp": 0 }, { "in_stamp_rally": true, "stamp": 1 }, { "in_stamp_rally": true, "stamp": 2 }, { "in_stamp_rally": true, "stamp": 3 } ]
今日はここまで。
npm loginは空いているユーザ名を指定するとアカウントを作成する
inabaです。
npm owner add
で自分のパッケージにオーナーを追加できると知り、
試すためにログインが必要だったのでnpm login
でログインした時の話。
$ npm login Username: foobar Password: Email: (this IS public) foobar@example.com
上記を見てもらうとわかりますが、ユーザ名、パスワードの次にEmailの入力があります。
「なんでユーザ名入力してるのにEmailの入力も必要なのかな〜」という疑問が湧いてきたので調べてみた。
実はnpm login
は取得されていないユーザ名を指定すると新しいアカウントを作る挙動になっていた。
loginという名前だったのでこの挙動にはちょっと驚いた。
loginはnpm adduser
のエイリアスなんだそう。
上記issueではloginとadduserは別々のコマンドに分割する方向で考えているというような事が書いてあった。
今日はここまで。
git mergeのとき、デフォルトでno-ffになるようにする設定
inabaです。
ブランチのマージ時にマージ対象のブランチでどんな変更があったか知るためにマージコミットが欲しいので--no-ff
(No Fast-forward)オプションをつけていました。
$ git merge --no-ff foobar
ですが、設定でデフォルトをno-ffにできることを知りました。
設定
$ git config --global merge.ff false
上記設定をすることでgit merge foobar
のみでno-ffのマージになります。
$ git merge foobar ... $ git log --graph --oneline * f84d525 Merge branch 'foobar' |\ | * 2d68cba Fix B. |/ * a3159f3 Fix A.
参考サイト: Git - git-merge Documentation
問題
しかし、上記のみだとpull時もno-ffでマージコミットができてしまいます。
$ git pull ... $ git log --graph --oneline * 73018f7 Merge branch 'master' of github.com:178inaba/test |\ | * f84d525 Merge branch 'foobar' | |\ |/ / | * 2d68cba Fix B. |/ * a3159f3 Fix A.
よく言われることですがpullというのはfetchとリモートブランチのmergeの組み合わせなのでリモートブランチをマージする時に上記no-ffの設定が適用されてしまいます。
これを防ぐためにpull時にはFast-forwardでマージするように設定します。
$ git config --global pull.ff only
$ git log --graph --oneline * c7356cb Fix C. $ git pull ... $ git log --graph --oneline * bdbb39c Fix D. * c7356cb Fix C.
pullでマージコミットが作られないようになりました。
まとめ
$ git config --global merge.ff false $ git config --global pull.ff only $ cat ~/.gitconfig [merge] ff = false [pull] ff = only
今日はここまで。
HomebrewでインストールしたMariaDBをStrictモードに設定
inabaです。
MariaDBをHomebrewでインストールして使っていたのですが、
NOT NULL制約のカラムにDEFAULT NULLが設定されていると、INSERT時に値を設定していないカラムにNULLが入ってしまうという事がありました。
これはStrictモードが設定されていると起きないそうなので、Strictモードの設定をします。
デフォルトのsql_mode
Strictモードはmysqlにログイン後、sql_modeの値で設定されているか確認できます。
MariaDB [(none)]> SELECT @@SQL_MODE, @@GLOBAL.SQL_MODE; +--------------------------------------------+--------------------------------------------+ | @@SQL_MODE | @@GLOBAL.SQL_MODE | +--------------------------------------------+--------------------------------------------+ | NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION | NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION | +--------------------------------------------+--------------------------------------------+ 1 row in set (0.00 sec)
デフォルトではNO_AUTO_CREATE_USER
とNO_ENGINE_SUBSTITUTION
が設定されており、
Strictモードの設定値であるSTRICT_ALL_TABLES
またはSTRICT_TRANS_TABLES
が設定されていません。
my.cnfのパス
my.cnfのパスを調べます。
上記には/etc/my.cnf
が一番上に書いてありますが、/etc/my.cnf
に設定ファイルを置いてみても反映されませんでした。
上記の方法を試します。
$ mysql --help | grep my.cnf /usr/local/etc/my.cnf ~/.my.cnf order of preference, my.cnf, $MYSQL_TCP_PORT, $ cat /usr/local/etc/my.cnf # # This group is read both both by the client and the server # use it for options that affect everything # [client-server] # # include all files from the config directory # !includedir /usr/local/etc/my.cnf.d
/usr/local/etc/my.cnf
に設定ファイルmy.cnf
がありました。
設定
先程の設定に!includedir /usr/local/etc/my.cnf.d
とあるので、/usr/local/etc/my.cnf
を直接編集するのではなく、/usr/local/etc/my.cnf.d
に設定ファイルを新しく作るようにします。
設定値にはデフォルトで設定されていたNO_AUTO_CREATE_USER
とNO_ENGINE_SUBSTITUTION
も含めておきます。
含めないとデフォルトで設定されていた上記2つは消えてしまいます。
Strictモードの設定値はSTRICT_ALL_TABLES
とSTRICT_TRANS_TABLES
があります。
違いについてはDifference between strict_all_tables and strict_trans_tablesがわかりやすいです。
自分は間違った値が入ってほしくなかったのでSTRICT_ALL_TABLES
を使用するようにしました。
InnoDBだと違いは無いようなのでInnoDBの場合はどちらを選んでも良いでしょう。
$ emacs /usr/local/etc/my.cnf.d/my.cnf $ cat /usr/local/etc/my.cnf.d/my.cnf [mysqld] sql_mode = NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION,STRICT_ALL_TABLES # MariaDBの再起動 $ mysql.server restart Shutting down MySQL .. SUCCESS! Starting MySQL .170105 01:51:36 mysqld_safe Logging to '/usr/local/var/mysql/i.local.err'. SUCCESS!
設定値を確認してみます。
MariaDB [(none)]> SELECT @@SQL_MODE, @@GLOBAL.SQL_MODE; +--------------------------------------------------------------+--------------------------------------------------------------+ | @@SQL_MODE | @@GLOBAL.SQL_MODE | +--------------------------------------------------------------+--------------------------------------------------------------+ | STRICT_ALL_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION | STRICT_ALL_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION | +--------------------------------------------------------------+--------------------------------------------------------------+ 1 row in set (0.00 sec)
STRICT_ALL_TABLES
が設定されている事が確認できました。
これでNOT NULL制約が厳密に適用されます。
今日はここまで。