【Linux】SUID, SGID, スティッキービット
SUID
SUID(Set User ID)とは、実行権のあるファイルに設定されている特殊なアクセス権のこと。
例えば、一般ユーザ(rootでないユーザ)が自身のパスワードを変更すると、この変更は/etc/passwdファイルに保存されます。そこで、/etc/passwdファイルのパーミションを見ると以下のようになっています。
$ ls -l /etc/passwd
-rw-r--r-- 1 root root 667 Apr 21 00:21 /etc/passwd
このファイルの所有者はrootで、rootのみ書込み可であることがわかります。
これによると、一般ユーザは/etc/passwdに書き込みができないように思えますが、ここでpasswdコマンドを調べると、
$ ls -l `which passwd`
-rws--x--x 1 root bin 4731 Mar 09 10:43 /usr/bin/passwd
所有者のアクセス権が”rws”となっています。実行権の部分がsとなっていますが、これは実行権を持っているユーザによってプログラムが実行された場合は、ファイルの所有者の権限で実行されることを意味します。これをSUIDと呼びます。
つまり、一般ユーザはpasswdの実行権を持っているので、passwdが/etc/passwdに書き込む際はそのファイルの所有者であるrootの権限(rw)で実行されることになりますSUIDが設定されると、所有者の実行権限にsが入り、8進数表記にすると4000になります。なので、/usr/bin/passwdの権限は4611となります。
SGID
SGID(Set Group ID)は、SUIDと同じことが所有グループに適用され、所有グループの実行権にsが入ります。8進数表記だと2000です。
スティッキービット
スティッキービットは、特定のディレクトリに対しアクセス権が許可されていてもファイルの削除は行えないようにする設定です。
/tmpのアクセス権を見ると、
$ ls -ld /tmp
drwxrwxrwt 1 root root 4096 Apr 21 23:45 /tmp
となっており、その他のユーザの実行権がtとなっています。これがスティッキービットで、これによりその他のユーザに読み取り書込み権限があっても、自分以外のユーザが所有するファイルを削除することはできなくなります。
スティッキービットは8進数表記だと1000になります。なので、/tmpの権限は1777です。
まとめ
名称 権限 8進数表記 SUID 所有者の実行権限にs ( e.g. -rws--x--x ) 4000 SGID 所有グループの実行権限にs ( e.g. -rwx--s--x ) 2000 スティッキービット その他のユーザの実行権限にt ( e.g. drwxrwxwt ) 1000
【Linux】標準出力と標準エラー出力は何が違う?
Linuxの標準入出力について
Linuxでは、データのストリーム(データの入出力に伴うデータの流れのこと)を扱うために以下のように、標準出力/標準出力/標準エラー出力の3つの経路(インターフェース)が用意されています.
ファイル・ディスクリプタとは、OSが標準入出力の種類を識別するための識別子です.
以下は標準出力と標準エラー入力を出力する例です.
std_out_err.sh
#!bin/bash
echo "標準出力" >&1 # 標準出力
echo "標準エラー出力" >&2 # 標準エラー出力
以下のようにすると、標準出力の内容をファイルへ書き込み、標準エラー出力を端末に表示できます.
$ sh std_out_err.sh > std_out.log
標準エラー出力
$ cat std_out.log
標準出力
>はリダイレクトを意味します. リダイレクトとは、データの入出力をコントロールする機能で、上の例ではechoの標準出力をstd_out.logに書き出す処理をしています.
> は 1> と同義で、1>は標準出力のリダイレクトを意味します. 2> とすると標準エラー出力をリダイレクトできます.
また、以下のように>&を使うと、標準出力と標準エラー出力どちらもリダイレクトできます.
$ sh std_out_err.sh >& std_out_err.log
$ cat std_out_err.log
標準出力
標準エラー出力
なぜ標準出力と標準エラー出力を分けている?
ここで、エラーとそれ以外でデータの経路を分けている理由を何でしょうか.
それは、上の例のように、エラーとそれ以外の出し分けが簡単になるからです.
例えば、毎日実施するバッチがあったとすると、標準出力は必要なくエラーのみログに保存されていて欲しい、ということがあると思います.
この場合は以下のようにすると、標準出力を捨て、エラーのみログに残すことができます.
$ sh std_out_err.sh 2> std_err.log > /dev/null
$ cat std_err.log
標準エラー出力
* /dev/null はnullデバイスと呼ばれ、ここに書き込まれたデータは全て捨てられます.
標準出力と標準エラー出力の経路を別にすることで(1, 2で分けている)、ストリームの扱いが簡潔になります.
Origin(オリジン)とは
Originとは、URLのスキーム、ドメイン、ポートの3つによって定義され、それらが全て一致した場合に、その2つのオブジェクトは同じオリジンといいます。
ちなみに、https://example.com:8080 というURLがあった時、httpsをスキーム、example.comをドメイン、8080をポートと呼びます。
例えば、以下のURLは同一オリジン
以下のURLは異なるオリジンです。
dbに接続できない - could not connect to server
Dockerを使ってRails6の環境を作り、docker-compose upで起動すると、以下のようなエラーが表示されました。
PG::ConnectionBad: could not connect to server: No such file or directory
Is the server running locally and accepting
connections on Unix domain socket "/var/run/postgresql/.s.PGSQL.5432"?
原因と解決策
調べてみるとこれは、railsがホストマシーンに接続しようとしていることが原因でした。ということでrailsのconfig/database.ymlを確認すると、username/password/hostを指定し忘れていました。
default: &default
adapter: postgresql
encoding: unicode
# For details on connection pooling, see Rails configuration guide
# https://guides.rubyonrails.org/configuring.html#database-pooling
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
# username/password/hostを指定してない!
なので、docker-compose.ymlで指定している値をdatabase.ymlに設定してあげます。
version: "3"
services:
postgres:
image: postgres
environment:
POSTGRES_USER: 'admin'
POSTGRES_PASSWORD: 'admin-pass'
restart: always
...
image名、POSTGRES_USER, POSTGRES_PASSWORDの値を、database.ymlにも指定します。
default: &default
adapter: postgresql
encoding: unicode
# For details on connection pooling, see Rails configuration guide
# https://guides.rubyonrails.org/configuring.html#database-pooling
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
username: admin
password: admin
host: postgres
これで無事アクセスに成功しました。
Next.js - プリレンダリングについて
Next.jsのプレリンダリングについて学んだので忘備録として残します。
プリレンダリングとは
Next.jsにおいて、最も重要な概念はプリレンダリング(pre-rendering)です。
Next.jsは、デフォルトで全てのページでプリレンダリングします。
これは、クライアントサイド(ブラウザ)でJavaScriptがページを逐一レンダリングするのではなく、予め全てのページのHTMLを生成する、ということです。これによりSEOがより良くなります。
プリレンダリング時には、HTMLは必要最低限のJavaScriptコードとともに生成されており、ブラウザによるロード時にこのJavaScriptが動き、ページが完全にインタラクティブなものになります。これをハイドレーション(hydration)と呼びます。
なので、ブラウザの設定でJavaScriptを無効にしてNext.jsアプリのページを表示すると、staticなHTMLのみが表示されます。
一方、Next.jsではなくReactのみのアプリだと、JavaScript無効のブラウザでは動きません。React単体にはプリレンダリング機能はないからです。
プリレンダリングには、以下の2種類があります。
静的生成(Static Generation)
これはビルド時にHTMLを生成するプリレンダリング方法です。リクエスト時にこのプリレンダリングされたHTMLを返します。
これはリクエスト時にHTMLを生成するプリレンダリング方法です。
Next.jsでは、それぞれのページにつきどちらの方法でプリレンダリングするか選べますが、やはり静的生成の方が速いので、Next.jsは可能な限り静的生成を使うよう推奨しているようです。
一方、リクエスト毎に表示が変わるようなページはリクエスト毎にプリレンダリングするSSRの方が適しているので、このような場合はSSRを使うなど、「リクエスト前にプリレンダリングできるかどうか」を考えて選択する必要があります。
参考:
Dockerでコンテナ化したrailsアプリをproductionモードで起動する
Dockerでコンテナ化したrailsアプリをproductionモード(本番環境)で起動する方法をメモします。
productionモードとして起動するには以下のような設定項目があります。
- production用docker-compose.ymlを用意
- DB設定ファイルの編集(config/database.yml)
- secret_key_baseの準備
- Gemfileの編集
- DBのセットアップ
- アセットのプリコンパイル
以下で一つずつ解説していきます。
production用docker-compose.ymlを用意
ファイル名をdocker-compose.prd.ymlとしてproduction用のdocker-compose設定ファイルを準備します。
version: '3'
services:
...
app:
...
# 以下を含める
environment:
RAILS_ENV: production
...
DBの設定
以下のように、config/database.ymlにproduction用にデータベースの設定を追記します。
production:
<<: *default
database: データベース名
username: ユーザ名
password: パスワード
secret_key_baseの準備
既にcredentials.yml.encが存在していれば、secret_key_baseは記述されているかと思うので内容を確認します。seret_key_baseはconfig/master.keyによって暗号化されているのでそのままでは中身を見ることはできないので以下を実行。
// コンテナ内に入る
$ docker exec -it コンテナID bash
(コンテナ内)> EDITOR="vim" bin/rails credentials:edit
EDITOR="vim"でエディターを指定します。もしvimの編集モードでファイルが開かずNew credentials encrypted and saved.とだけ返って来るようなら、コンテナにvimがないことが原因の場合があるのでvimをインストールします。(e.g. apt-get install vim)
credentials.yml.encが開ければ、以下のようにsecret_key_baseがあることを確認します。
# aws:
# access_key_id: 123
# secret_access_key: 345
# Used as the base secret for all MessageVerifiers in Rails, including the one protecting cookies.
secret_key_base: キー
Gemfileの編集
以下のように、production環境のみで使いたいgem, またはtest/development環境のみで使うgemを含めないよう設定します。
group :production do
gem 'xxx',
gem 'yyy',
・・・
end
DBのセットアップ
docker-compose runコマンドで、rails db:setをproduction向けに実行します。appは各自のコンテナ名に置き換えてください。
$ docker-compose run app rails db:setup RAILS_ENV=production
Creating docker_rails_app_run ... done
Created database 'prd_database'
$ docker-compose -f docker-compose.prd.yml exec app rails db:create
$ docker-compose -f docker-compose.prd.yml exec app rails db:migrate
アセットのプリコンパイル
静的ファイルをプリコンパイルをします。
$ docker-compose run app rails assets:precompile RAILS_ENV=production
ここまでできたら、docker-compose.prd.ymlを指定してproductionモードで起動してみます。
$ docker-compose -f docker-compose.prd.yml up --build