20201207のLinuxに関する記事は10件です。

linux関係自習メモ

・dockerのcentosコンテナで作業した
・入力中のディレクトリやコマンドはtabで補完できる
・深いディレクトリを一気に使う場合はmkdir に-p オプションを付ける
・ディレクトリをコピーしたい場合にはcopyに-rオプションを付ける
・lessコマンドは無かったので、yum install less した
・作る
フォルダ:mkdir
ファイル:touch

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

Linux netstat

【Linux netstat】

CentOS 7 以降では、netstatは非推奨であり、ss(socket statistics)が推奨。
netstatとssに大きな違いはない。

netstatコマンド

TCP/IP関連のトラブルシューティング

主にTCPの通信状態を調べる。
UDPのサービスポートやUNIXドメインソケットの状態、ルーティング情報なども調べる。

netstatのオプション

-a (--all)        すべてのプロトコルを表示
-l (--listening)  接続待ちのソケットを表示
-n (--numeric)    ホスト、ポート、ユーザなど名前を解決せず、数字のアドレスで表示
-p (--program)    ソケット、ポートをオープンしているプログラムのPIDと名前を表示
-r (--route)      ルーティングテーブルを表示
-s (--statistics) 統計情報を表示
-t (--tcp)        TCPソケットを表示
-u (--udp)        UDPソケットを表示
-x (--unix)       UNIXソケットを表示

※オプションなしは、TCPポートのLISTEN以外のESTABLISHEDなどの状態とUNIXドメインソケットの状態を表示
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

UNIX と Linux

UNIX(ユニックス)Linux(リナックス) はどちらもOSの一つです。
CUI ( コマンド入力 ) をメイン操作として動き、背景色が大体黒なので使ってるとエンジニアっぽく見えるやつ。

           

UNIX

UNIXとは最も古参なOSになります。もともとは『Multics』というOSからの派生。
Multicsがあれこれやろうとして複雑怪奇になっていく中で、もっと簡単に出来ねぇもんかと
考えられ生み出されたOS。結果、軽くて機能も少ない上に使い易いとして生まれたので大人気。破格の値段で買える上に UNIXの作り方もついていた為、あれよあれよと広まって色々な形の UNIXが誕生して行くことに.....
鉄人28号が ∀ガンダムになったくらいに派生した所で UNIXとしてのまとまりが無くなって来ている事と、まだシェアはあるから金になるだろうと考え AT&T社が「うちから出すのが本物、使いたいなら金払え」 と言い出し勢いが弱まる。

Linux

LinuxとはUNIX系OSの一つであり、Linuxカーネルを軸として作られているOS。 :penguin:
Linuxカーネルとはリーナス・トルバーズさんが開発したソフトウェアのこと。
このリーナスさん、UNIXを使っていて物足りないなぁと感じたところを改造していた結果
ほぼ自走するレベルまで行ってしまい、なんなら最初から作り直すかとノリで始めてLinux誕生。
UNIXを参考に作られていることからUNIX感はあるけどUNIXではないOSが生まれました。
UNIXをガンダムとするなら、Linuxはエヴァンゲリオンってな感じ。( たぶんきっと.... )
このLinux何がすごいって、リーナスさんがノリで作ったOSを「アドバイス下さい」って公開したところ多くのプログラマー達が「あれも欲しい、これも欲しい、もっと欲しい、もっともっと欲しい」と手を加え続け、わずか2年で実用可能。IBMやHP、インテルなどのプログラマーも参加して企業や政府機関にまで取り入れられて行きました。これからもシェアは伸び続けていくと考えられているとか。

ソースが公開されているので、様々な種類のLinuxが誕生しました。そのLinuxの種類の事を『Linuxディストリビュージョン』と言います。
 

RedHat系

image.png
商業用に特化したデストリビュージョン。読みはレッドハット。
RedHat社が出している「RedHatEnterpriseLinux」略してRHEL(レル)がLinuxの商用としては業界のスタンダート。
このRHELは "アップストリームファースト" という考え方(機能修正や追加した物を自社内だけで改良し続けるのでは無く、コミニュティを使って改良、成長させていこうとする考え方)のもと開発を行っているため安心感は抜群です。
RedHat系の有名どころでは CentOS Fedoraなどがあります。 

   

Debian系

RedHatと並んで人気なのがこのDebian(デビアン)になります。
ボランティアが中心になって開発が進められているため、ユーザーに優しい作りになっています。
「Debian 社会契約」を掲げており天使にラブソングって感じ。

  1. Debian は 100% フリーソフトウェアであり続けます
  2. 私たちはフリーソフトウェアコミュニティにお返しをします
  3. 私たちは問題を隠しません
  4. 私たちはユーザとフリーソフトウェアを大切にします
  5. 私たちのフリーソフトウェア基準に合致しない著作物について

パッケージ管理(各種のソフトウェアの導入と削除、そしてソフトウェア同士やライブラリとの依存関係を管理するシステム)にも優れており Linux精神を地で行くLinux。
歴史も古いので様々な派生のディストリビュージョンがある。

   

    などなど他にも色々あります。
 

Slackware系

image.png

最も古くに存在しているデストリビュージョン
公式に配布されているのが全て『 シンプル イズ ザ ベスト !!!』 
弄りたいなら自分でやって勝手に調整してって言う、初学者には優しくない
けれど使えば色々勉強になるから使ってみなよって唆され、使っているうちに自分用に
カスタマイズされてて気づいた時には、もうこれじゃなきゃ嫌ってなる。 コアファンが多い。

#中毒性  #「スーパーカブ」みたいなヤツ  #沼

 
 
 
 

参考

https://eng-entrance.com/linux_birth
https://kitsune.blog/linux-summary
https://eng-entrance.com/linux_beginner_distribution#Linux-2
https://www.ibm.com/downloads/cas/VW0PZRBO

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

Linux認証のためのOpenLDAPをコンテナ(AWS Fargate)で動かす

せっかく作った仕組みですが、使わない方向に進んでいるので供養として投稿。

皆さんはLinuxのユーザ管理はどのように実施されていますでしょうか?
一台一台、温かみのある手作業による管理も良いのですが、流石に令和のこの時代、何らかの方法で一元管理を行いたいものです。

EC2 Instance ConnectSystems Manager Session ManagerTeleportなどなど、Linuxへログインするだけであれば、昔から比べると色々な方法が用意されていますが、所属グループの管理やsudo権限の管理なども考え出すと、やはりOpenLDAP + SSSDによる管理がノウハウも豊富で安定しています。

本記事では、Linuxユーザ管理のためのOpenLDAPをAWS Fargate上で稼働させるために私が考えた方法を紹介します。(大枠での挙動に絞って記載し、細かなところは端折っています)

OpenLDAPはコンテナとマッチしない?

いきなりOpenLDAPをコンテナ環境で稼働させてハイ終わりではなく、コンテナ稼働に最適化されたOpenLDAPとはどんなものか、について考えてみます。

一般的に、コンテナ上で稼働させるアプリケーションはImmutableで状態を持たないことが望ましいです。
今回のOpenLDAPであれば、ユーザ情報のような、変更可能性があるようなデータをコンテナ内には持たないような仕組みにしたいです。

それでは、状態を持つデータをNFSのようにコンテナ外部に置いておけばOKでしょうか?

困ったことにOpenLDAPのバックエンドとしてよく使われているbdbはNFSのような共有ストレージで使われることは想定されていません。(mdbはどうなんだろう…?)
Can OpenLDAP use NFS to store back-bdb/hdb data?

単一のコンテナしか建てられず、ストレージもホストのものを使う、となると果たしてそれはコンテナ化する必要があるのか? という疑問が湧いてきます。

認証はしたいけど認証システムの管理はしたくない

タイトルはOpenLDAPをコンテナで動かしてみたいと思った方へのリーチできるように設定しましたが、実はコンテナでの運用は結果としてそれが最適解となっただけで、最初からOpenLDAPをコンテナで運用する! といったことを目指したわけではありませんでした。

まず、第一にあったのが「認証サーバの運用をしたくない」です。

認証一元管理の仕組み自体はすごく重要ですが、これ自体は企業としての競争優位性を生み出すものではありません。
手間を省くための一元管理の仕組みなのに、その仕組みの管理に手間を取られるのは本末転倒です。
(そして、往々にしてこういったシステムは原因不明のトラブルが起こりがちです……)

そこで「認証システム管理の手間を減らすため、OpenLDAPをDisposable/Immutableな環境として運用できないか?」と考えた結果、
「保持するユーザデータを外部で管理して、変更が入るたびに都度コンテナ化してデプロイするのはどうだろう?」と思いつき、その仕組みを設計・構築していきました。

処理の流れ

実際にどのような仕組みでOpenLDAPをImmutableなコンテナで稼働させることにしたのか、全体の流れを図にしてみました。

Overview.png

それぞれをステップごとに説明します。

ユーザ情報の管理・変更

ここが従来のOpenLDAP運用と大きく違うポイントです。

DataManagement.png

ユーザ情報はLDIFファイルをGit(BitBucket)リポジトリで管理しています。

Linuxへのパスワード認証は無効で、公開鍵認証のみでしたので、ユーザ名・UID/GID・公開鍵などは社内限定公開としています。

具体的に管理される情報(LDIFファイル)のサンプルは下記の通りです。

users.ldif
dn: cn=hoge.hoge,ou=Users,dc=mydomain,dc=local
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: ldapPublicKey
uid: hoge.hoge
uidNumber: 10010
gidNumber: 10000
homeDirectory: /home/hoge.hoge
loginShell: /bin/bash
mail: hogehoge@hamee.co.jp
sn: hoge
givenName: hoge
gecos: hoge.hoge
sshPublicKey: ssh-rsa *****************************************************

dn: cn=foo.bar,ou=Users,dc=mydomain,dc=local
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: ldapPublicKey
uid: foo.bar
uidNumber: 10013
gidNumber: 10000
homeDirectory: /home/foo.bar
loginShell: /bin/bash
mail: foobar@hamee.co.jp
sn: foo
givenName: bar
gecos: foo bar
sshPublicKey: ********************************************
sshPublicKey: ********************************************
groups.ldif
dn: cn=hamee,ou=Groups,dc=mydomain,dc=local
objectClass: posixGroup
objectClass: top
gidNumber: 10000
cn: developer

dn: cn=sre,ou=Groups,dc=mydomain,dc=local
objectClass: posixGroup
objectClass: top
gidNumber: 10010
cn: sre
memberUid: hoge.hoge
memberUid: foo.bar
sudoers.ldif
dn: cn=sre,ou=SUDOers,dc=mydomain,dc=local
objectClass: sudoRole
objectClass: top
cn: sre
sudoHost: ALL
sudoCommand: ALL
sudoOption: !authenticate
sudoUser: %sre

パスワードなどの本人以外が閲覧可能であるとマズいような情報は含まれていません。
この情報はGitで管理されているため、ユーザがPCのリプレースなどで公開鍵が変更された場合やメールアドレスの変更など、登録上の変更については、自分で変更のためのプルリクを作成することが可能です。

所属グループの変更やsudo権限の申請なども、プルリクに申請理由を書く、または別途ワークフローシステムと連携して自動的に作成もできそうです。

作成されたプルリクは承認者によるレビューを経て、適切な権限を持った運用担当者によってマージされます。

OpenLDAPコンテナイメージの生成

BuildAndPush.png

運用担当者によるBitBucketでのマージをトリガーにし、BitBucket Pipelineが起動。
LDIFファイルを含むArtifactsをS3にアップロードされS3内アイテムのバージョン変更をトリガーにCodePipeline/CodeBuildが起動します。

CodeBuildでは更新されたユーザ情報を含むLDIFファイルを元に、OpneLDAPコンテナイメージを作成しECRにプッシュします。
その後、新たなコンテナイメージはFargateで構成されたECSにデプロイされます。

一見、CodePipelineを使用しなくてもBitBucket Pipelineだけですべての処理を完結することができそうです。
なぜCodePipeline/CodeBuildを使用したのか? について説明します。

この仕組みでは、ユーザ情報(LDIFファイル)を管理するリポジトリとコンテナイメージを作成するためのDockerfileを管理するリポジトリを分けてあります。
DockerfileのリポジトリにはOpenLDAPの設定ファイルが含まれており、これらのファイルを一般ユーザが閲覧・操作できる場所に置くのは好ましくなかったため、運用者のみにアクセス権が付与されたリポジトリで管理しています。

MultiSource.png

CodePipeline/CodeBuildには複数のソースをトリガにしてビルドを走らせる機能があり、複数のソースのうちどちらかが更新されたらビルドを開始する、という設定が可能です。

AWS CodePipeline を CodeBuild の複数の入力ソースおよび出力アーティファクトと統合するサンプル

ここでは、ユーザ情報が変更された場合・Dockerfileが更新された場合、どちらがトリガーでもコンテナイメージの再作成・デプロイが動くように設定を行いました。

Dockerfileはこのような内容で、 /ldif ディレクトリに必要なLDIFファイルが配置されている想定で初期化を行っています。

Dockerfile
FROM alpine:latest

EXPOSE 636

RUN apk --update --no-cache add openldap-back-mdb openldap openldap-clients openldap-passwd-pbkdf2
RUN mkdir -p /run/openldap && mkdir -p /var/lib/openldap/run && mkdir -p /etc/openldap/slapd.d
RUN rm -f /etc/openldap/slapd.conf
COPY ldif /ldif
COPY slapd.ldif /etc/openldap/slapd.ldif
COPY --from=cert-builder /certs /certs
RUN slapadd -n0 -F /etc/openldap/slapd.d -l /etc/openldap/slapd.ldif
RUN slapd -h ldapi:/// \
      && sleep 3 \
      && ldapadd -Y EXTERNAL -H ldapi:/// -f /ldif/init/10-domain.ldif \
      && ldapadd -Y EXTERNAL -H ldapi:/// -f /ldif/init/20-ou.ldif \
      && ldapadd -Y EXTERNAL -H ldapi:/// -f /ldif/groups.ldif \
      && ldapadd -Y EXTERNAL -H ldapi:/// -f /ldif/users.ldif -c || true \
      && ldapadd -Y EXTERNAL -H ldapi:/// -f /ldif/sudoers.ldif \
      && kill -SIGTERM $(pidof slapd)
WORKDIR /etc/openldap
CMD ["slapd", "-h", "ldap:/// ldaps:///", "-d", "256"]

余談ですがこのDockerfileを書いているときに、AlpineLinuxのAportsに登録されているOpenLDAPで、slapd.ldifから初期化する処理がうまくいかず、よく見てみると不要な改行が入っていることに気づき修正のプルリクを作成しました。マージされたときはほんのり嬉しかったです:relaxed:
https://github.com/alpinelinux/aports/pull/10225

CodeBuildに渡す buildspec.yml はこんな感じ。

buildspec.yml
version: 0.2

phases:
  install:
    runtime-versions:
      docker: 18
  build:
    commands:
      - echo "### mix up each source"
      - echo "${CODEBUILD_SRC_DIR_SOURCE_LDIF}"
      - cp -f ${CODEBUILD_SRC_DIR_SOURCE_LDIF}/ldif/*.ldif ${CODEBUILD_SRC_DIR}/ldif/
      - echo "### build image"
      - docker build -t myopenldap:latest ${CODEBUILD_SRC_DIR}
      - echo "### generate tag"
      - REPO=************.dkr.ecr.ap-northeast-1.amazonaws.com/myopenldap
      - LDIF_COMMIT_HASH=$(cat ${CODEBUILD_SRC_DIR_SOURCE_LDIF}/commit-id.txt | cut -c1-7)
      - DOCKERFILE_COMMIT_HASH=$(cat ${CODEBUILD_SRC_DIR}/commit-id.txt| cut -c1-7)
      - TAG=${LDIF_COMMIT_HASH}_${DOCKERFILE_COMMIT_HASH}
      - docker tag myopenldap:latest ${REPO}:${TAG}
      - echo "### push"
      - $(aws ecr get-login --no-include-email --region=ap-northeast-1)
      - docker push ${REPO}:${TAG}
#     この後はデプロイのためのstepが続くが省略

${CODEBUILD_SRC_DIR_SOURCE_LDIF}はLDIF管理のリポジトリからのArtifactsが保存されたディレクトリで、${CODEBUILD_SRC_DIR}はDockerfile管理のリポジトリのものとなります。
つまり、docker buildより前の行でDockerfileリポジトリ内の/ldifディレクトリにLDIFリポジトリのLDIFファイルをコピーしています。

各リポジトリのbitbucket-pipelines.ymlには

bitbucket-pipelines.yml
- echo ${BITBUCKET_COMMIT} > commit-id.txt

というステップが定義されていて、コミットIDをcommit-id.txtというファイルに吐き出してArtifactsに含めるようにしています。
このファイルを元にしてコンテナイメージのタグ名コミットハッシュをつなぎ合わせたユニークな値に設定しています。

Fargateへのデプロイ

Deploy.png

デプロイはCodeDeployを使用せず、CodeBuild内でecspressoを呼び出しています。

デプロイされたコンテナはNLBのターゲットとして起動し、ヘルスチェックを経てLinuxからのLDAPS/LDAPアクセスを受け取り始めます。

コンテナは別々のAZで2つ以上起動され、一方が何らかの障害で応答不能になったとしても片肺での処理が可能です。ECS Serviceの設定で2つ以上のコンテナが起動している状態を維持しようとしますので、ホスト障害や何らかの原因でコンテナのヘルスチェックに失敗した場合は、自動的にコンテナが入れ替えられます。
通常のOpenLDAP運用であれば可用性担保のためレプリケーションの設定が必要となりますが、この仕組みの場合、2つのOpenLDAPコンテナは同じデータを保持し、中身が書き換わることがないためそのような設定は不要となります。

メリット・デメリット

この仕組みによって、得られるメリット・デメリットは下記の通りです。

サーバの管理が不要

OpenLDAPコンテナはFargate上で稼働しているため、ホストのハードウェア・OSのメンテナンスは不要です。
運用担当者が気にするべき事項はベースイメージのOSバージョン、OpenLDAPのバージョンのみとなります。

OpenLDAPの基本的な機能しか使用していないので、クリティカルなセキュリティパッチ以外はほぼメンテナンス不要でしょう。

ユーザ情報化の管理が容易

一般的なOpenLDAPの管理では、ユーザの登録情報(公開鍵など)が変更される場合は、運用担当者が申請を元に変更したり、ユーザ自身に変更権限を付与して変更のための操作マニュアルを要する必要がありました。

この仕組みであれば、ユーザは使い慣れたGitのプルリクという形で登録情報の変更申請が行なえます。

また、運用担当者側のレビュー・承認もプルリクベースで行うことができ、さらにその証跡はBitBucket上に記録されます。

データの変更反映に時間がかかる

BitBucket上で管理されたユーザ情報が書き換えられた際、コンテナイメージのビルド・デプロイという流れを経て変更が反映されるため、通常のOpenLDAPサーバ運用の場合と比べて、変更反映に時間がかかります。

これについては、弊社の場合はユーザ数がそこまで多くはないことと、データの書き換え頻度が少ないため大きな問題とはなりませんでした。

緊急時のロックダウンについては、AWSのSSM RunCommandでLinuxの設定を変更することで対応する想定でした。(nsswitchの設定でローカルの/etc/passwdを優先するようにしてあったため、そこにロック済みの同名ユーザを追加することで緊急ロックが可能)

また、同様の理由で大量のユーザ情報を格納する大規模なLDAPサーバの稼働方法としては不向きと言えます。

ユーザ情報は公開可能な情報のみに限る

BitBucketでユーザ情報を管理するため、パスワードのような、社内であっても非公開の情報を保持することができません。

基本的にはLinux認証には公開鍵認証のみを想定していたので、こちらも特に運用上困ることはありませんでした。

新しい認証方式へ

ここでは記載しませんでしたが、MySQLのユーザ認証にもこの仕組みを拡張すべく、API Gateway + Lambda + DynamoDBで別途パスワードを管理するRESTエンドポイントを作成、DynamoDB Streamでパスワード変更をトリガにLDIFをArtifactsとして出力、CodePipelineのマルチソースビルドのソースの1つとして組み込んでVaultを使って連携、みたいなことも行っていました。

Password.png

しかしながら、作りかけの状態で私自身に別の大きなタスクが降ってきたこと、
パスワードの取り扱いのためリソースを拡張した結果、把握しなければいけないシステムの範囲が大きくなってしまったこと、
OpenLDAPやSSSDが古い仕組みになってきているのか、Web上でも参考となるページが少なくなっており、今後新しく参加してくれるエンジニアにとって優しくない技術要素となる懸念、
一番大きな要因として、アプリケーションのコンテナ化とログの取り扱い方法の変化というモダンなアーキテクチャへの切り替えにより、EC2へのログイン制御にユーザグループやsudo権限などの細かな管理が不要となったこと、
これら諸々の事情が重なったところに、同僚がAWS SSOとSSM Session ManagerやVaultを組み合わせた、更に面白く便利な仕組みを作ってくれたため、今回紹介した仕組みはそのままお蔵入りとすることにしました。

結果として弊社では本番稼働前にお蔵入りとなってしまいましたが、OpenLDAPをDisposable/Immutableなコンテナで扱うためユーザ情報をGit上で管理する、というアプローチは我ながら面白いなと思っており、この記事が同じような問題で悩んでおられる方への一助となれば幸いです。

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

管理者用初期化URLを踏んでWebサービスのデータをふっとばした話

自己紹介

本職のエンジニアではありませんが、ちょっとICT系に詳しそうなやつって感じで、部署のサーバ管理を任されたりもしています。

背景

私の(当時所属していた)部署では、毎年、数週間かけて前年の各人の業務実績をとりまとめて一つの冊子(PDF)にするという仕事があり、この作業を少しでも自動化するため、Webサービスが内製されました。当初は単純に各ユーザが自分の業務実績一覧をテキストで用意してアップロードするというものでしたが、秘伝のタレのように毎年少しずつ改良されたり、大幅に作り直されて別システムから業務データを取り込んでからブラウザ上で編集できるようになったりしつつ、なんやかんやあって私が引き継ぎます。他にやりたい人もなく、ひとり鯖管です。OSはCentOS6でした。

このシステムでは、毎年新しいデータを編集するため、その作業開始時にデータを初期化する必要があります。この作業も自動化し、管理者権限でログインして管理者ページ(ここでは https://hoge.example.com/kanri としましょう)から起動できます。危険な作業なので、初期化しようとすれば警告が表示される仕様です。

そもそもWebサービスに初期化などという機能なんてと思われるかもしれませんが、そこは内製のシステム。前年の編集データはほんとうにまったく不要なので、単純に初期化する実装になっています。さらに後述しますが、問題の本質は初期化とは無関係です。

事故

何をした(つもり)か

各ユーザに依頼していた数週間の編集作業の第1回目の期限が過ぎ、アクセスも落ち付いたころ、管理者ページに一気に行きたかった私は、ブラウザのアドレスバーに kanri と打ち込み、閲覧履歴から目的のURL https://hoge.example.com/kanri を探してちゃちゃっと開きました。

何をやらかしたか

すると、管理者ページを開いただけのはずなのに、期待に反して、初期化が完了したことを表すページが表示されました。

まさかと思いつつ、アドレスバーを確認すると https://hoge.example.com/kanri/init のURLがありました。閲覧履歴にあった管理者ページではなく、その隣の初期化ページを開いてしまっていました。ユーザに編集作業を依頼する前に初期化をしていたので、それが閲覧履歴にあったわけです。出てほしい警告は一切出ませんでした。

やらかしたと悟りました。ユーザは200人弱、みなさん極めて多忙、そんな中で時間を割いて集中的にやってもらった作業をすべてパーにしたというわけです。大規模な商用サービスに比べれば微々たるものですが、ユーザのみなさんに同じ作業をまたやってもらわなければいけないかもというプレッシャーで胃が痛みます。

何が消えたのか

このWebサービスではDBは使用せず、データはすべて特定のディレクトリにあるユーザごとのサブディレクトリに、複数のファイルに分けて置かれていました。このデータ用ディレクトリを確認しましたが、初期化したのですから当然もぬけの空です。その当時は内部の初期化コードまで承知していませんでしたが、ようはrmで大量のファイルとディレクトリを削除したということです。

応急処置

あわてない

あわててはいけません。まずは落ち付きます。今なら「全集中、鯖の呼吸」とつぶやくべきところです。たぶんサーバと一体化し、最善の一手となるキーに一筋の光が見えます……(知らんけど)

閑話休題。削除してしまったファイルを回復させるための最善策を考えます。

ユーザによる編集作業はほぼ一段落していて数日前のバックアップはあるにはありましたが、戻してしまうと、その間に誰が編集したかつきとめて個別に依頼していく手間と精神的負担がかかります。バックアップを戻すのは最後の手段とします。

ですが、最後の手段はある。これは気分として重要です。

データ保全

初期化がrmであったことは幸いです。truncateなどしていればどうなっていたことか。UNIX系は昔から使っているし、i-nodeを中心としたファイルシステムの概略も把握しているし、rmしてもファイルがすぐには消えないことも知っていますが、ほっておくと消えていくので時間との戦いです。このへんはどんなOSでも同じでしょう。とはいえ、リモートサーバなので下手にシングルユーザモードなどにしてネットワークアクセスができなくなると困ります。幸いこのサーバは他の用途で使っていないので、httpdを含む不要そうなサービスをすばやく止め、ディスクアクセスを最小限にします。

次に、ファイルシステムをこのままいじっていくわけにいかないので、どこかにファイルシステムのイメージを置いてゆっくり作業したいところです。このサーバにはメインのファイルシステムが一つしかないので、イメージを一時的にも同じサーバ内には置けません。sshできる方向はローカルからサーバのみ。ddはroot権限がいる。最短時間で。ということで、sshdのPermitRootLoginをyesにし、rootのパスワードはさすがに使いたくなかったので気休めですがuid=0の別アカウントを急遽用意して、このアカウントでローカルからddしました。今にして思えば、デバイスのアクセス許可を自分に出すだけでもよかったですね。

local# ssh uid0@hoge.example.com dd if=/dev/hogeroot ibs=1M >hoge.img

これも幸いですが、このファイルシステムは16GBほどしかなく、25分ほどでイメージの転送が完了しました。やらかして、45分後です。

さらに、このファイルは原本として死守する必要があるので、別の作業用ファイルにコピーしておきます。

local# cp hoge.img hoge.wrk

ファイル回復

ここからは腰を据えて作業できます。

まずは、ext4のファイルシステムでrmしてしまったファイルをどう回復すればよいか、ググります。出てきた https://www.no-title.com/linux/extundelete で、extundelete というコマンドがあることを知り、インストールします。指定したファイルシステムから指定したパスのファイルを回復するようです。

local# apt install extundelete

次に、ddしたファイルシステムイメージに対してどうすればファイルシステムとしてアクセスできるか、ググります。出てきた http://ng3rdstmadgke.hatenablog.com/entry/2016/10/06/064434 によれば、ファイルをループデバイスとして設定してやればマウントもできるようです。loop14 は適当です。他のが使われていたようなので、使われていない最小の番号にしました。マウントは作業には不要ですが、ファイルシステムが認識されているかの確認用です。

local# losetup /dev/loop14 hoge.wrk
local# mount -o ro /dev/loop14 /mnt

これら、参考にした平易な記事を書いてくださっているみなさんには感謝です。

さて、準備はできました。extundeleteは動くでしょうか。とりあえずパスのすぐわかる出力先PDFを指定して試します。

local# extundelete --restore-file /var/www/hoge/data/20xx/hoge.pdf /dev/loop14
...
Unable to restore inode 915251 (var/www/hoge/data/20xx/hoge.pdf): Space has been reallocated.
...

Unable...、だめなのか。reallocated...、もう上書きされたのか。遅かったのか。

他のファイルも試します。多数ありますが、パスは機械的に生成されるので、その一覧を生成して一気に与えました。

local# extundelete --restore-files /tmp/filelist.txt /dev/loop14
...
Successfully restored file /var/www/hoge/data/20xx/....
Successfully restored file /var/www/hoge/data/20xx/....
Successfully restored file /var/www/hoge/data/20xx/....
Successfully restored file /var/www/hoge/data/20xx/....
...

すごい。Successfully!! じゃんじゃん回復して RECOVERED_FILES というディレクトリに作成されていきました。ごく一部に回復できなかったファイルが報告されましたが、これも幸いなことに、手動で回復できるものでした。

復旧

実際には、ほかにいくつもリストからもれたファイルやリンクなどがあったのですが、それらも手作業で回復できました。あとから落ち付いてマニュアルを読むと --restore-directory というオプションがあり、こちらの方が断然楽ができたみたい。

最後にファイルをサーバにコピーし、祈りながら各サービスを復帰させたところ、件のWebサービスがやらかし前のまま無事に動作していることが確認できました。安堵しました。

例のURLを踏んでから復旧完了まで約6時間で済み、最初の編集作業が終わった時期だったのでサーバの停止に気づいたユーザはごく少数です。ましてや、裏でこんなことがあったと知っている人はいません。

原因と対策

何が問題だったか

そもそも、初期化のページに行くには警告が出るはずでした。しかしそれが出ず、閲覧履歴から開くだけで初期化されてしまったのが問題です。

なぜそんなことが起きたか、みなさんはもう想像がついていることでしょう。

それは、初期化ページのURLをGETメソッドで叩くだけで、初期化が行われる仕様だったせいです。管理者ページからそのリンクをクリックしたときだけ、 onclickconfirm() を呼んで警告を出すしくみでした。閲覧履歴からそのページを開けば、 confirm() など実行するはずもなく、GETメソッドでいきなり叩いてしまい、初期化が実行されてしまったわけです。

再発しないための対策

あたりまえですが、Webサービスの内部状態を変化させる処理は、GETメソッドで起動してはいけません。Webサービスの初期化も当然内部状態を変化させます。例のリンクが、formのボタンにもなっていないただのリンクであるところで気付いて修正しておくべきでした。ボタンでもGETを使っていれば同じですが。

対策としては、POSTメソッドでアクセスしたときだけ初期化するように変更しました。初期化した結果は常に同じで羃等性があるので、DELETEを使うという手もあったかもしれません。

CSRFに関する追記(2020/12/9)

この記事に直接だけでなく、はてなブックマークやTwitterでも多数のコメントをいただき、ありがとうございます。その中で特にCSRFに関して強く言及いただきました。CSRFについては無対策であったため、本記事をそのままなぞって、GETをPOST等に修正しさえすればOKとされてしまわないよう、追記します。

GETの場合はCSRF以前の話で、自分ひとりで勝手にやらかしてしまいます。POST等にすればいわゆる自爆はなくなるのですが、URLを知っている攻撃者がPOST等のリクエストを生成するリンクやボタンを設置し、被害者がログイン状態でそれを踏むと、そのリクエストを実行してしまうというCSRF攻撃が成立してしまいます。重要な処理(商品購入や送金、メールアドレス変更、Webサービス初期化:sweat_smile:、等)の最終確定時にCSRF対策が必要です。

本件のような内部利用システムでは、CSRF対策はRefererチェックという簡便な方法ですむことも多いと思われます。不特定多数が利用する商用システムでのCSRF対策には、きちんとセッションを管理し、最終確定前のページでセッションIDなどのトークンを埋め込んで、最終確定ページでトークンを確認してから処理を実行するのが一般的です。セッション管理にはノウハウが多数あるようで、独自実装よりもフレームワーク利用がよいかもしれません。これらの詳細は徳丸本などをご参照ください。

結論

やらかしてしまったものの、次のような幸運が重なり、最小限の影響だけで復旧できました。

  • アクセスのほとんどない期間だった。
  • rmによるファイルの単純な削除だった。
  • サーバが他の用途に使用されていなかった。
  • ファイルシステムが小さく短時間でイメージが転送できた。
  • SSDではなくHDDだった。

大事なことなのでもう一度言います。Webサービスの内部状態を変化させる処理は、GETメソッドで起動してはいけません。

現在の自分はわかっていても、過去の自分も含めてみんながそう実装している保証はありません。「GET POST 使い分け」みたいなキーワードで検索して上位に出てくる解説Webページでも、このことにちゃんと触れているものはかなり少ないようです。特に、完全な「初期化」は普通のWebサービスにはなかなか存在しない処理ですし、パラメータも必要ないので、GETで実装してしまうというミスがあっても不思議ではありません。管理者用の機能の実装は手を抜きがちですが、前任者のコードも自分の過去コードでさえも信用せず確認する習慣を忘れずに。

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

aaPanel 無料のLinux管理パネル説明1 導入編

無料のサーバーマネジメントツール

日本語情報は全くと言って出てこないオープンソース管理パネルaaPanelの説明書となります。
aaPanelは元々Pagoda panel(宝塔)として開発され(こちらも日本では無名)、
Pagoda panelの国際版としてaaPanelがリリースされました。
githubのスター数は本家pagodaが2400スター、aaPanelが580となっています。
cpanelの代替を探している方にもおすすめです。

aaPanelとは?

キャプチャ4.JPG

aaPanelはLinuxとWindowsサーバーを簡単に構築できるマネジメントツールです。
ワンクリックで各種環境をインストール可能となり、その後のモニタリングやセキュリティ設定をGUIで設定可能です。
大きな特徴として、pagodaが中国語のみLinuxとWindowsの管理が行える。
aaPanelが英語対応ですが、Linuxのみとなっています。
aaPanelでは英語化されているのでここではaaPanelの導入方法を説明していきます。
キャプチャ7.JPG

aaPanelの機能

キャプチャ1.JPG
キャプチャ2.JPG
キャプチャ3.JPG
キャプチャ5.JPG

その他簡単に構築できる機能

キャプチャ6.JPG

サーバー要件

オペレーティングシステム:CentOS / Ubuntu / Debian / Fedora / Deepin
512M以上(768M以上を推奨)
100M以上のハードディスク空き容量
クリーンサーバー(ApacheやNginxなどのサーバー環境はインストールしないでください)
aaPanel6.xバージョンはcentos7に基づいて開発されています。centos7.xを使用することをお勧めします

インストールの流れ Linux

現在、aaPanelはDebian、Ubuntu、CentOSをサポートしています。

Debianの場合

wget -O install.sh http://www.aapanel.com/script/install-ubuntu_6.0_en.sh && bash install.sh

Ubuntuの場合

wget -O install.sh http://www.aapanel.com/script/install-ubuntu_6.0_en.sh && sudo bash install.sh

CentOSの場合

yum install -y wget && wget -O install.sh http://www.aapanel.com/script/install_6.0_en.sh && bash install.sh

インストールする際にいくつかのユーザー入力が必要です。
aaPanelインストールディレクトリを要求します/www。デフォルトでは、にインストールされています。ディレクトリを変更するには、「n」と入力します。その他、管理画面にSSLの導入をするかなどを聞かれますのでご自身の希望通り進めてください。
分からない場合はすべてyesで問題ないです。

インストール後のログインの流れ

インストール完了後にログインURLとIDパスワードが表示されます。
キャプチャ8.JPG

https://xxx.com:8888/ランダム8文字の英字
ポートはデフォルトで8888となりますので、セキュリティ設定で8888ポートを事前に開放してください。

新しくインストールされたマシンにはランダムに8文字のセキュリティエントランス名が付けられます。
これはパネル設定でも変更できます。
このログイン情報を記録していないか覚えていない場合は、次の方法で解決できます。

解決策: SSHターミナルに次のコマンドのいずれかを入力して解決します

1.パネルエントリを表示:/etc/init.d/bt default
2.管理画面の入口を閉じる場合:rm -f /www/server/panel/data/admin_path.pl

ログインパスワードを忘れた場合の対応方法(Linux)

次のコマンドを入力してパスワードをリセットします(コマンドの最後の「testpasswd」 を変更する新しいパスワードに置き換えます)
注:debianの場合/ ubuntuユーザー、root権限を持つアカウントを使用してこのコマンドを実行してください

cd / www / server / panel && python tools.py panel testpasswd

190708bg391o1q032g1z91.png
紫色のボックスは変更するパスワードです。
赤いボックスはパネルアカウントです。

ログイン試行回数が上限に達してロックされている場合

以下のコマンドで解除可能です。

rm -f /www/server/panel/data/*.login

インストール完了です!

管理画面に移動して直観的にサーバーを操作してみましょう!
キャプチャ9.JPG
初回、アクセス時にワンクリックインストールで環境を一気に構築することも可能です。
バージョンや環境を指定可能で、ワンクリックインストールを進めた場合は以下のように自動で環境構築が開始されます。
キャプチャ10.JPG

linux-pc.png

管理コマンド一覧

管理機能

停止

service bt stop

開始

service bt start

再起動

service bt restart

アンインストール

service bt stop && chkconfig --del bt && rm -f /etc/init.d/bt && rm -rf /www/server/panel

現在の管理パネルのポート番号を確認する

cat /www/server/panel/data/port.pl

ポート番号を変更する例 8881 (CentOS6 ポート番号8881に変更する例)

echo '8881' > /www/server/panel/data/port.pl && service bt restart
iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport 8881 -j ACCEPT
service iptables save
service iptables restart

ポート番号を変更する,例 8881(CentOS 7 ポート番号8881に変更する例)

echo '8881' > /www/server/panel/data/port.pl && service bt restart
firewall-cmd --permanent --zone=public --add-port=8881/tcp
firewall-cmd --reload

MySQLマネージャ(root)のパスワードを強制的に変更,例 123456

cd /www/server/panel && python tools.py root 123456
Change control Panel login password,e.g. 123456
cd /www/server/panel && python tools.py panel 123456

サイト構成の場所

/www/server/panel/vhost

Delete banding domain of control panel

rm -f /www/server/panel/data/domain.conf

Clean login restriction

rm -f /www/server/panel/data/*.login

コントロールパネルの認証IPを表示

cat /www/server/panel/data/limitip.conf

アクセス制限の停止

rm -f /www/server/panel/data/limitip.conf

ドメインパーミッション確認

cat /www/server/panel/data/domain.conf

コントロールパネルのSSLをオフにする

rm -f /www/server/panel/data/ssl.pl && /etc/init.d/bt restart

コンパネエラーログ

cat /tmp/panelBoot

データーベースエラーログ

cat /www/server/data/*.err

Site Configuration directory(nginx)

/www/server/panel/vhost/nginx

Site Configuration directory(apache)

/www/server/panel/vhost/apache

サイトデフォルトディレクトリー

/www/wwwroot

データベースバックアップディレクトリー

/www/backup/database

サイトバックアップディレクトリー

/www/backup/site

サイトログ

/www/wwwlogs

Nginx

nginx installation directory

/www/server/nginx

Nginx起動

service nginx start

Nginx停止

service nginx stop

Nginx再起動

service nginx restart

Nginxリロード

service nginx reload

nginx設定

/www/server/nginx/conf/nginx.conf

Apache

apache installation directory

/www/server/httpd

Apache起動

service httpd start

Apache停止

service httpd stop

Apache再起動

service httpd restart

Apacheリロード

service httpd reload

apache設定

/www/server/apache/conf/httpd.conf

MySQL

mysql installation directory

/www/server/mysql

phpmyadmin installation directory

/www/server/phpmyadmin

Data storage directory

/www/server/data mysql

Start

service mysqld start

Stop

service mysqld stop

Restart

service mysqld restart

Reload

service mysqld reload

mysql Configuration

/etc/my.cnf

FTP

FTPインストールディレクトリ

/www/server/pure-ftpd

FTP開始

service pure-ftpd start

FTP停止

service pure-ftpd stop

FTP再起動

service pure-ftpd restart

FTP設定

/www/server/pure-ftpd/etc/pure-ftpd

PHP

phpインストールディレクトリ

/www/server/php

PHP開始(PHPバージョンに置き換えてください, 例 service php-fpm-54 start)

servicephp-fpm-{52|53|54|55|56|70|71} start

PHP停止(PHPバージョンに置き換えてください, 例 service php-fpm-54 stop)

service php-fpm-{52|53|54|55|56|70|71} stop

Restart(PHPバージョンに置き換えてください, 例 service php-fpm-54 restart)

service php-fpm-{52|53|54|55|56|70|71} restart

Reload(PHPバージョンに置き換えてください, 例 service php-fpm-54 reload)

service php-fpm-{52|53|54|55|56|70|71} reload

PHP設定(PHPバージョンに置き換えてください, 例 /www/server/php/52/etc/php.ini)

/www/server/php/{52|53|54|55|56|70|71}/etc/php.ini

Redis

redisインストールディレクトリ

/www/server/redis

Redis開始

service redis start

Redis停止

service redis stop
redis Configuration
/www/server/redis/redis.conf

Memcached

memcached installation directory

/usr/local/memcached

Memcached開始

service memcached start

Memcached停止

service memcached stop

Memcached再起動

service memcached restart

Memcachedリロード

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

Docker をつかって RAR 形式のファイルを展開(解凍)する

 Windows 10 で ubuntu Docker imageunrar をつかって RAR 形式のファイルを展開する手順です。ホスト OS の Windows 10 には RAR 展開ソフトウェアをインストールせずに、Docker コンテナで展開処理します。

 RAR 形式に圧縮されたファイルを展開するためには WinRAR や 7-Zip のような RAR 圧縮ファイルを扱えるソフトウェアが当然必要になりますが、Docker を利用することによって、ホスト OS ではなくコンテナに展開プログラムをインストールし、用が済めばコンテナごと破棄する、という使い方ができます。普段から RAR 形式をよく利用するのであればソフトウェアをインストールしておけばよいですが、稀に使用する程度という人にとって、ホスト OS に専用ソフトウェアをインストールせずに済みます。

1. ubuntu イメージを取得する

 ファイルの展開に使用する Linux コンテナイメージを docker hub から取得します。ここでは現時点の ubuntu:latest である 20.04 Focal Fossa を使用します。

ubuntuイメージを取得する
> docker pull ubuntu

    Using default tag: latest
    latest: Pulling from library/ubuntu
    Digest: sha256:c95a8e48bf88e9849f3e0f723d9f49fa12c5a00cfc6e60d2bc99d87555295e4c
    Status: Image is up to date for ubuntu:latest
    docker.io/library/ubuntu:latest

2. ubuntu コンテナに unrar をインストールする

 初期イメージに unrar は含まれていないため、unrar をインストールしたコンテナを作成します。unrar のインストールには ubuntu のパッケージ管理ツール apt-get を利用します。

unrarをインストールする
> docker run -it ubuntu /bin/bash -c "apt-get update && apt-get -y install unrar && rm -rf /var/lib/apt/lists/*"

    ...
    Reading package lists... Done
    Reading package lists... Done
    Building dependency tree
    Reading state information... Done
    The following NEW packages will be installed:
      unrar
    0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded.
    ...

3. コンテナから新しいイメージを作成する

 ファイルを展開するときに使用する新しいイメージを作成します。イメージの名前は任意ですが、下記の例では my/unrar としています。まず、docker ps でコンテナ ID を確認して(下記の例では 9b720fae191a )、docker commit でイメージを作成し、イメージを作成した後は docker rm でコンテナを削除します。

新しいイメージを作成する
> docker ps -l

    CONTAINER ID        IMAGE               STATUS
    9b720fae191a        ubuntu              Exited (0) 3 minutes ago

> docker commit 9b720fae191a my/unrar
> docker rm 9b720fae191a

4. RAR 形式の圧縮ファイルを展開する

 ホスト OS のファイルシステムにある RAR 形式の圧縮ファイルを展開します。下記の例では「New Text Document.rar」から同じフォルダに「New Text Document.txt」を展開しています。 docker run は実行する度にコンテナを作成しますが --rm オプションを指定しておくと処理終了時にコンテナが自動で削除されるようになります。

RARアーカイブを展開する
> docker run -v ${PWD}:/tmp -w /tmp -i --rm my/unrar unrar e -r './New Text Document.rar'

UNRAR 5.61 beta 1 freeware      Copyright (c) 1993-2018 Alexander Roshal

Extracting from ./New Text Document.rar

Extracting  New Text Document.txt                                     OK 
All OK

 使用しなくなったときは、Docker コンテナとイメージを削除します。

 以上の手順のうち、新しいイメージを作成するまでの手順้は Dockerfile と docker build コマンドで代用することもできます。Dockerfile を下記のような内容で用意しておいて docker build -t my/unrar . を実行すると、my/unrar イメージからすぐに unrar を実行できる(上記 4 の手順)状態になります。この流れは unrar に限らず、Windows で Linux のプログラムを実行したい、という場面に応用できます。

Dockerfile
FROM ubuntu:latest

# set a directory for the app
WORKDIR /tmp

# install unrar
RUN apt-get update && apt-get -y install unrar && rm -rf /var/lib/apt/lists/*
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

nlコマンドでの空行の取り扱い

いままで特に触れてきませんでしたが、nlはデフォルトで空行(改行だけの行)に行番号を出力しません。これは行番号を出力する機能を持つ他のコマンドとは大きく異なる点だと思います。

空行を含むデータの行番号

まずは空行に対してnlがどんな動作をするのか確かめてみましょう。echo-eを指定することで\nを改行コード(LF)として出力します。

$ echo -e 'a\n\nb'
a

b
$ echo -e 'a\n\nb' | nl
     1  a

     2  b

確かに空行に行番号は出力されません。

空行と判定される条件

スペースやCR改行(\r)を含む行など、見た目は空行に見えるデータも試してみましょう。

$ echo -e 'a\n \nb' | nl
     1  a
     2   
     3  b
$ echo -e 'a\r\n\r\nb\r' | nl
     1  a
     2  
     3  b

一見空行に見えても行番号が振られています。LF以外の文字が含まれる行は「空行」とはみなされないようです。UNIX系のコマンドなので、LFのみを改行として取り扱うのは当然の結果です。Windowsなど改行がCRLFで保存されたデータを扱う場合は気をつける必要がありそうです。

空行に付加されるデータ

セパレータ

では、-sオプションの記事で説明したセパレータは出力されているのでしょうか。実はcoreutilsのnlとBSD系のnlでは結果が異なります。タブのままでは見えないので-sでわかりやすく@@@に変更して検証してみましょう。

coreutils
$ echo -e 'a\n\nb' | nl -s @@@ 
     1@@@a

     2@@@b
busybox
$ echo -e 'a\n\nb' | busybox nl -s @@@ 
     1@@@a

     2@@@b
BSD(macOS)
$ echo -e 'a\n\nb' | nl -s @@@ 
     1@@@a
      @@@
     2@@@b

BSD系のnlだけが空行でもセパレータを出力しています。

BSD系のnlのセパレータとPOSIX

このように実装によって異なる挙動を発見した場合は、標準であるPOSIXを確認してみましょう。

<empty>
When line numbers are suppressed for a portion of the page; the <separator> is also suppressed.

これは、-nオプションの記事でも引用した「OUTPUT」の一部分の記載になります。
POSIXによると「行番号が抑制されるとき、セパレータもまた抑制される」とあります。つまりBSD系のnlのこの挙動は「POSIXに準拠していない仕様」あるいは「ソフトウェアのバグ」ということになります。

STANDARDS
The nl utility conforms to IEEE Std 1003.1-2001 ("POSIX.1").

さらに、Manpageを参照すると「標準」としてPOSIXが引用されています。おそらくはバグということになるのでしょう。

ちなみに、coreutilsのManpageを参照すると、-sについて以下のように書かれています。

coreutils
$ man nl | grep -A1 'number-separator'
       -s, --number-separator=STRING
              add STRING after (possible) line number

この(possible)という部分が、「行番号をつけるところだけ」という補足のようです。

coreutilsのnlでの空行の取り扱い

デフォルトのタブのときも検証してみましょう。xxdというvimに含まれるコマンドユーティリティでバイナリを16進数のテキストで出力します。

coreutils
$ echo -e 'a\n\nb' | nl | xxd
00000000: 2020 2020 2031 0961 0a20 2020 2020 2020       1.a.       
00000010: 0a20 2020 2020 3209 620a                 .     2.b.
BSD(macOS)
$ echo -e 'a\n\nb' | nl | xxd
00000000: 2020 2020 2031 0961 0a20 2020 2020 2009       1.a.      .
00000010: 0a20 2020 2020 3209 620a                 .     2.b.

16進数表示されている真ん中のブロックの1行目右端7バイトに注目してください。coreutilsは20 2020 2020 2020でBSD系は20 2020 2020 2009ですが、この部分がちょうど元データの2行目の文字列(改行0x0aは含まず)にあたります。つまり、coreutilsのnlでも空行を一切変更しないわけではなく、行番号とセパレータを表示していないだけです。右揃えにするためのスペース0x20が出力されており、さらにセパレータのタブ0x09の分まで1文字余計に付加されています。

あまり大きな問題は無いかもしれませんが、これはnlを2つ重ねて使う場合には想定した挙動と異なる結果になるかもしれません。

coreutils
$ echo -e 'a\n\nb' | nl | nl
     1       1  a
     2         
     3       2  b

空行に付加されるデータを取り除く

coreutilsの空行に付加されるデータを消せるのか、他のオプションとの組み合わせを試してみましたが難しいようです。

coreutils
$ echo -e 'a\n\nb' | nl -n ln -w 1 -s '' | xxd
00000000: 3161 0a20 0a32 620a                      1a. .2b.

coreutilsでは-w1以上の値しか受け付けませんので0を指定することはできません。一方、busyboxもほとんど同じ挙動ですが、-w0が指定できるので空行のまま取り扱うことはできるようです。

busybox
$ echo -e 'a\n\nb' | busybox nl -w 0 -s '' | xxd
00000000: 3161 0a0a 3262 0a                        1a..2b.

しかし、行番号についてもセパレータが消えてしまいますので実用するのは難しいでしょう。現実的に空行を元に戻したい場合は、sedなどを利用するしかなさそうです。

coreutils
$ echo -e 'a\n\nb' | nl | sed 's/^[ ]*$//' | nl
     1       1  a

     2       2  b

ToDo

BSD系のnlのPOSIX準拠について

Manpageには最新のPOSIX.1-2017ではなくPOSIX.1-2001準拠となっていますので、その変更差分を調べる必要があります。後日確認したら更新する予定です。

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

TypeScriptのアップデート中に EEXIST のエラーが表示されてちょっと詰まったことの解決手順

今回は今までなぜかグローバルのTSのアプデができずに無視してたところついに立ち向かった(面倒くさがり)
そのせいでバージョン 1.5.3 に甘んじていました。

npm i -g typescript@latest

で最新版のTypeScriptにアプデしようとしたら案の定エラーが表示。

npm ERR! code EEXIST
npm ERR! syscall symlink
npm ERR! path ../lib/node_modules/typescript/bin/tsc
npm ERR! dest /Users/myuser/.nodenv/versions/12.16.0/bin/tsc
npm ERR! errno -17
npm ERR! EEXIST: file already exists, symlink '../lib/node_modules/typescript/bin/tsc' -> '/Users/myuser/.nodenv/versions/12.16.0/bin/tsc'
npm ERR! File exists: /Users/myuser/.nodenv/versions/12.16.0/bin/tsc
npm ERR! Remove the existing file and try again, or run npm
npm ERR! with --force to overwrite files recklessly.

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/myuser/.npm/_logs/2020-12-06T17_00_41_920Z-debug.log

nodenvを使っている人が遭遇したことあるかもしれないエラー。npm installなどでも遭遇する人がいるらしいが詳しくは見ていない。

再インストールであっさり解決?

結果としては

npm ERR! Remove the existing file and try again, or run npm

これの言う通りにして解決した。このエラーで言うところの existing file は

npm ERR! dest /Users/myuser/.nodenv/versions/12.16.0/bin/tsc

のことだから、直接 tsc に対して " rm -rf tsc " で削除した。
これでOK。なはず。

typescriptアップデートの再チャレンジ

しかしここで再びアプデコマンドを打ったところなんとまた同じエラーが発生。
今度は、「tscフォルダ」ではなく「tsserverフォルダ」に対して怒られた。そのため tsc と同じように削除した。

3度目の正直でついに

その後、再び

npm i -g typescript@latest

で無事に最新版にアプデできた。バージョンは 4.1.2。
1.5.3から4.1.2はなかなかの跳躍。

npm ERR! syscall symlink ってなんだ

タイトルにもある通り、つまづいた理由はこのエラーがよくわからなくて放置。ちゃんと調べてなかったのでよくわからなかった笑
調べたところ symlink とはショートカットのディレクトリ名を意味しており、プログラミングとは関係なく日常でもショートカットのファイルなどを作成する機会があると思うがそれと同じものになるとの理解であってるかと。

つまりsymlinkによってAというショートカットのディレクトリ(中身は入っていない)を呼び出すことで、本体であるBを呼び出すことができるらしい。

今回のケースで言えば具体的には
Aがこれ(npm ERR! path ../lib/node_modules/typescript/bin/tsc)
Bがこれ(npm ERR! dest /Users/myuser/.nodenv/versions/12.16.0/bin/tsc)
に当たると思われる。というか path は文字通りの意味で、 dest が目的地であり本体のことを指しているはず。

そのため今回は本体のフォルダを削除することで再インストールできたということになります。
nodenvの場合は各nodeバージョン毎に入っているライブラリバージョンも異なるためショートカットという形で呼び出しているのかなと思います。

問題は解決できたけれど...

しかし結局のところなぜエラーが生じたのかわからかった。。
他のライブラリやモジュールでも同じエラー生じるはずなので。

npm i -g typescript@latest

バージョンが古すぎてそのまま上書きアプデできなかったとか??

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

Gecko Linux Install

GeckoLinux ←クリック(新窓) をインストールしました。
本家のサイトからDLし Install は簡単です。
DistroWatch の解説では
「Linux spin based on the openSUSE distribution.」
日本語は ibus がデフォとなっていますが
Fcitx + Mozc を使いたい・・
Language 等を Install した後、何故か勝手に更新が始まります。
終わると、 Yast ソフトウェアーセンター から Fcitx Mozc 等をInstall
この時、面倒なので ibus を削除しました。
このままでは日本語になりませんでした。
お馴染みとなっている3行のText を追加します。
「export GTK_IM_MODULE=fcitx
 export QT_IM_MODULE=fcitx
 export XMODIFIERS=@im=fcitx」
これを home .bashrc に追加
メソッドのトリガーを「zenkakuhankaku」にして
日本語入力、完了しました。
Screenshot from 2020-12-07 00-35-26
OpenSUSE 何となく個人的には重たいという印象があったんですが
Core 2 Duo の10年前のノートPCでサクサクと動いてくれます。

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