Skip to content

thakyuu/SFTPServer_Android

Repository files navigation

SFTPServer for Android

日本語 | English

Android 端末上で SFTP サーバーを動作させ、PC 等の SFTP クライアントから端末のファイルシステム(内部ストレージ・SD カード)へワイヤレスにアクセス・ファイル転送できるようにするアプリです。

開発者向けの要件、設計、品質基準、ロードマップは DEVELOP.md を参照してください。


概要

本アプリは Android デバイスを SFTP(SSH File Transfer Protocol)サーバーとして動作させ、同一 Wi-Fi ネットワーク上の PC などからワイヤレスでファイルを読み書きできるようにします。USB ケーブルなしで、使い慣れた SFTP クライアント(WinSCP、FileZilla、Cyberduck、OpenSSH の sftp コマンドなど)から端末のストレージへアクセスできます。

主な機能

  • SFTP サーバーの起動/停止: ホーム画面のワンタップボタンで起動・停止。稼働中は通知領域に常駐表示されます。
  • 認証方式
    • パスワード認証(ユーザー名・パスワードを設定画面から変更可能)
    • 公開鍵認証(Ed25519 / ECDSA / RSA。最大 10 鍵まで登録可能。GitHub アカウントからの一括インポートにも対応)
    • 双方を個別に有効/無効化でき、いずれかで認証成功すればログイン可能(両方無効時はサーバー起動不可)
  • 読み取り専用モード: 有効化すると、認証済みユーザーであっても書き込み・削除・リネーム・ディレクトリ作成が拒否されます。
  • ブルートフォース対策: 同一 IP からの連続認証失敗を検知し、一時的に接続を拒否します。
  • フォアグラウンドサービスによる常駐動作: アプリを閉じても、常駐通知からサーバー状態の確認・停止操作ができます。
  • ストレージアクセス
    • ネイティブモード(全ファイルアクセス権限で内部共有ストレージや SD カードに直接アクセス)
    • SAF モード(Storage Access Framework によるフォルダ選択。選択したフォルダのみに最小限の権限でアクセス)
  • 日本語・英語 UI: 端末言語に自動追従、アプリ内で手動切り替えも可能。
  • ログ機能: 接続・転送・エラーログのリアルタイム表示、フィルタ、検索、共有。

動作環境

項目 要件
OS Android 8.0 (API 26) 以上
ターゲット SDK Android 14 (API 34)
ネットワーク Wi-Fi(IPv4 / IPv6)。SFTP クライアントと同一ネットワークに接続していること
言語 日本語(デフォルト)・英語

本アプリは Google Play では配布せず、APK ファイルを直接配布(サイドロード)する形式です。


インストール手順

  1. 配布された APK ファイル(例: sftpserver-vX.Y.Z.apk)を Android 端末にコピーします。
  2. 端末で APK ファイルをタップしてインストールを開始します。
  3. 「提供元不明のアプリ」に関する警告が表示された場合は、以下の手順で許可してください。
    • Android 8〜10 系: インストール時のダイアログで「このソースを許可」を選択
    • Android 11 以降: 「設定」→「アプリ」→「特別なアプリアクセス」→「不明なアプリのインストール」から、APK を開いたアプリ(ファイルマネージャーやブラウザ)に対して許可を付与
  4. インストール完了後、アプリを起動します。

Google Play 経由のインストールではないため、Play Protect によるスキャン警告が表示されることがあります。配布元を信頼できる場合はそのままインストールを継続してください。

Obtainium でのインストールと自動更新

Obtainium を使うと、GitHub Releases から APK を直接取得し、継続的に自動更新できます。

  1. Obtainium アプリをインストールします(GitHub Releases または F-Droid から)
  2. Obtainium を起動し、「アプリを追加」→「URL を入力」
  3. 以下のリポジトリ URL を入力:
    https://github.com/thakyuu/SFTPServer_Android
    
  4. 「追加」をタップします。最新リリースの APK がインストールされ、以降は自動的に更新をチェックして通知します

初期設定

1. 認証情報の設定(必須)

初期認証情報はありません。サーバーを起動する前にユーザー名を設定し、パスワード認証または公開鍵認証の少なくとも一方を利用可能にしてください。

  1. 「設定」→「認証」を開く
  2. ユーザー名(1〜32文字)を設定する
  3. パスワード認証を使う場合はパスワード(8〜128文字)を設定する。公開鍵認証を使う場合は公開鍵を1件以上登録する
  4. 設定が完了するまでサーバーは起動できません

推奨: パスワードは推測困難なものにし、可能であれば公開鍵認証への切り替えを推奨します。

2. ストレージ権限の付与

サーバーがファイルへアクセスするために、いずれかのストレージアクセス方式の許可が必要です。

  • ネイティブ(推奨・簡単): 「設定」→「ストレージ」で「ネイティブ」を選択し、全ファイルアクセスを求められたら許可します(Android 11 以降は「特別なアプリアクセス」→「全ファイルアクセス」の設定画面が開きます)。
  • SAF(フォルダ選択): 「設定」→「ストレージ」で「SAF(フォルダ選択)」を選択し、「フォルダを選択」から公開したいフォルダをシステムのフォルダ選択 UI で選びます。選択した権限は永続化され、再起動後も有効です。

いずれの権限も許可されていない状態ではサーバーを起動できません。

ネイティブモードで SD カードへアクセス

ネイティブモードのアクセス先ディレクトリ選択ダイアログには、端末に挿入されている SD カード等の外部ボリュームが自動検出され、プリセットとして表示されます(表示名と /storage/XXXX-XXXX 形式のパス)。これを選ぶと、SAF のフォルダ選択 UI を経由せずに SD カードへ直接アクセスできます。

  • Android 11(API 30)以降: 全ファイルアクセス権限により SD カードへの読み書きが完全にサポートされます。
  • Android 8〜10(API 26〜29): プラットフォームの制約により、SD カード(セカンダリボリューム)への 書き込みができません。この区間で SD カードをネイティブモードのルートに設定すると、読み取り専用モード(「設定」→「サーバー動作」→「読み取り専用モード」)が有効な場合に限り読み取り専用で起動できます。書き込みが必要な場合は SAF(フォルダ選択)方式を使用してください。「このフォルダには書き込めません」というエラーが表示された場合は、この制約が原因です。

3. アクセス先ディレクトリの選択

「設定」→「ストレージ」→「アクセス先ディレクトリ」で、SFTP 接続時に公開するフォルダを選択します。ネイティブモードの場合はプリセット(例: /storage/emulated/0)または手入力、SAF モードの場合はフォルダ選択 UI で指定します。クライアントから選択範囲より上位へ移動することはできません。稼働中に変更した場合は、アプリがサーバーを安全に再起動して反映します。


使い方

サーバーの起動

  1. ホーム画面の起動ボタンをタップします。
  2. 状態表示が「稼働中」に変わり、接続先の IP アドレス一覧とポート番号が表示されます(デフォルトポート: 2222)。
  3. 複数の IP アドレス(Wi-Fi、IPv6 等)がある場合は一覧から該当するものを選び、タップしてコピーできます。

接続文字列

ホーム画面の「接続情報をコピー」ボタンから、以下の形式の接続文字列をクリップボードへコピーできます。

sftp://<ユーザー名>@<IP アドレス>:<ポート>

各クライアントからの接続例

OpenSSH(sftp コマンド/macOS・Linux・Windows)

sftp -P 2222 user@192.168.1.10

初回接続時にホスト鍵の確認を求められます。表示されたフィンガープリントが、アプリの「設定」→「バージョン情報」に表示される内容と一致することを確認したうえで承認してください。

公開鍵認証を使う場合:

sftp -P 2222 -i ~/.ssh/id_ed25519 user@192.168.1.10

WinSCP

  1. 「新しいサイト」→ プロトコルに SFTP を選択
  2. ホスト名: 端末の IP アドレス、ポート: 2222(既定)
  3. ユーザー名・パスワード、または秘密鍵ファイルを設定
  4. 「ログイン」で接続

FileZilla

  1. 「サイトマネージャー」→ 新しいサイトを作成
  2. プロトコル: SFTP - SSH File Transfer Protocol
  3. ホスト: 端末の IP アドレス、ポート: 2222
  4. ログオンの種類: 「通常」(パスワード認証)または「鍵ファイル」(公開鍵認証)
  5. 「接続」

Cyberduck

  1. 「新規接続」→ プロトコルで SFTP (SSH File Transfer Protocol) を選択
  2. サーバー: 端末の IP アドレス、ポート: 2222
  3. ユーザー名・パスワード、または SSH 秘密鍵を指定
  4. 「接続」

サーバーの停止

ホーム画面の停止ボタン、または常駐通知の「停止」アクションからサーバーを停止できます。停止時には接続中の全セッションが安全に切断されます。

稼働中の設定変更

ポート、認証、ネットワーク公開範囲、ストレージ、読み取り専用モードなどサーバー動作に影響する設定を変更すると、アプリがサーバーを安全に再起動して反映します。再起動時は接続中のセッションが切断されます。

GitHub から公開鍵をインポート

「設定」→「認証」→「公開鍵を管理」画面の右上にあるインポートアイコンから、GitHub アカウントに登録済みの公開鍵をまとめて取り込めます。

  1. インポートアイコン(雲マーク)をタップします。
  2. GitHub のユーザー名を入力して「OK」を押します。
  3. https://github.com/<ユーザー名>.keys(GitHub が無認証・平文で公開している公開鍵一覧)から鍵を取得し、authorized_keys に追加されます。コメントが付いていない鍵には自動的に github:<ユーザー名> というコメントが付与され、由来が分かるようになります。
  4. 完了すると「追加 n 件 / スキップ n 件 / 失敗 n 件」の形式で結果がスナックバーに表示されます。すでに登録済みの鍵(重複)や登録上限(10 件)に達している鍵はスキップとしてカウントされます。

GitHub 側にユーザーが存在しない、公開鍵が 1 件も登録されていない、通信に失敗した場合はエラーメッセージが表示されます。本機能は追加のパーミッションや外部ライブラリを必要とせず、既存の INTERNET 権限のみで動作します。


バッテリー最適化の除外設定

Android の多くの端末では、バックグラウンドで動作するアプリをバッテリー消費削減のため強制終了することがあります。SFTP サーバーをバックグラウンドで安定して稼働させ続けるには、本アプリをバッテリー最適化の対象から除外することを強く推奨します。

一般的な手順

  1. アプリの「設定」画面にある「バッテリー最適化」から「バッテリー最適化から除外」をタップします。
  2. システムダイアログが表示されるので許可します。
  3. 除外状態は設定画面に「除外済み」「未除外」として表示されます。
  4. システムダイアログが表示されない、または効果がない場合は、下記のメーカー別手順も併せて確認してください。

メーカー別の追加設定

OS のバッテリー最適化除外だけでは不十分な端末メーカーがあります。以下の手順も併せて設定してください(メニュー名は OS バージョンにより多少異なります)。

Xiaomi / Redmi / POCO(HyperOS・MIUI)

  1. 「設定」→「アプリ」→「アプリを管理」→ 本アプリを選択
  2. 「バッテリーセーバー」→「制限なし」を選択
  3. 「自動起動」をオンにする
  4. 「設定」→「バッテリー」→「省電力」→ 本アプリを「制限なし」に設定
  5. (HyperOS)「セキュリティ」アプリ →「バッテリー」→「省電力設定」からも同様の除外設定を確認

Huawei(EMUI / HarmonyOS)

  1. 「設定」→「アプリ」→「アプリ起動管理」→ 本アプリを選択
  2. 「手動で管理」に切り替え、「自動起動」「二次起動」「バックグラウンドでの動作」をすべて有効化
  3. 「設定」→「バッテリー」→「アプリの起動」で同様に個別管理をオンにする

Samsung(One UI)

  1. 「設定」→「アプリ」→ 本アプリを選択 →「バッテリー」
  2. 「バックグラウンドでの使用の制限なし」を選択
  3. 「設定」→「バッテリーとデバイスケア」→「バッテリー」→「バックグラウンドでの使用を制限されたアプリ」に本アプリが含まれていないことを確認
  4. スリープ状態のアプリ/未使用アプリの自動終了リストから本アプリを除外

OPPO / OnePlus(ColorOS)

  1. 「設定」→「バッテリー」→「アプリのバッテリー使用」→ 本アプリを選択 →「バックグラウンドでの実行を許可」を有効化
  2. 「設定」→「アプリ管理」→ 本アプリ →「バッテリー使用の詳細設定」→「バックグラウンドで実行」を許可
  3. 「省エネモード」等の一括最適化リストから本アプリを除外

Google Pixel(標準 Android)

  1. 「設定」→「アプリ」→「すべてのアプリ」→ 本アプリを選択
  2. 「バッテリー」→「制限なし」を選択
  3. 「アプリの一時停止」設定で本アプリが「未使用のアプリを一時停止」対象から除外されていることを確認

上記手順は OS バージョンやカスタム UI のアップデートにより変更される場合があります。表記と実際のメニュー名が異なる場合は、端末の「バッテリー」「省電力」関連の設定内を確認してください。


トラブルシューティング

クライアントから接続できない

  • サーバーとクライアントが同一 Wi-Fi ネットワークに接続されているか確認してください。
  • ホーム画面に表示されているポート番号とクライアント側の設定が一致しているか確認してください(既定 2222)。
  • 「設定」→「ネットワーク」で「ローカルのみ」(127.0.0.1 待受)が有効になっていないか確認してください。有効な場合、同一端末以外からは接続できません。
  • ルーターや端末側のファイアウォール設定でポートがブロックされていないか確認してください。
  • モバイルデータ通信のみで Wi-Fi に接続していない場合、サーバーは到達可能な IP アドレスを持たない可能性があります。

権限エラーが表示される/サーバーが起動しない

  • 「設定」→「ストレージ」で、選択中のストレージ方式(ネイティブまたは SAF)の権限が許可されているか確認してください。
  • ネイティブモードの場合、全ファイルアクセスの許可がシステム設定上で無効化されていないか確認してください。
  • 認証方式(パスワード・公開鍵)が両方とも無効になっているとサーバーは起動できません。少なくとも一方を有効にしてください。
  • パスワード認証を使う場合はパスワード、公開鍵認証だけを使う場合は公開鍵が登録済みか確認してください。
  • 起動失敗ダイアログに表示される原因と復旧操作に従ってください。

SAF で選択したフォルダにアクセスできない(URI 失効)

SD カードの抜き差しや OS のアップデート等により、SAF で選択したフォルダへの永続アクセス権限が失効することがあります。設定画面に「フォルダにアクセスできません」という警告が表示された場合は、「再選択」からフォルダを選び直してください。

接続時にホスト鍵の警告が表示される

初回接続時にクライアントがホスト鍵の確認を求めるのは正常な動作です。表示されたフィンガープリントが、アプリの「設定」→「バージョン情報」に表示されているホスト鍵のフィンガープリントと一致することを確認してから承認してください。アプリを再インストールする、またはホスト鍵を再生成すると、クライアント側で「ホスト鍵が変更された」という警告が表示されます。この場合は新しいフィンガープリントを別経路で確認し、クライアントの known_hosts から該当エントリを削除して再接続してください。心当たりがない場合は接続を中止してください。

バックグラウンドで停止する

設定画面でバッテリー最適化の除外状態を確認し、端末メーカー独自の自動起動・省電力・バックグラウンド制限も解除してください。Android やメーカーの仕様により、除外後も OS がプロセスを終了する可能性があります。


開発者向け情報

実装時に守る要件、アーキテクチャ、テスト方針、未検証事項、今後の機能候補は DEVELOP.md を正本とします。

ソースからのビルド

  1. Android Studio(最新安定版推奨)でプロジェクトルート(本リポジトリ)を開きます。
  2. 初回オープン時に Gradle Sync が実行され、gradle/wrapper/gradle-wrapper.properties の指定に基づき Gradle wrapper が解決されます。
  3. 依存解決完了後、app モジュールを選択し、実機またはエミュレータ(API 26 以上)で実行します。

主要な Gradle タスク

# JDK は Android Studio 同梱のものを使用(例)
# Windows PowerShell の場合:
$env:JAVA_HOME = "C:\Program Files\Android\Android Studio\jbr"

# 単体テスト(JVM)
./gradlew.bat testDebugUnitTest

# コードフォーマットチェック(ktlint)
./gradlew.bat ktlintCheck

# 静的解析(detekt)
./gradlew.bat detekt

# domain/data 層のカバレッジ検証(閾値 70%)
./gradlew.bat jacocoDomainDataCoverage

# Android instrumented test(接続済み端末またはエミュレーターが必要)
./gradlew.bat connectedDebugAndroidTest

リリースビルド

  1. 署名鍵を未生成の場合は生成します。

    keytool -genkeypair -v -keystore release.jks -alias sftpserver -keyalg RSA -keysize 4096 -validity 10000
  2. リポジトリルートの keystore.properties.samplekeystore.properties としてコピーし、storeFile / storePassword / keyAlias / keyPassword を実際の値に置き換えます。keystore.properties.gitignore 対象のためコミットされません。

  3. リリース APK をビルドします。

    ./gradlew.bat assembleRelease

    keystore.properties が存在しない場合でも Gradle Sync や assembleRelease 自体は失敗しませんが、生成される APK は署名なし(インストール不可)になります。配布用ビルドでは必ず署名設定を行ってください。

コーディング規約

  • Kotlin コーディング規約(ktlint)、detekt による静的解析(config/detekt/detekt.yml)に準拠します。
  • コミット前に ktlintCheck / detekt / testDebugUnitTest / jacocoDomainDataCoverage を通してください。

ライセンス

本アプリ自体のライセンスは MIT License です。詳細は LICENSE ファイルを参照してください。

本アプリが使用しているオープンソースソフトウェアのライセンス一覧は、アプリ内の「設定」→「バージョン情報」画面から確認できます。

About

No description, website, or topics provided.

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors