sudoers.dファイルでコマンドのオプションはどこまで制限できるのか
sudoers.dで指定する、許可されるコマンドは、どこまで寛容なのか。 一字一句そのコマンドじゃなきゃいけないのか。 それとも、オプションだとかパスはよしなに指定させてくれるのか。 検証する。
# rootになっておく $ sudo su -
# まずは、現在のユーザー一覧を確認 $ ll /home/ 合計 2 drwx------. 5 vagrant vagrant 4096 1月 12 10:52 vagrant # userを追加 $ useradd test-user # ユーザー追加されたのを確認 $ ll /home # パスワードを設定 $ passwd test-user # test-userにログイン $ su test-user # sudoが必要なコマンドを実行してみます $ yum install -y git Loaded plugins: fastestmirror You need to be root to perform this command. # rootに戻ります $ exit # sudoers権限をいじります $ visudo # 下記を追加 # test-userに root権限でのyumの使用を許可します。 # /bin/yumじゃなくていいのかな? # yumのオプションは何でも使えるのかな? test-user ALL=(root) yum 編集を抜けると visudo: >>> /etc/sudoers: syntax error near line 121 <<< What now? Options are: (e)dit sudoers file again e(x)it without saving changes to sudoers file (Q)uit and save changes to sudoers file (DANGER!) What now? e と、 Qをやってしまったが最後、二度とsudoを使えなくなってしまいました。 rootにログインすることも叶わなかった。。。 VM立ち上げ直した。。。 $ visudo # 以下のように追加 test-user ALL=(root) NOPASSWD: /bin/yum :wqでちゃんと抜けられました。 # test-userにログイン $ su test-user # 権限を確かめる $ sudo yum install -y tig ->OK! yumのコマンドなら全部使えるっぽい。 権限をもう少し絞ってみます。 # rootになる $ exit $ visudo # 以下の設定に変更 test-user ALL=(root) NOPASSWD: /bin/yum install -y tig # test-userになる $ su test-usre # 確かめる $ sudo yum install -y tig 出来ました。 # 他のコマンドは使えない? $ sudo yum install -y tree ダメでした。 # どこまで使えるのか $ visudo # 次のように設定 test-user ALL=(root) NOPASSWD: /bin/yum install #これでyum installなら何でも使えるかな? # 試してみます $ su test-user $ sudo yum install -y tree ↓ なんかパスワード求められた。。。 なんかダメみたいだ。 なので、方法としては、 完全に決め打ちのコマンドを一つずつ書くか、 オプションは指定しないで、実行ファイルのパスだけを書くか。 のどちらかっぽい。 コマンドの処理の仕方とかもっと深く知れば「はいはい、そりゃそうだ」って言えるようになる気がする。
40秒で支度しな!AWSのEC2にpython36で最速でflask実行環境を作成する
$ sudo yum install -y python36 $ sudo pip-3.6 install flask $ vi ~/hello.py # 以下の内容で作成作成
from flask import Flask app = Flask(__name__) @app.route('/') def hello(): return 'Hello World!' if __name__ == '__main__': app.run(host='0.0.0.0')
$ python36 hello.py * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
$ curl http://127.0.0.1:5000 127.0.0.1 - - [20/Feb/2018 16:20:52] "GET / HTTP/1.1" 200 - Hello World!
いい感じです。
これをブラウザから確認したい。
xxx.xxx.xxx.xxx:5000
Hello World!
が表示されました。
当然セキュリティグループから、アクセスしようとしているIPの5000番ポートを開けておく必要がありやす。
どうでしょう。40秒で支度できましたか?
pythonのboto3でのエラーハンドリング
問題
pythonのboto3で以下のようなコードを書いていて、
try: hoge() except: print("error")
flake8にdo not use bare except'
と怒られた。
pythonのエラーの書き方をちゃんと調べてみると、エラーの種類をexceptで指定しておかないと、エラーメッセージを表示する際に、実際のエラーの原因とは違うエラーメッセージを表示してしまい、バグのもとになるとのことで、かっちり書くことにしてみた。
ここで言うエラーの種類は、ZeroDivisionError
とかNameError
とかValueError
とかだそうだ。
このhoge()
で発生しているエラーがなんの種類のエラーなのか調べてみる。
try: hoge() except: print(sys.exc_info())
をやってみると、
(<class 'botocore.errorfactory.LoadBalancerNotFoundException'>, LoadBalancerNotFoundException("An error occurred (LoadBalancerNotFound) when calling the DescribeLoadBalancers operation: Loa d balancers '[fa]' not found",), <traceback object at 0x7f1eda94c708>)
と返ってくる。 なるほどと思い、
try: hoge() except LoadBalancerNotFoundException: print("catch!")
と書くと、うまくいかぬ。
except botocore.errorfactory.LoadBalancerNotFoundException
としてみても、だめ。
During handling of the above exception, another exception occurred: . . . except botocore.errorfactory.LoadBalancerNotFoundException: NameError: name 'botocore' is not defined
となる。さらにエラーが呼び出されてしまう。
どうやってエラーの種類を調べればいいのかは、結局分からなかったが、boto3のエラーハンドリングは以下のように書けるようだ。
stack overflowより(
amazon web services - Boto3, python and how to handle errors - Stack Overflow
)
import boto3 from botocore.exceptions import ClientError try: iam = boto3.client('iam') user = iam.create_user(UserName='fred') print "Created user: %s" % user except ClientError as e: if e.response['Error']['Code'] == 'EntityAlreadyExists': print "User already exists" else: print "Unexpected error: %s" % e
Clientなのかぁ…解決。
知らないふり
ただ、この答えを書いてくれてる人はどうやってこれを探したんだろうか。。。 と、リファレンスも貼ってくれている。
Upgrading to Clients — botocore 1.8.34 documentation
なるほど、こういうときはライブラリのリファレンスのエラーハンドリングの章を参照すればいいのだな!
いやまて、botocoreってなんだ?boto3がラッパーしているライブラリ??
→ boto3のcoreだそうだ。
しかし、boto3のリファレンスから知らないふりして探そうとしてみても、先程のページが見つけられない。
errorとかで検索かけてみても、たくさん引っかかりすぎる。。。
う〜ん、だめだ。
一旦、これは寝かしておこう。。。
sudo rm -rf で削除できないファイルのパーミッション設定
背景
想定するのは、次のようなケース
- ownerがrootでパーミッション設定が700のディレクトリmydir配下のディレクトリ・ファイルをすべて削除したい
- mydir配下のディレクトリ・ファイルのパーミッションはすべて700
- 削除を実行するのはsudo権限を持った一般ユーザー
- mydirのパスは、/tmp/mydir
普通にrootユーザーから削除しようと思ったら、
# rm -rf /tmp/mydir/*
を実行すればいいだけです。
と、いうことは、sudo権限を持っている一般ユーザーならば、
$ sudo rm -rf /tmp/mydir/*
とすればいいのではないか!
となるのが、素直な発想。
しかし、どうやらrmに関してはそうもいかないらしい。
最終的には、mydirのパーミッションを777に変更して、
$ sudo rm -rf /tmp/mydir/*
と実行して、その後にまたパーミッションを700に戻すという愚直な方法で解決しました。
以下、動作確認の備忘録です。
環境
MacOS 10.11.6 El Capitan
Vagrant 2.0.1
CentOS 7.2.1511
動作確認
mydirの作成
まずは、mydirを作成します。
rootになります。
sudo su -
mydirをはじめとするファイル群をつくります。
$ mkdir /tmp/mydir $ mkdir /tmp/mydir/aaa /tmp/mydir/bbb $ touch /tmp/mydir/aaa/c /tmp/mydir/aaa/d
現在のパーミッションを確認します。
$ yum install -y tree $ tree /tmp/mydir/ -pug mydir/ ├── [drwxr-xr-x root root ] aaa │ ├── [-rw-r--r-- root root ] c │ └── [-rw-r--r-- root root ] d └── [drwxr-xr-x root root ] bbb
パーミッションを変更します。
$ chmod 700 /tmp/mydir/ --recursive
確認します。
$ tree /tmp/mydir/ -pug /tmp/mydir/ ├── [drwx------ root root ] aaa │ ├── [-rwx------ root root ] c │ └── [-rwx------ root root ] d └── [drwx------ root root ] bbb```
期待通り、再帰的にパーミションの変更ができました。 mydirのパーミッションも確認します。
$ ls -l /tmp/ total 0 drwx------ 4 root root 26 Jan 18 04:06 mydir
そもそもこのディレクトリに対して、rootは期待する動作を実行できるのでしょうか。
$ rm -rf /tmp/mydir/* $ tree /tmp/mydir -pug /tmp/mydir 0 directories, 0 files
これはOKですね。 もう一度作り直します。
$ mkdir /tmp/mydir/aaa /tmp/mydir/bbb $ touch /tmp/mydir/aaa/c /tmp/mydir/aaa/d $ chmod 700 /tmp/mydir/ --recursive
ユーザー作成 そのままvagrantユーザーで行っても挙動は同じだと思いますが、新しく作ってまっさらなアカウントから検証します。
$ useradd test-user $ passwd test-user
sudo権限を付与する
/etc/sudoers.dを最近覚えた!
$ visudo -f /etc/sudoers.d/test-user
以下のように設定
test-user ALL=(ALL) NOPASSWD: ALL
正常に終了できました。
test-userで作業
$ su test-user
早速削除をやってみます。
$ sudo rm -rf /tmp/mydir/* $ sudo tree /tmp/mydir/ -pug /tmp/mydir/ ├── [drwx------ root root ] aaa │ ├── [-rwx------ root root ] c │ └── [-rwx------ root root ] d └── [drwx------ root root ] bbb 2 directories, 2 files
ガッツリ残っている。 やはり、一般ユーザーからは700のrootがownerのファイルは削除できないようです。
解決策
mydirのパーミッションを変更します。
$ sudo chmod 777 /tmp/mydir/ $ ls -l /tmp/ total 0 drwxrwxrwx 4 root root 26 Jan 18 04:17 mydir
mydirのパーミッションが変更されました。 mydir以下のパーミッションは変わっていません。
$ sudo tree /tmp/mydir/ -pug /tmp/mydir/ ├── [drwx------ root root ] aaa │ ├── [-rwx------ root root ] c │ └── [-rwx------ root root ] d └── [drwx------ root root ] bbb 2 directories, 2 files
と、これでrmを実行してみます。
$ sudo rm -rf /tmp/mydir/* $ sudo tree /tmp/mydir/ -pug /tmp/mydir/ 0 directories, 0 files
これでめでたく削除できました〜
あとはmydirのパーミッションを戻しておしまいですね
$ sudo chmod 700 /tmp/mydir
本当はソースとか読んで、「はいはい、そういう仕様ね」ってやりたいんですが、それはまたの機会にしておきます。
visudoでsudoersを変更して、sudo権限を設定する
zabbixユーザーとsudo等々の権限周りでハマりました。
備忘録として開発環境で動作確認したのを残しておきます。
環境
MacOS High Sierra 10.13.1
Vagrant 2.0.1
CentOS 7.2.1511
かんたんなsudoersの知識
sudoとかの権限を設定しているのは、
/etc/sudoers
というファイルです。
ちなみに/etc
の下にsudoのつくファイルは以下でした。
$ ls -l /etc/ | grep sudo
-rw-r-----. 1 root root 1786 Sep 25 2012 sudo.conf -r--r----- 1 root root 4191 Jan 16 17:27 sudoers drwxr-x---. 2 root root 20 Jan 27 2016 sudoers.d -rw-r-----. 1 root root 3181 Jul 25 2013 sudo-ldap.conf
よく見ると、sudoersは440で書き込みできませんね。
普通には書き込みができないのがこのsudoersファイルです。
普通に書き込みしてしまって、シンタックスエラーがある状態で保存してしまうと、二度とrootを使えなくなったりする危険なファイルです。
なので、sudoersを編集する際にはシンタックスチェックをする、安全なvisudoというコマンドを使って編集をしていきます。
一般ユーザーを作成します
rootになっておく
$ sudo su -
まずは、現在のユーザー一覧を確認
$ ls -l /home/
合計 2 drwx------. 5 vagrant vagrant 4096 1月 12 10:52 vagrant
vagrantユーザーだけでした。
userを追加
$ useradd test-user
ユーザー追加されたのを確認
$ ll /home
total 0 drwx------ 2 test-user test-user 59 Jan 16 17:01 test-user drwx------. 3 vagrant vagrant 90 Jan 27 2016 vagrant
test-userが作成されました。
test-userのパスワードを設定(一応)
$ passwd test-user
Changing password for user test-user. New password: Retype new password: passwd: all authentication tokens updated successfully.
パスワード設定完了。
権限確認
test-userにログイン
$ su test-user
$ whoami
test-user
test-userにログインできました。
sudoが必要なコマンドを実行してみます
$ yum install -y git
Loaded plugins: fastestmirror You need to be root to perform this command.
rootじゃないとできないみたいですね。
sudoをつけてみます。
$ sudo yum install -y git
We trust you have received the usual lecture from the local System Administrator. It usually boils down to these three things: #1) Respect the privacy of others. #2) Think before you type. #3) With great power comes great responsibility. [sudo] password for test-user:
やはり、パスワードがないとだめですね〜
これは当然ながらtest-userを作った管理者は知っていますが。。。
sudoers権限をいじります
rootに戻ります
$ exit
sudoersをいじります
$ visudo
以下の行をを一番下に追加します。
test-user ALL=(root) yum
test-userに root権限でのyumの使用を許可します。という意味です。
/bin/yumじゃなくていいのかな?
yumのオプションは何でも使えるのかな?
試しにやってみます。
編集を抜けると
visudo: >>> /etc/sudoers: syntax error near line 121 <<< What now? Options are: (e)dit sudoers file again e(x)it without saving changes to sudoers file (Q)uit and save changes to sudoers file (DANGER!) What now?
と、
何も考えずにQをやってしまったが最後、二度とsudoを使えなくなってしまいました。
rootにログインすることも叶わなかった。。。
VM立ち上げ直した。。。
これ本番でやったら偉いことになります。
めのまえがまっくらになった!状態になります。
所持金がなくなります。
そんな事故がないために、単にviだけでは編集できず、専用のsyntaxチェックをしてくれるvisudoを使うんですね~
さて、もう一度vagranをdestroyしてupして、visudoまでの操作を行います。
$ visudo
以下のように追加
test-user ALL=(root) NOPASSWD: /bin/yum
:wqでちゃんと正常に抜けられました。
動作確認
test-userにログイン
$ su test-user
権限を確かめる
$ sudo yum install -y git
->OK!
$ sudo yum history
→正常に表示されます。
yumのコマンドなら全部使えるっぽい。
権限をもう少し絞ってみます。
権限変更
とりあえず/bin/yumが使えることがわかりました。
オプション等々、どこまでsudoersファイルで制限できるのでしょうか。
試してみます。
rootになる
$ exit
$ visudo
一番下の行を以下の設定に変更
test-user ALL=(root) NOPASSWD: /bin/yum install -y tig
test-userになる
$ su test-user
確かめる
$ sudo yum install -y tig
出来ました。
他のコマンドは使えないのか確認します。
$ sudo yum install -y tree
ダメでした。パスワードを求められてしまいます。
全部決め打ちでroot権限を与えることもできるようですね。
どこまで制限できるのか
検証してみます。
$ visudo
次のように設定
test-user ALL=(root) NOPASSWD: /bin/yum install
期待する動作は、これでyum installなら何でも使えるけど、他のyumコマンドは使えない挙動!
試してみます
$ su test-user
$ sudo yum install -y tree
↓
なんかパスワード求められた。。。
なんかダメみたいだ。
なので、方法としては、
完全に決め打ちのコマンドを一つずつ書くか、
オプションは指定しないで、実行ファイルのパスだけを書くか。
のどちらかっぽい。
コマンドの処理の仕方とかもっと深く知れば「はいはい、そりゃそうだ」って言えるようになる気がする。
けど今は一つ一つ動作を検証してみて、こういう挙動なのだとわかりました!頑張った!
あと実はファイルパスとディレクトリの権限等のこともハマったので、次回整理してまとめておきたいです。
python3でstrからdictに変換する
import ast str = "{'name':'Taro', 'hieght':170}" type(str) => <class 'str'> dict = ast.literal_eval(str) type(dict) => <class 'dict'>
他の記事でast.literal_dict(str)
となっていたのですが、
AttributeError: module 'ast' has no attribute 'literal_dict'
とエラーになってしまったので、調べてみたら、literal_dictではなく、literal_evalでした。
AWS EMRを使ったhiveのチュートリアル
やりたいこと
以下のような3つの項目を持ったjsonファイルがあります。
id, cookie, date
基本的にはidはcookieと一対一の関係にありますが、たまにあるcookieに対して同じidが振られていることがあります。 つまり、cookieは完全にユニークですが、idには重複があります。 困りました。
この重複をなくして、idとcookieともに完全にユニークなデータセットにしたいと思います。 idが同じオブジェクトあったときは、dateを参照して新しい方のcookieを残して古い方のcookieは無視します。 同じ日に一つのidに対して異なるcookieが生成されることはないものとします。
データはS3から読み込んでS3に保存します。 入力 s3://hive-00001111-ap-northeast-1/Cookie_origin/の下に、複数ファイルに別れたtsvファイルがあります。 出力 s3://hive-00001111-ap-northeast-1/Cookie_unique/以下に保存したいです。
データセット生成
https://www.json-generator.com/ このサイトで以下のような条件で100レコード分のjsonファイルを生成して、indentをcompactでダウンロードします。
[ '{{repeat(100, 100)}}', { _id: '{{objectId()}}', cookie: '{{guid()}}', date: '{{date(new Date(1000, 0, 1), new Date(), "YYYYMMdd")}}' } ]
generated.json
という名前でダウンロードされました。
若干テキストエディタで整形します。
まずは、先頭と最後の[]を削除します。
次に
},{
を
} {
で置換します。これで、いい感じの元データができました。 試しに先頭の5行だけ抜き出してみます。
{"_id":"5a02dcbb94b8be7f1a0fe41e","cookie":"c8be7285-608c-4158-86c6-0c8e03a5a8d3","date":20160720} {"_id":"5a02dcbbec03abdc3585ba76","cookie":"b744804e-0423-4283-995a-843ce861f305","date":20130423} {"_id":"5a02dcbb47fe699b5d4f1b4f","cookie":"1fbec6e8-b6a7-41f7-99a2-708e04f86ab8","date":20170912} {"_id":"5a02dcbb5b3d47ca9c229706","cookie":"31216342-1d61-4f2b-8315-755b163ee225","date":20120325} {"_id":"5a02dcbbfeb00508901f7261","cookie":"0415a1e9-19c8-4a77-a4c6-7aa85fff733a","date":20140928}
こんな感じです。 このファイルから5件だけidのvalueをコピーして他の行のidのvalueにしておきます。
s3の構成
s3のファイル構成は以下のようにします。
S3 └── hive-00001111-ap-northeast-1 ├── Cookie_origin │ └── generated.json ├── Cookie_unique └── src └── uniquify_id.q
generated.jsonはダウンロードしたファイルです。 uniquify_id.qは後ほど説明します。
HiveQL
HiveはHiveQLというSQLによく似たクエリを使っています。 そんなにSQLに詳しくないですが、SQLでできる基本的なことはほとんどできるっぽいです。
uniquify_id.q
-- -------------------------------------------------- -- 使用するテーブルを作成 -- -------------------------------------------------- CREATE EXTERNAL TABLE IF NOT EXISTS id_origin(line STRING) ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' LINES TERMINATED BY '\n' LOCATION 's3://hive-00001111-ap-northeast-1/Cookie_origin'; CREATE EXTERNAL TABLE IF NOT EXISTS id_unique ( id STRING, cookie STRING, get_date STRING)ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' LINES TERMINATED BY '\n' LOCATION 's3://hive-00001111-ap-northeast-1/Cookie_unique'; -- -------------------------------------------------- -- Jsonをパースする -- -------------------------------------------------- INSERT INTO TABLE id_unique SELECT json_data.* FROM id_origin LATERAL VIEW json_tuple(id_origin.line, '_id','cookie', 'date') json_data AS id, cookie, get_date; -- -------------------------------------------------- -- idとcookieを一対一にする。 -- -------------------------------------------------- INSERT OVERWRITE TABLE id_unique SELECT A.id, A.cookie, A.get_date FROM id_unique AS A INNER JOIN (SELECT id,MAX(get_date) AS NEWEST FROM id_unique GROUP BY id) AS B ON A.id = B.id AND A.get_date = B.NEWEST;
細かいHiveQLの書き方は以下のサイトをとても参考にさせていただきました。 http://www.ne.jp/asahi/hishidama/home/tech/apache/hive/ql.html
AWS EMR
クラスター作成
ここからEMRに操作です。
まずはコンソールでEMRを選択します。
以下のように設定します。 基本はデフォルトのままで。 クラスター名は適当。 EC2キーペアはいつもの奴を選択。 クラスターを作成。
ステップ追加
続いてステップを追加していきます。 ステップタブから、「ステップの追加」
ステップタイプはHiveプログラム スクリプト場所は、s3://hive-00001111-ap-northeast-1/src/uniquify_id.q を指定して、作成。
しばらく待ちます。
今回のデータ量だと1,2分待てば良さそうです。 しばらく待ってから更新をかけます。 ステータスが完了になっていたら成功です。
確認
S3の保存先を確認してみます。 Cookie_uniqueフォルダと同じ階層にCookie_unique_$folder$というファイルができていますが、これはhiveのテーブルとCookie_unique/のデータを結びつけているものです。特に理由がなければそのままにしておくのが吉な気がします。
Cookie_uniqueの中に000000_0というファイルが生成されています。これが欲しかったデータです。 ダウンロードして確認してみます。 idが同一のデータを5ペア作ったので、95行になっているか確認します。
$ wc 000000_0
95 285 6745 000000_0
うまくいってそうです。
これにてユニーク化終了です。お疲れ様でした。
なんかうまくいかないってときは、ターミナル等から直接操作もできます。
クラスター > (クラスターを選択する) > ハードウェア > ノードタイプがMASTERのやつを選択
パブリックIPアドレスから普通にsshでログインできます。 ユーザー名はhadoopでログインします。(ec2-userにしちゃうとhiveが使えない)
こんなのが表示されればOKです。
`$ hive' でhiveに入れます。 ここからはMySQLとかと同じ要領でできます。