タイムアウトとstraceユーティリティを使用してユーザーの非アクティブを監視し、Shellinabox接続を切断する

最近、私はweb-sshプロキシサーバーを実装するためにどのようなソリューションが存在するかを調査しました。 タスクの本質は、ユーザーがWebインターフェースを介して任意のsshサーバーに接続できるようにすることです。 通常、web-sshソリューションはデプロイ先のサーバーに接続するように設計されていますが、私のタスクの一環として、ユーザーにIP、ポート、ユーザー名、パスワード(またはキー)を指定して、任意のサーバーに接続してもらいたいと思いました。 すぐにそのような解決策を見つけることができませんでした。







実際には、もちろん、ワカモレがありますが、私のタスクでは、このアプリケーションの使用は、開発リソースと機能およびその組織の両方でコストが高すぎたため、ワカモレを拒否しました。

しかし、オープンシェルナボックスパッケージについては、ドイツ語のブログで解決策を見つけました。それを必要なレベルに引き上げることにしました。 その結果、 GitHubDockerhubの両方にある素敵なDockerコンテナーが得られ、必要なタスクをすべて解決できます。







しかし、この記事はそれについてではなく、私が書かなければならなかったコンパニオンPythonコードについてです。 事実、ユーザーがweb sshを開いてどこかを離れると、セッションが無期限にハングアップするのが気に入らなかったのです。 これは、次のマイナスの結果につながります。







  1. ターミナルプロキシリソースは、未使用のセッションを処理することを余儀なくされるため、非効率的に使用されます。
  2. ブラウザにsshがある無人ウィンドウは、切望と脆弱性を感じさせます。


一般に、ユーザーが数分間コンソールに何も書き込まず(stdin)、stdoutにデータが送信されない場合、shellinaboxが接続を切断することを確認することにしました。







かなり長いGoogle検索で、timeoutコマンドとstraceコマンドを使用して目標を達成できることがわかりました。 タイムアウトコマンドは私にとって新しいものであることが判明しました。その目的は、完了していない場合に特定のタイムアウトに達するとプロセスが中断されるようにすることです。







私は頻繁にstraceコマンドを使用しますが、通常は、一部のサービスまたはコマンドが期待どおりに機能しない理由を追跡するために使用します。 stdinチャネルでのアクティビティ、stdoutプロセスを監視する方法に関する私の検索の一環として、straceでもこれを提供できることがわかりました。







strace -e write=1,2 -e trace=write -p <PID>
      
      





一般に、これらのコマンドは、計画を実装するために必要なものでした。 一般的なスキームは次のようになります。







  1. 新しい接続を開くと、shellinaboxはpythonスクリプトを実行します。
  2. Pythonスクリプトは、プロセスのアクティビティを監視するためにデーモンプロセスを開始(フォーク)します。デーモンプロセスは独自のPIDを監視します。
  3. Pythonスクリプトは(exec)sshを実行します(自分自身をsshに置き換えます)。


スクリプト全体がどのように配置されているかをすぐに見たい場合は、 ここに送信します 。 残りの部分はさらに部分的に。







簡単に言うと、コードは次のように表すことができます。







 monitor_daemon(inactivity_interval, identity_file) ... ... os.execv("/usr/bin/ssh", ["/usr/bin/ssh"] + identity_args + ["-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null", "-p", str(peer_port), "%s@%s" % (peer_login, peer_ip)])
      
      





コードはシンプルで自明です。 すべての魔法はmonitor_daemon



関数内にあります。







 def monitor_daemon(inactivity_interval, identity_file): orig_pid = os.getpid() try: pid = os.fork() if pid > 0: return except OSError as e: print("Fork #1 failed: %d (%s)" % (e.errno, e.strerror)) sys.exit(1) os.chdir("/") os.setsid() os.umask(0) try: pid = os.fork() if pid > 0: sys.exit(0) except OSError as e: print("Fork #2 failed: %d (%s)" % (e.errno, e.strerror)) sys.exit(1) if identity_file != "": time.sleep(1) os.unlink(identity_file) try: while True: proc = subprocess.Popen('timeout %d strace -e write=1,2 -e trace=write -p %d' % (inactivity_interval, orig_pid), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) proc.poll() counter = 0 for line in proc.stderr.readlines(): counter += 1 if(counter <= 3): os.kill(orig_pid, signal.SIGKILL) sys.exit(0) except Exception as e: pass sys.exit(0)
      
      





私たちがこの部分に最も興味を持っている場所:







 while True: proc = subprocess.Popen('timeout %d strace -e write=1,2 -e trace=write -p %d' % (inactivity_interval, orig_pid), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) proc.poll() counter = 0 for line in proc.stderr.readlines(): counter += 1 if(counter <= 3): os.kill(orig_pid, signal.SIGKILL) sys.exit(0)
      
      





ループ内で監視が開始され、指定された待機期間の後、必要に応じてsshプロセスが完了します。







それだけです。 ソリューションは非常にシンプルであることが判明しましたが、利用可能なツールを調査するのに時間がかかりました。







PS:私はプロのPython開発者ではありません。コードは最適ではありません。

PPS:誰かがコードを改善したいという要望があれば、 リポジトリへようこそ、私は喜んでPRを受け入れます。








All Articles