20190821のLinuxに関する記事は13件です。

Ubuntu18.04LTS ユーザー登録のミス

社内メンバーをユーザー登録する際、

useradd hogihogi

で登録した結果、\home配下にhogihogiのディレクトリができなかった。
正しくは

adduser hogihogi

\home\hogihogiを作成してくれる。
ごっちゃになっちゃうのでメモしときます。
ちなみにグループ追加は

adduser hogihogi group

で作成。ちゃんとできたか確認は

id -a hogihogi

で確認。useraddコマンド間違えて使っちゃった場合は

userdel -r hogihogi

で消してみる。プロセスIDが動いてて消せなかった場合は

kill -KILL processID

で消してから

adduser --force-badname hogihogi

でユーザーを再追加できる。

どれもどこかに書いてあることばかりだけど、備忘録として書き残します。。。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

vagrantの時間のずれを修正する方法

vagrantの時間のずれの修正方法

勤怠管理アプリを作ってみようかなーと思っていたところ、ゲストとホストの時間がずれていて気持ち悪かったので修正しました。
この投稿が同じことで困っている誰かの解決の一助になれば幸いです。

時間がずれている方で以下のコマンドを実行しました。
$ date -s "2019/08/21 22:21:00"

すると

date: 日時を設定できません:
許可されていない操作です

と出ました。

sudoを頭につけて実行
$ sudo date -s "2019/08/21 22:21:00"
そうすると
2019年 8月 21日 水曜日 22:21:20 JST

と時刻が反映されました。

他の方法

自分は他の方法がうまくいかずにこの方法にたどり着きましたが以下も参考にしてみてください。

https://polidog.jp/2014/01/08/vagrant/

http://hiroki-prog.hatenablog.com/entry/2018/07/15/135539

最後に

いろんな方法がありましたが試してみてエラーや不具合が発生するたびにエラー文などを検索してみると徐々に解決に近づくとおもいます。

この記事に誤り、改善点などございましたら編集リクエストやコメントにてご指摘をお願いいたします。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Dockerを使ってみよう-1

この記事は Dockerを分かった気になれる Dockerのコンセプトとかあまり紹介しないまま、脳死コピペでなんとかいける仕様になってます。今回はDockerをインストールし、コンテナを作って起動させるところまで書きます。
次回はAWSのECRにコンテナをプッシュする作業について書きたいと思います。

仕事でDockerを使えって言われたけど、どこから手を付ければいいのか分からなかった数日前の自分のために…

環境

・Amazon Linux AMI 2018.03.0 (HVM)
・Docker CE
・ポート4000

Dockerをインストール

最初からスーパーユーザーになってたら楽かもしれませんね。

アプデ
sudo yum update
インストール
sudo yum install docker -y
Docker起動
sudo service docker start

Docker Imageを作成

ここではDockerfilerequirements.txtapp.pyのファイルを三種類を作り、編集していきます
以下のコードはDocker公式のドキュメントから拝借しています。リンクは記事の一番下にあるのでぜひ見に行ってくださいね!
コードの詳細が書かかれてますよ(英語)

ファイルを作成

touch Dockerfile

内容はこれに編集します

Dockerfile
# Use an official Python runtime as a parent image
FROM python:2.7-slim

# Set the working directory to /app
WORKDIR/app

# Copy the current directory contents into the container at /app
COPY. /app

# Install any needed packages specified in requirements.txt
RUN pip install --trusted-hostpypi.python.org -rrequirements.txt

# Make port 80 available to the world outside this container
EXPOSE80

# Define environment variable
ENVNAME World

# Run app.pywhen the container launches
CMD["python", "app.py"]

requirements.txtも作り、編集していきます

requirements.txt
Flask
Redis

app.pyも同様です

app.py
from flask import Flask
from redis import Redis,RedisError
import os 
import socket
# Connect to Redis
redis = Redis(host="redis", db=0, socket_connect_timeout=2, socket_timeout=2)

app=Flask(__name__)

@app.route("/")
def hello():
    try:
        visits = redis.incr("counter")
    except RedisError:
        visits = "<i>cannot connect to Redis, counter disabled</i>"

    html = "<h3>Hello {name}!</h3>"\
            "<b>Hostname:</b> {hostname}<br/>"\
            "<b>Visits:</b{visits}"
   return html.format(name=os.getenv("NAME","world"),
hostname = socket.gethostname(), visits=visits)

if__name__=="__main__":

    app.run(host='0.0.0.0',port=80)

Dockerを走らせます

いよいよですよ…

まずはビルドから

friendlyhelloのタグをつけてやりますので、タグの名前を変更しても大丈夫です

docker build -t friendlyhello .

Docker imagesでビルドを確認します

docker images

先ほどつけた名前がREPOSITORYの下にでますので、ちゃんとありましたら走らせます!!!!
ポート4000を使うのでEC2インスタンスの方でもちゃんと開けておいてくださいね

docker run -p 4000:80 friendlyhello

アウトプットに'#Running on http://0.0.0.0.80/ (Press CTRL+C to quit)' がでましたら、インスタンスのPublic IP アドレスとポート番号を指定して実際にHello World!が表示されてるか確認しにいきましょう

簡単にいうとこんな感じです↓
http://your_public_ip:4000/

参考にしたサイト

この記事で使用したコードはDocker公式のご提供です
https://docs.docker.com/get-started/part2/

アマゾンさんは日本語に対応していますので
https://docs.aws.amazon.com/AmazonECS/latest/developerguide/docker-basics.html

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

メール送信のパスワードが破られたらどうなるか

序章

あるところに、世界に公開している メールサーバA がありました。
このサーバで aaaa@xxxx.yyy(仮称)というメールアドレスを、
登録/運用していました。

この aaaa@xxxx.yyy には送信用パスワードが設定されていましたが、
ある日、何者かによって、パスワードが ばれてしまいました。
(おそらくパスワードの総当たり攻撃で、偶然 判明したのでしょう。)

このパスワードを引き当てた相手(ファースト スパマー)は、
正規のアドレス aaaa@xxxx.yyy から、世界中にスパムメールを送りつけたのです。

それだけではありません。
ファーストスパマーは、親切にも他のスパマー仲間に
メアドとパスワードを教えてあげたのです(たぶんですが。)

その結果 世界中から、メールサーバA の送信用の
25番ポート(587番や465番も)にアクセスが集中したのです。

対応開始

メールサーバA が どうもおかしいということで、さっそく調査しました。
数時間かけてようやく、上記の現象を把握しました。

まずはスパムメールを止めないといけません。


どうやって?

メールサーバA では postfix を利用していて、postfix の停止を考えましたが、
それだと aaaa@xxxx.yyy 以外のアカウントも使えなくなってしまうので、
この案は却下しました。

とりあえず aaaa@xxxx.yyy のパスワードを変えてみました。
長いパスワードに変えてやりましたよ。

これで大丈夫、と netstat コマンドで確認すると・・・
スパマーからの接続は まだ残っています。なぜ?

おそらく、新規の接続はできなくなったけど、
パスワード変更前のコネクションが、切断されずに残っているのでしょう。

それではと、これらコネクションを iptables コマンドで切断してみました。

IPアドレスごとに切断するので、何個ものコマンドを根気強く投入しました。
少し時間は かかりましたが、残っていたコネクションはすべて、
消えて無くなりました。

これでOK! とその日は家に帰りました。

翌日・・・

はぁぁっ!?
スパムメール送信は、まだ続いていました。

パスワードが再度ばれた? のではありません。
じつは送信キューに、大量の処理待ちが残っていたのです。

スパマーは昨日のコネクション接続時に、
これでもか!と送信要求をしていたのでしょう。
そのため1日経った時点でも、まだ数万のキューが残っていました。

業を煮やした私は、最終手段をとります。
「キュー全削除じゃぁっ!」

参考URL
Postfixのメールキューを確認、削除する方法

最後に

これでようやく、ほんとにスパムメール送信が収まりました。

スパムメールの受信も厄介ですが、送信のほうは さらに厄介です。
なにせ加害者になってしまいますからね。ほんと気を付けましょう。

(まぁスパムメールの大半は、宛先不明で届かなかったようですが。)

ちなみに破られたパスワードは、文字数も短く「弱い」パスワードだったようです。
今後はアカウント作成時、「強い」パスワードを要求するように設定変更しました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Oracleリスナーログのローテーション(linux版)

 リスナーログのローテーション

日々溜まっていき、自動でローテーションしてくれず、そのうち爆発するリスナーログのローテーション方法です。

 シェルで下記のように記載

#!/usr/bin/bash
#■リスナーログを別ファイルとする。
lsnrctl set log_file /u01/app/oracle/diag/tnslsnr/hogehoge/listener/trace/listener1.log
#■次にファイルを移動。
mv /u01/app/oracle/diag/tnslsnr/hogehoge/listener/trace/listener.log /var/ora/lsnrlog/listener_`date "+%Y%m%d_%H%M%S"`.log
#■元のリスナーログに戻す。
lsnrctl set log_file /u01/app/oracle/diag/tnslsnr/hogehoge/listener/trace/listener.log
#■ファイルを削除。
rm /u01/app/oracle/diag/tnslsnr/hogehoge/listener/trace/listener1.log

varlogにバックアップしてますが、たまに削除したほうがいいです。
これをcronに登録しておけばバッチリ!
ユーザはOracleユーザで行うことを忘れずに。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Linuxでファイルシステムを削除する

あるディスクの先頭1MBをふっとばします。

dd if=/dev/zero of=/dev/xvdf bs=1M count=1
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Google Edge TPUとは何ですか?

Edge TPUとは何ですか?

Edge TPUは、低電力デバイス向けの高性能ML推論を提供するGoogleが設計した小さなASICです。 たとえば、MobileNet V2などの最先端のモバイルビジョンモデルを400 FPSで電力効率の高い方法で実行できます。
1_Jryu97esXxkonWBj4t62fw.jpeg

Edge TPUビルトインを含む複数の製品を提供しています。

Edge TPUはどの機械学習フレームワークをサポートしていますか?

TensorFlow Liteのみ。

Edge TPUはどのタイプのニューラルネットワークをサポートしていますか?

第一世代のEdge TPUは、畳み込みニューラルネットワーク(CNN)などのディープフィードフォワードニューラルネットワーク(DFF)を実行できるため、さまざまな視覚ベースのMLアプリケーションに最適です。

サポートされているモデルアーキテクチャの詳細については、モデルの要件を参照してください。

Edge TPUのTensorFlow Liteモデルを作成するにはどうすればよいですか?

モデルをTensorFlow Liteに変換する必要があり、量子化に対応したトレーニング(推奨)またはトレーニング後の完全整数量子化のいずれかを使用して量子化する必要があります。 次に、Edge TPUとの互換性のためにモデルをコンパイルする必要があります。 詳細については、Edge TPUのTensorFlowモデルをご覧ください。

ただし、画像分類アプリケーションを構築している場合は、Cloud AutoML Visionを使用して、Edge TPUと互換性のあるモデルを簡単に構築することもできます。 このWebベースのツールは、独自の画像を使用してモデルをトレーニングし、モデルを最適化してからEdge TPUにエクスポートするためのグラフィカルUIを提供します。

Edge TPUの処理能力はどのくらいですか?

Edge TPUは、1秒あたり4兆回の操作(テラ操作)(TOPS)を実行でき、各TOPSに0.5ワット(1ワットあたり2 TOPS)を使用します。

実際にどのようなパフォーマンスを提供しますか?

こちらのベンチマークをご覧ください。(https://coral.withgoogle.com/docs/edgetpu/benchmarks)

Edge TPUは加速MLトレーニングを実行できますか?

はい、ただし最終層のみを再トレーニングします。 TensorFlowモデルはEdge TPUで高速化するためにコンパイルする必要があるため、後ですべてのレイヤーの重みを更新することはできません。 ただし、2つの異なる方法で高速転送学習を実行するAPIを提供しています。

クロスエントロピー損失関数を使用して、最終的に完全に接続されたレイヤーだけの重みを更新する逆伝播。
新しいデータからの画像埋め込みを使用して最終層に新しい活性化ベクトルを刷り込む重み刷り込み。これにより、非常に小さな小さなデータセットで新しい分類を学習できます。

Dev BoardとUSB Acceleratorの違いは何ですか?

Coral Dev Boardは、SOMに統合されたSOCとEdge TPUを含むシングルボードコンピューターであるため、完全なシステムです。 SOMを削除(または個別に購入)し、3つのボード間コネクタを介して他のハードウェアと統合することもできます。このシナリオでも、SOMにはSOCとEdge TPUを備えた完全なシステムとすべてのシステムインターフェイスが含まれます( I2C、MIPI-CSI / DSI、SPIなど)は、基板間コネクタの300ピンを介してアクセスできます。

一方、Coral USB Acceleratorは、既存のシステムにコプロセッサーとしてEdge TPUを追加するアクセサリーデバイスです。USBケーブルを使用してLinuxベースのシステムに接続するだけです(最高のパフォーマンスを得るにはUSB 3.0をお勧めします)。

Edge TPUチップだけを購入できますか?

いいえ、現在、スタンドアロンのEdge TPU ASICは提供していませんが、USB 3.0またはPCI-Eインターフェイスのいずれかを使用して、既存のハードウェアシステムのコプロセッサーとしてEdge TPUを簡単に統合できる2つの製品を提供しています。

しかし、あなたはここからサンゴ装置を購入することができます:https://store.gravitylink.com/global
微信截图_20190520145825.png

微信截图_20190520145835.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

実運用を想定したLinuxユーザ管理の例

概要

実運用を想定して、Linux系のOSへのユーザをこんな感じで管理、運用したらよいのではないかという案です。
実際はケースバイケースなのでこれをもとに個々の要件などと照らし合わせて細部の設定を変更していくイメージで書いています。

ユーザとグループについて

  • 各サーバーへのSSH接続は個人毎のアカウントを作成して公開鍵認証方式のみとする。
  • 個人アカウントのパスワードは設定するが、主な目的はsudoコマンド実行時に聞かれるパスワードであり、パスワード認証によるSSH接続は禁止する
  • 個人ユーザの所属グループは開発者:developers or 運用者:maintainersの2パターンとする
  • システム管理者のみwheelグループをサブグループとして追加する
  • グループをまとめると以下の通り。
    • developers:ベンダーなどの開発担当者が所属するグループでログなどの参照のみを可能と想定したグループ
    • maintainers:運用担当者の所属するグループでapplicationsグループに所属するユーザにスイッチができる。
    • applications:ミドルウェアやアプリケーション(Web系/バッチ系)の実行ユーザの所属グループ。自作アプリの場合は主グループ。ミドルウェアでインストール時に独自にユーザやグループを作るタイプ(apache/nginx/mysql/plsql等)場合はサブグループでも可。ポイントとしてはmaintainersグループの所属のアカウントからこのグループに所属するユーザにsu可能とする点。(これが必ずしもベダーではないので、細かいことはサーバーの用途毎にやるべきだと考える)
    • wheel:Redhat系におけるsudoでrootコマンドを実行できるようにするグループ(管理者権限オプションが指定されたユーザのサブグループに指定する)
    • adm:journalctlでログを参照できるようにするためのグループで全員所属させる方針とする。
      参考
      方針としてだれでもすべてのログを参照することは許容してしまっているが、特定のログしか見せないようにするにはこのグループに全員所属させるわけではなく、個別の参照できるjournalログを制御する設定が必要になるので、sudo設定のjournalctlのオプション付き設定のコマンドエリアスなどを定義するのかな?

その他事前準備

  • SSH接続からsudoでuseradd,usermod,userdelがパスワード無し(:NOPASSWORD)で実行できること

管理される側のサーバーの事前準備

PAMによるsuコマンドの実行を制限する

  • wheelグループ以外のスイッチは原則禁止
  • 例外としてmaintainersグループに所属するユーザはapplicationsグループの所属ユーザにスイッチできる。
/etc/pam.d/su
#%PAM-1.0
# rootは全部OK(デフォルト)
auth        sufficient                          pam_rootok.so
# 現在のユーザ(スイッチ前)の所属グループがmaintainersの場合は1行スキップする
auth        [success=1 ignore=1 default=ignore] pam_wheel.so use_uid group=maintainers
# 現在のユーザ(スイッチ前)の所属グループがwheel以外の場合はスイッチ不可のチェック(maintainersグループはここは通らない)
auth        required                            pam_wheel.so use_uid
# 現在のユーザ(スイッチ前)の所属グループがmaintainers以外の場合はスイッチ不可
auth        [success=1 default=ignore]          pam_succeed_if.so use_uid user notingroup maintainers
# maintainersグループもapplicationsグループのユーザにしかスイッチできない
auth        required                            pam_succeed_if.so user ingroup applications
# 以下はデフォルト設定の(はず)
auth        substack                            system-auth
auth        include                             postlogin
account     sufficient                          pam_succeed_if.so uid = 0 use_uid quiet
account     include                             system-auth
password    include                             system-auth
session     include                             system-auth
session     include                             postlogin
session     optional                            pam_xauth.so

備忘録

デフォルト定義からauth required pam_wheel.so use_uidのコメントを外すだけだと、wheelグループ以外のスイッチができなくなってしまう。末尾に"root_only"とすればrootのみのチェックとなるが、逆にrootユーザ以外のスイッチが自由になってします。
→よって、以下のようにpam_wheel.soモジュールの判定をスキップできる条件をpam_succeed_if.soモジュールで行う。

sudoの設定例

デフォルトでwheelグループはパスワードありでの任意のsudoが実行可能であり、visudoなどで/etc/sudoersを直接編集する箇所はなかった(はず)なので、/etc/sudoers.d/配下に任意のファイル名で以下の内容のファイルを作成する
ポイントは以下の通り。

  • developers/maintainersグループともにsudoでファイルの中身を参照するコマンドなどで/var/log配下のファイルを参照できる
    ↑のadmグループでも記載したが、すべてのログを見せたくない場合は個別に限定するなどの設定が必要だが、この投稿はあくまもで一例であり、実際にはサーバーの用途毎に設定を変更すべきかもしれない。。。
  • DBサーバーの設定例として、maintainersグループのユーザはsudoでDBサーバーの再起動(systemctl restart mysql.serviceetc.)が実行できる。  他の用途のサーバーであれば、適宜このあたりを可変にするとよいと考える。
/etc/sudoers.d/hogehoge
Cmnd_Alias LOG_LIST = /usr/bin/ls /var/log/*, /usr/bin/ls * /var/log/*
Cmnd_Alias LOG_VIEW = /usr/bin/cat /var/log/*, /usr/bin/cat * /var/log/*, \
                      /usr/bin/head /var/log/*, /usr/bin/head * /var/log/*, \
                      /usr/bin/tail /var/log/*, /usr/bin/tail * /var/log/*, \
                      /usr/bin/more /var/log/*, /usr/bin/more * /var/log/*, \
                      /usr/bin/less /var/log/*, /usr/bin/less * /var/log/*, \
                      /usr/bin/grep /var/log/*, /usr/bin/grep * /var/log/*, \
                      /usr/bin/egrep /var/log/*, /usr/bin/egrep * /var/log/*
Cmnd_Alias DB_MAINTAINANCE_COMMAND = /usr/bin/mysql*, /usr/bin/systemctl * mariadb, /usr/bin/systemctl * mariadb.service
%developers   ALL=(ALL) LOG_LIST, LOG_VIEW
%maintainers  ALL=(ALL) LOG_LIST, LOG_VIEW, DB_MAINTAINANCE_COMMAND 

注意事項してこのファイルのパーミッション設定が必要。
chmod 0440 /etc/sudoers.d/hogehoge

ユーザ管理用シェルの実装例

ユーザ作成

実行例)cat /dev/urandom | tr -dc '[:alnum:]' | head -c8 | useradd.sh -a -g maintainers -c "User Name" "userid" "$(cat ~/.ssh/id_rsa.pub)"

注意事項:公開鍵文字列には空白を含むため、ダブルクォートなどで囲まないとエラーになります。

useradd.sh
#!/bin/bash

#====================================================
# SSH接続用の個人アカウントを作成するシェル
#  root or パスなしでsudoできるユーザでの実行すること
#====================================================
# usagesに表示する名前です。ここではシェルのファイル名にしています。
PROGRA_NAME="$( basename $0 )"
# 共通関数のロード
. $(dirname $0)/user_common.sh

# ヘルプ表示。(オプションの指定方法に不正があった場合にも出力)
function _usage() {
  cat << __EOF__

Usage:
  ${PROGRA_NAME} [-g|--group developers|maintainers] [-a|--admin] [-c|--comment usercomment] [-h|--help] usrename public_key.
  And input the password used by \`sudo\` from the standard input.

Description:
   Create a personal account for SSH connection.

Options:
  -c --comment  Uesr comments.(=useradd command "c" options.)
  -g --group    Specify the user's group. "developers" or "maintainers". default is "developers".
  -a --admin    Add use to wheel group.(Not specify is false)
  -h --help     Show help message.

__EOF__
}

# オプション値の初期値をここで定義する。
COMMENT=""
MAIN_GROUP=developers
IS_ADMIN=false

# bashの組み込み関数`getopts`はlong nameオプションに対応してないので以下のように引数すべてループしてオプションを解析します。
while [ $# -gt 0 ]; do
  # 現在位置の引数からオプションの名前を抽出(先頭のハイフン1回or2回繰り返しは除去)
  OPT_NAME=$(echo $1 | sed 's/^-\{1,2\}//')
  case "${OPT_NAME}" in
    # ユーザコメントオプション
    'c' | 'comment' )
      COMMENT=$2
      shift 2
      ;;
    # グループ指定オプション
    'g' | 'group' )
      MAIN_GROUP=$2
      shift 2
      ;;
    # wheelグループに所属させるオプション
    'a' | 'admin' )
      IS_ADMIN=true
      shift 1
      ;;
    'h' | 'help' )
      # ヘルプメッセージを表示して即終了。
      _usage
      exit 0
      ;;
    * )
      # ハイフンで始まる場合は不正なオプションとする。(ただし、引数自体をハイフンで始める値が受け付けられない問題があるけど)
      if [[ "$1" =~ ^- ]]; then
        output_error "${PROGRA_NAME}: illegal option -- '${OPT_NAME}'"
        _usage
        exit 1
      fi
      break
      ;;
  esac
done
# パスワードは標準入力から
read -sp  "Creating user's password: " USER_PASSWORD
# 対話型実行の場合は改行されない対策
tty -s && echo ""

#----------------------------------------------------------------
# チェック
#----------------------------------------------------------------
# コマンド(シェル)自体のオプションを除く必須パラメータはユーザ名と公開鍵(文字列)の2つ
if [ $# -ne 2 ]; then
  output_error "Required username & public key(string)!!"
  _usage
  exit 1
fi
USER_NAME=$1
PUBLIC_KEY=$2
# ユーザの書式と存在チェック
_check_user "${USER_NAME}" || exit 1
# 公開鍵書式チェック
_check_publickey "${PUBLIC_KEY}" || exit 1
# パスワードの書式チェック
_check_password "${USER_PASSWORD}" || exit 1
# グループ指定された文字列チェック
_check_group "${MAIN_GROUP}" || exit 1
# ログ参照でjournalctlコマンドを実行できるようにサブグループにadmを指定する
SUBGROUPS="-G adm"
# rootへのスイッチが可能なオプションが指定された場合
if ${IS_ADMIN}; then
  # 管理者は運用担当者のグループでなければならない(事実上、このチェックがなくても弊害はないが紛らわしいので)
  if [ "${MAIN_GROUP}" != "maintainers" ]; then
    output_error "Administorator is must be maintainers group."
    exit 1
  fi
  # wheelグループへも追加する
  SUBGROUPS=" wheel"
fi
#----------------------------------------------------------------
# ユーザ作成&パスワード設定&公開鍵のデプロイ
#----------------------------------------------------------------
# "--password"オプションだとなんでかうまく設定されなかったで以下で回避
sudo useradd -g ${MAIN_GROUP} ${SUBGROUPS} -ms /bin/bash -c "${COMMENT}" "${USER_NAME}"
sudo sh -c "echo ${USER_NAME}:${USER_PASSWORD} | chpasswd"
# 公開鍵のデプロイ
sudo mkdir -p /home/${USER_NAME}/.ssh
sudo chmod 0700 /home/${USER_NAME}/.ssh
sudo sh -c "echo ${PUBLIC_KEY} > /home/${USER_NAME}/.ssh/authorized_keys"
sudo chmod 0600 /home/${USER_NAME}/.ssh/authorized_keys
sudo chown -R "${USER_NAME}:${MAIN_GROUP}" /home/${USER_NAME}/.ssh

# 結果出力
output_info "Created user: $(id ${USER_NAME}) / $(egrep "^${USER_NAME}:.+$" /etc/passwd)"

exit 0

ユーザ更新

ユーザ更新は更新したい要素のみをオプション指定します。(パスワードは標準入力からリード)

実行例)cat /dev/urandom | tr -dc '[:alnum:]' | head -c8 | usermod.sh ―p -r -g developers -c "User Name" -k "$(cat ~/.ssh/id_rsa.pub)" "userid"

usermod.sh
#!/bin/bash

#====================================================
# SSH接続用の個人アカウントを更新するシェル
#  root or パスなしでsudoできるユーザでの実行すること
#====================================================
# usagesに表示する名前です。ここではシェルのファイル名にしています。
PROGRA_NAME="$( basename $0 )"
# 共通関数のロード
. $(dirname $0)/user_common.sh

# ヘルプ表示。(オプションの指定方法に不正があった場合にも出力)
function _usage() {
  cat << __EOF__

Usage:
  ${PROGRA_NAME} [-k|--publickey ssh's public_keys(authorized_keys)] [-g|--group developers|maintainers] [-p|--password] [-a|--admin] [-c|--comment usercomment] [-h|--help] usrename.
  And input the password used by \`sudo\` from the standard input.

Description:
   Update a personal account for SSH connection.

Options:
  Specify only the fields to be changed.
  -c --comment  Uesr comments.(=useradd command "c" options.)
  -k --publickey    Update ssh authorized_keys.
  -g --group        Specify the user's group. "developers" or "maintainers".
  -p --password     Update user password(the password used by \`sudo\`). Password input from standard input.
  -a --admin        Add use to wheel group.(Not specify is false)
  -r --revoke-admin Add use to wheel group.(Not specify is false)
  -h --help         Show help message.

Examples:
  Update authrized_keys.
   => ${PROGRA_NAME} -k "ssh-rsa AAAAA...........== foo@hostname" foo

__EOF__
}

# オプション値の初期値をここで定義する。
IS_ADMIN=false
IS_REVOKE_ADMIN=false

# bashの組み込み関数`getopts`はlong nameオプションに対応してないので以下のように引数すべてループしてオプションを解析します。
while [ $# -gt 0 ]; do
  # 現在位置の引数からオプションの名前を抽出(先頭のハイフン1回or2回繰り返しは除去)
  OPT_NAME=$(echo $1 | sed 's/^-\{1,2\}//')
  case "${OPT_NAME}" in
    # ユーザコメントオプション
    'c' | 'comment' )
      COMMENT=$2
      shift 2
      ;;
    # "-k" or "--publickey"の場合(正確には"--k"や"-publickey"も許容するけど。。。)
    'k' | 'publickey' )
      # "-k" or "--publickey"の次の引数をオプションの値として抜粋
      PUBLIC_KEY=$2
      # 公開鍵妥当性チェック
      _check_publickey ${PUBLIC_KEY} || exit 1
      # "-g" or "--group"とその値の分引数配列をシフトさせる。
      shift 2
      ;;
    # "-g" or "--group"の場合(正確には"--o"や"-optvalue"も許容するけど。。。)
    'g' | 'group' )
      # "-g" or "--group"の次の引数をオプションの値として抜粋
      MAIN_GROUP=$2
      # developers or maintainers以外はNG
      _check_group "${MAIN_GROUP}" || exit 1
      # "-g" or "--group"とその値の分引数配列をシフトさせる。
      shift 2
      ;;
    # パスワードの更新
    'p' | 'password' )
      # パスワードの入力自体は標準入力から受ける
      read -sp  "Updating user's password: " USER_PASSWORD
      # 対話型実行の場合は改行されない対策
      tty -s && echo ""
      _check_password ${USER_PASSWORD} || exit 1
      shift 1
      ;;
    # wheelグループにも追加する場合
    'a' | 'admin' )
      IS_ADMIN=true
      shift 1
      ;;
    # wheelグループを削除する場合
    'r' | 'revoke-admin' )
      IS_REVOKE_ADMIN=true
      shift 1
      ;;
    'h' | 'help' )
      # ヘルプメッセージを表示して即終了。
      _usage
      exit 0
      ;;
    * )
      # ハイフンで始まる場合は不正なオプションとする。(ただし、引数自体をハイフンで始める値が受け付けられない問題があるけど)
      if [[ "$1" =~ ^- ]]; then
        output_error "${PROGRA_NAME}: illegal option -- '${OPT_NAME}'"
        _usage
        exit 1
      fi
      break
      ;;
  esac
done

#----------------------------------------------------------------
# チェック
#----------------------------------------------------------------
# コマンド(シェル)自体のオプションを除く必須パラメータはユーザ名と公開鍵(文字列)の2つ
if [ $# -ne 1 ]; then
  output_error "Required username!!"
  _usage
  exit 1
fi
USER_NAME=$1
# 存在チェック&所属グループが個人ユーザの所属するグループのユーザであるか?
_check_exists_user "${USER_NAME}" || exit 1
# 変更後の主グループ判定
if [ -n ${MAIN_GROUP} ]; then
  CHANGED_MAIN_GROUP=${MAIN_GROUP}
else
  CHANGED_MAIN_GROUP=$(id -gn "${USER_NAME}")
fi
# 変更後にwheelグループに所属させるか判定
CHANGED_ADMIN=false
if ${IS_ADMIN}; then
  # adminの付与を解除オプションが同時に指定された場合
  if ${IS_REVOKE_ADMIN}; then
    output_error "--admin option and --revoke-admin option cannot be specified at the same time."
    exit 1
  fi
  CHANGED_ADMIN=true
elif ! ${IS_REVOKE_ADMIN}; then
  for GRP in $(id -Gn ${USER_NAME}); do
    if [ "${GRP}" == "wheel" ]; then
      CHANGED_ADMIN=true
      break
    fi
  done
fi
# 管理者は運用担当者のグループでなければならない
if ${CHANGED_ADMIN}; then
  if [ "${CHANGED_MAIN_GROUP}" != "maintainers" ]; then
    output_error "Administorator is must be maintainers group."
    exit 1
  fi
fi

#----------------------------------------------------------------
# 更新処理
#----------------------------------------------------------------
# コメント更新オプションが指定された場合
if [ -n "${PUBLIC_KEY}" ]; then
  sudo usermod -c "${COMMENT}" "${USER_NAME}"
fi
# 公開鍵オプションが指定された場合
if [ -n "${PUBLIC_KEY}" ]; then
  # 作成時にこけて再実行することも考慮して作り直しておく
  sudo mkdir -p /home/${USER_NAME}/.ssh
  sudo chmod 0700 /home/${USER_NAME}/.ssh
  sudo sh -c "echo ${PUBLIC_KEY} > /home/${USER_NAME}/.ssh/authorized_keys"
  sudo chmod 0600 /home/${USER_NAME}/.ssh/authorized_keys
  sudo chown -R "${USER_NAME}:${MAIN_GROUP}" /home/${USER_NAME}/.ssh
fi
# パスワードが指定された場合
if [ -n "${USER_PASSWORD}" ]; then
  # パスワード更新
  sudo sh -c "echo ${USER_NAME}:${USER_PASSWORD} | chpasswd"
fi
# 所属グループが指定された場合
if [ -n "${MAIN_GROUP}" ]; then
  sudo usermod -g ${MAIN_GROUP} "${USER_NAME}"
fi
# wheelグループへの追加
if ${IS_ADMIN}; then
  # 既に所属していてもエラーにはならないのでそのまま実行
  sudo gpasswd -a "${USER_NAME}" wheel
fi
# wheelグループからの削除
if ${IS_REVOKE_ADMIN}; then
  # こちらは所属していないのに削除するとエラーになるので判定
  for GRP in $(id -Gn ${USER_NAME}); do
    if [ "${GRP}" == "wheel" ]; then
      sudo gpasswd -d "${USER_NAME}" wheel > /dev/null
      break
    fi
  done
  # もともと所属していなくてもエラーにはしない
fi

# 結果出力
output_info "Updated user: $(id ${USER_NAME}) / $(egrep "^${USER_NAME}:.+$" /etc/passwd)"
exit 0

ユーザを削除

ユーザ削除のオプションは強制モードのみ。

実行例)userdel.sh -f "userid"

userdel.sh
#!/bin/bash
#====================================================
# SSH接続用の個人アカウントを削除するシェル
#  root or パスなしでsudoできるユーザでの実行すること
#====================================================
# usagesに表示する名前です。ここではシェルのファイル名にしています。
PROGRA_NAME="$( basename $0 )"
# 共通関数のロード
. $(dirname $0)/user_common.sh

# ヘルプ表示。(オプションの指定方法に不正があった場合にも出力)
function _usage() {
  cat << __EOF__

Usage:
  ${PROGRA_NAME} usrename.

Description:
   Delete a personal account for SSH connection.

Options:
  -f --force    Ignore error when not exists user.
  -h --help     Show help message.

__EOF__
}

# オプションの初期値定義
# 存在しない場合もエラーにしない(複数サーバー実行時に途中で落ちた場合などの対処用)
IS_FORCE=false

# bashの組み込み関数`getopts`はlong nameオプションに対応してないので以下のように引数すべてループしてオプションを解析します。
while [ $# -gt 0 ]; do
  # 現在位置の引数からオプションの名前を抽出(先頭のハイフン1回or2回繰り返しは除去)
  OPT_NAME=$(echo $1 | sed 's/^-\{1,2\}//')
  case "${OPT_NAME}" in
    'f' | 'force' )
      # 強制指定された場合は存在チェック関数でエラーがあっても"0"正常とする
      IS_FORCE=true
      shift 1
      ;;
    'h' | 'help' )
      # ヘルプメッセージを表示して即終了。
      _usage
      exit 0
      ;;
    * )
      # ハイフンで始まる場合は不正なオプションとする。(ただし、引数自体をハイフンで始める値が受け付けられない問題があるけど)
      if [[ "$1" =~ ^- ]]; then
        output_error "${PROGRA_NAME}: illegal option -- '${OPT_NAME}'"
        _usage
        exit 1
      fi
      break
      ;;
  esac
done

#----------------------------------------------------------------
# チェック
#----------------------------------------------------------------
# コマンド(シェル)自体のオプションを除く必須パラメータはユーザ名の1つ
if [ $# -ne 1 ]; then
  output_error "Required username!!"
  _usage
  exit 1
fi
USER_NAME=$1
# 存在チェック&所属グループが個人ユーザの所属するグループのユーザであるか?
RESULT=$(_check_exists_user "${USER_NAME}" 2>&1)
if [ $? -ne 0 ]; then
  if ${IS_FORCE}; then
    exit 0
  fi
  output_error "${RESULT}"
  exit 1
fi

#----------------------------------------------------------------
# ユーザを削除
#----------------------------------------------------------------
sudo userdel -r "${USER_NAME}"
if [ $? -ne 0 ]; then
  exit 1
fi
output_info "Deleted user: ${USER_NAME}"
exit 0

上の3つシェルの共通関数などの定義ファイル

user_common.sh
#====================================================
# ユーザの作成、変更、削除で利用する共通関数定義
#====================================================
# 個人アカウントの所属しうるグループの定義
GROUP_VALID_VALUES="developers maintainers"

# シアン色でメッセージを標準出力に赤字で出力するための関数。
function output_info() {
  echo -e "\e[36m$@\e[m"
}
# 黄色字で警告メッセージを標準出力に出力するための関数。
function output_warn() {
  echo -e "\e[33m$@\e[m" >&2
}
# 赤字で標準エラー出力にメッセージを出力するための関数。
function output_error() {
  echo -e "\e[31m$@\e[m" >&2
}

# ユーザ名チェック
function _check_user() {
  local USER_NAME=$1
  # 書式チェック
  # - 先頭は英小文字のみ
  # - 使用可能文字は英小文字/数字/ハイフン/アンダースコア/ドット
  # - 先頭は英小文字のみ
  # - 末尾は英小文字or数字
  # - 3文字以上、30文字以下
  local REGEXP_PTN='^[a-z]{1}[-.0-9_a-z]{1,28}[0-9a-z]{1}$'
  if [[ ! "${USER_NAME}" =~ ${REGEXP_PTN} ]]; then
    output_error "Invalid username: '${USER_NAME}'. Regexp pattern: ${REGEXP_PTN}"
    return 1
  fi
  # 既に存在したらNG
  if id ${USER_NAME} > /dev/null 2>&1; then
    output_error "Already exists user: ${USER_NAME}."
    return 1
  fi
}

# オプションで指定、または更新・削除時の既存ユーザのメイングループの妥当性をチェックする
function _check_group_value() {
  local MAIN_GROUP=$1
  for GRP in ${GROUP_VALID_VALUES}; do
    if [ "${MAIN_GROUP}" = "${GRP}" ] ; then
      return 0
    fi
  done
  return 1
}

# グループオプションのチェック
function _check_group() {
  local MAIN_GROUP=$1
  # 空文字でないこと、次のオプションではない(=ハイフンで始まらない)というチェック
  if [[ -z "${MAIN_GROUP}" ]] || [[ "${MAIN_GROUP}" =~ ^-+ ]]; then
    output_error "Group is empty value."
    return 1
  fi
  if ! _check_group_value "${MAIN_GROUP}"; then
    output_error "${MAIN_GROUP} is invalid values. Allowd value is (${GROUP_VALID_VALUES})"
    return 1
  fi
  return 0
}

# 存在チェック&所属グループが個人ユーザの所属するグループのユーザであるか?(update/delete時)
function _check_exists_user() {
  local USER_NAME=$1
  # local ORIG_MAIN_GROUP=$(id -gn ${USER_NAME} 2>&1)
  # →なぜかlocalをつけるとexit statusが正しく取れないので宣言と分けて書く
  local MAIN_GROUP
  MAIN_GROUP=$(id -gn ${USER_NAME} 2>&1)
  if [ $? -ne 0 ]; then
    output_error "${MAIN_GROUP}"
    return 1
  fi
  # 指定ユーザの所属グループの妥当性チェック
  if ! _check_group_value "${MAIN_GROUP}"; then
    output_error "${USER_NAME} does not belong to the target groups(${GROUP_VALID_VALUES})."
    return 1
  fi
  return 0
}

# sshの公開鍵の妥当性をチェックします
function _check_publickey() {
  # ssh-keygenコマンドを使うのでいったんファイルに吐き出す
  local TMP_PUBLIC_KEY_FILE=/tmp/AUTHORIZED_KEYS_$$
  echo $1 > ${TMP_PUBLIC_KEY_FILE}
  # 上の_check_exists_userと同じでなぜかlocalをつけるとexit statusが正しく取れないので宣言と分けて書く
  local RESULT
  RESULT=$(ssh-keygen -l -f ${TMP_PUBLIC_KEY_FILE} 2>&1)
  local RET=$?
  rm -f ${TMP_PUBLIC_KEY_FILE}
  if [ ${RET} -ne 0 ]; then
    output_error "${RESULT}"
  fi
  return ${RET}
}

# パスワードの妥当性をチェックします
function _check_password() {
  PASSWORD=$1
  # パスワードは8文字以上で英大文字・小文字・数字の1つ以上の組み合わせ
  if [[ ${#PASSWORD} -ge 8 && "${PASSWORD}" == *[A-Z]* && "${PASSWORD}" == *[a-z]* && "${PASSWORD}" == *[0-9]* ]]; then
    return 0
  else
    output_error "Password must be at least 8 characters, And must be contain at least one of uppercase letters, lowercase letters and numbers."
    return 1
  fi
}

実運用時の想定したフロー

  1. 各個人ユーザは秘密鍵と公開鍵のペアを作成する
    ssh-keygen -t rsa -b 4096 -N '秘密鍵のパスフレーズ' -C 'コメント' -f 出力する秘密鍵のファイルパス
  2. 管理者にアカウント作成を依頼する
    • ユーザID(3文字以上。30文字いない。英小文字、数字、ドット、ハイフン、アンダースコアのみ。先頭の文字は英子文字のみで末尾に記号は不可。)
    • ユーザ名(任意。なくてもよい)
    • 公開鍵(1のコマンド実行後に秘密鍵のファイル名.pubで出力されるファイル)
    • 運用者か開発者か? ※所属グループとかシステム管理者にするかは管理側のグループでジャッジ?
  3. 管理者は各サーバーの↑のシェルをSSH経由で実行する。
    • 1台ずつやると大変なので以下のようなシェルを作っておくとよいと考える。
    • パスワードは申請者から申請してもらってもよいが、以下の例では作成時に全サーバー同じになるようにしているので、ここで生成されたパスワードを申請者に連絡する想定です。(ほんとは発行されたパスワードをメールとチャットツール系などで伝達するのが理想だが。。。)
# パスワード生成
function _generate_password() {
  # cat /dev/urandom | tr -dc '!#$%&-@;:=~+0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' | head -c8; echo ""
  # 上だとすべての文字種に組み合わせにできないのでちょっと乱暴だが、文字種毎の利用数を固定にする
  L_CHARS=$(cat /dev/urandom | tr -dc '[:lower:]' | head -c3) # 英小文字3
  U_CHARS=$(cat /dev/urandom | tr -dc '[:upper:]' | head -c3) # 英大文字3
  N_CHARS=$(( RANDOM % 10 ))                                  # 数字1
  S_CHARS=$(cat /dev/urandom | tr -dc '!#$%&' | head -c1)     # 記号1
  # 結合した文字列をfoldで1文字ずつ分解(改行)して、並び順をシャッフルして最後に改行を除去する
  echo "${L_CHARS}${U_CHARS}${N_CHARS}${S_CHARS}" | fold -w1 | shuf | tr -d '\n'
  return 0
}
USER_PASSWORD=$(_generate_password)
echo ${USER_PASSWORD}
TARGET_HOSTS="guest1,guest2,guest3"
for TARGET in "guest1,guest2,guest3"; do
   ssh ${TARGET} "echo \"${USER_PASSWORD}\" | useradd.sh -a -c \"ユーザ名\" -g \"maintainers\"  \"ユーザID\" \"$(cat 申請された公開鍵のパス)\""
done

※↑のTARGET_HOSTSに記載した文字列は、~/.ssh/cofigに定義した接続定義名を想定。この接続ユーザは接続先ホストにおいてsudoでuseradd/usermod/usedelが実行可能なユーザである。

~/.ssh/config
Host guest1
  HostName ホスト名 or IPアドレス
  User sudoでuseradd/usermod/usedelが実行可能なユーザ
  Port 22
  UserKnownHostsFile ~/.ssh/known_hosts
  StrictHostKeyChecking no
  PasswordAuthentication no
  IdentityFile 秘密鍵のパス
  IdentitiesOnly yes

Host guest2
  HostName ホスト名 or IPアドレス
  User sudoでuseradd/usermod/usedelが実行可能なユーザ
  Port 22
  UserKnownHostsFile ~/.ssh/known_hosts
  StrictHostKeyChecking no
  PasswordAuthentication no
  IdentityFile 秘密鍵のパス
  IdentitiesOnly yes

Host guest3
  HostName ホスト名 or IPアドレス
  User sudoでuseradd/usermod/usedelが実行可能なユーザ
  Port 22
  UserKnownHostsFile ~/.ssh/known_hosts
  StrictHostKeyChecking no
  PasswordAuthentication no
  IdentityFile 秘密鍵のパス
  IdentitiesOnly yes

その他、作成した担当者が離任したら、管理者が削除シェルを実行して削除する。

余談

上のようなシェルを作ったが、台数増えるといろいろ面倒なのでユーザ管理部分はLDAPサーバーを立てた方がよさそう。。。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

指定したディレクトリ以下を検索する

以下はカレントディレクトリ以下にある特定のファイル/ディレクトリを検索する場合の例.

ファイルの場合

$ find ./ -name <file_name> -type f

ディレクトリの場合

$ find ./ -name <directory_name> -type d
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

inuxでunzipすると文字化けする対策

Linuxでunzipすると文字化けする対策

下記に従い、unar コマンドを使うと良さそう

日本語を含むZIPファイルを文字化けせず解凍する方法

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vi(Vim)の基本操作メモ

エンジニアが1年目には覚えたいViの基本操作について、個人的によく使う操作方法をメモします。

起動

$ vi ファイル名

入力モードについて

viはコマンドモードで立ち上がる (コマンドモードについて下記に記載)。
文字を入力する場合は、入力モードに移る以下キーを入力する。

[a]   ...カーソルの右から入力開始(a: Add の意味)
[A]   ...行末から入力開始
[i]   ...カーソルの左から入力開始(i: Insert の意味)
[I]   ...行頭から入力開始
[o]   ...現在の行の下に1行挿入し、その行頭から入力開始
[O]   ...現在の行の上に1行挿入し、その行頭から入力開始

コピー・貼り付けについて、入力モードでは何か他のテキストの文字列、外部サイトの文字列をコピーし、そのまま貼り付けることができる。

コマンドモードについて

escキーでコマンドモードに移る。ファイル・文字列操作、保存等が可能。

カーソル移動

1文字移動

基本的に矢印キーは使うなと昔上司に言われました。でも使ってもいいと思いますが…

左: [←キー] or [h] or [BackSpace]
右: [→キー] or [l] or [Space]
上: [↑キー] or [k]
下: [↓キー] or [j]

単語ごとに移動

[w]    ...1語次へ
[b]    ...1語前へ

削除・貼り付け

[x]    ...1文字削除。
[yy]   ...1行コピーしクリップボードへ格納する。
[dd]   ...1行切り取りしクリップボードへ格納する、または1行削除。
[p]    ...クリップボードの内容をカーソル行の下の行に貼り付ける。

行番号

:set number    ...行番号を表示
:se nu         ...行番号を表示(上に同じ)
::set nonumber 
:se nonu
:行番号        ...行番号を指定して移動

検索

[/]文字列    ...カーソルの後方に検索を行う
[?]文字列    ...カーソルの前方に検索を行う
[n]文字列    ...次の検索該当文字列へ
[N]文字列    ...前の検索該当文字列へ

置換

:s/文字列1/文字列2/    ...カーソル行で最初の「文字列1」のみを「文字列2」に置換する
:s/文字列1/文字列2/g    ...カーソル行の「文字列1」を全て「文字列2」に置換する

取り消し

[u]    ...直前の操作をやめる(u: Undo の意味)
[U]    ...行全体に操作を取りやめる

ビジュアルモード

ctrl + V でビジュアルモードに遷移。矩形選択しxで選択箇所の削除など可能。

終了処理

終了
:q    …書込みを行わず終了(quitの意味)
:q!    ...書込みを行わず強制終了
:wq    ...書込み後終了
:wq!    ...書込み後強制終了

色分けされていたほうが見やすいので、Vimを推奨されている方もいますが、
基本的な操作方法についてはViもVimも同じです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RPMパッケージのインストールコマンド

(強制的な)RPMインストールのオプション(2018.03)

RPMパッケージの強制インストール

  • すでにインストールされているパッケージを無視してインストールする
    --replacepkgs
$ rpm -ihv --replacepkgs hoge-3.2-1.i386.rpm
  • ファイルの重複を無視してインストールする
    --replacefiles
$ rpm -ihv --replacefiles hoge-3.2-1.i386.rpm
  • ダウングレードを許可しインストールする
    --oldpackage
$ rpm -ihv --oldpackage hoge-3.2-1.i386.rpm
  • 強制的にインストールする
    --force
$ rpm -ihv --force hoge-3.2-1.i386.rpm
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

logrotateの備忘録

logrotateのメモ...(2018.01)

設定ファイル

  • 共通の設定ファイル
    /etc/logrotate.conf

  • 個別の設定
    /etc/logrotate.d/****

実行

  • 動作確認(debugモードで実行)
    # /usr/sbin/logrotate -dv /etc/logrotate.conf

    -d, -debug : デバッグモード
    -v, -verbose : 詳細表示モード

  • 通常実行
    # /usr/sbin/logrotate /etc/logrotate.conf

  • 強制実行(条件を満たしていなくてもログローテさせる)
    # /usr/sbin/logrotate -f /etc/logrotate.conf

    -f, -force : 強制実行

確認

  • 実行結果確認
    # cat /var/lib/logrotate/logrotate.status
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む