Cronの時間指定をお客さんに任せたい
TL;DR
「バックアップの時間を変えてほしい」「監視の間隔を調整したい」...そんなお客さんからの依頼、毎回サーバーにSSHで入ってcrontab -eしてませんか?
もうやめましょう。お客さんに任せましょう。
Go標準ライブラリだけで作ったWebアプリ「go-cron-gate」を使えば、お客さんがブラウザからポチポチするだけでcronの時間設定を変更できます。開発者はもうcronの設定変更から解放されます。

お客さんでも使えるシンプルなWebインターフェース
きっかけ:また「cron変えて」って言われた...
普段はWebエンジニアとして開発したり、QAエンジニアとしてテストしたりしています。Goの経験は正直浅くて、簡単なRESTAPIを作ったことがある程度でした。
ある日のこと、お客さんから連絡が来ました。
「すみません、バックアップの時間を夜中の2時から夜10時に変更してもらえますか?」
はい、また来ました。cronの時間変更依頼です。
これで何度目でしょうか。毎回サーバーにSSH接続して、crontab -eして、時間を変更して...。作業自体は数分で終わりますが、この「毎回」が地味にストレスなんですよね。
しかも、お客さんからすれば「ちょっと時間を変えるだけ」のはずなのに、わざわざ連絡して待たなきゃいけない。お互いにとって非効率すぎる。
「これ、お客さんが自分で変更できたら楽なのに...」
そう思ったのが、このプロジェクトの始まりでした。
なぜ「お客さんに任せたい」のか
開発者側の本音
- 毎回SSH接続するの面倒: たった1行変更するだけなのに...
- 深夜・休日の対応: 「明日の朝までに変えておいて」が地味にしんどい
- 優先度の問題: 他の開発作業があるのに時間を取られる
- コミュニケーションコスト: 要件確認、作業完了報告...
お客さん側の本音(多分)
- 連絡するのが申し訳ない: こんな簡単なことで...
- 待たなきゃいけない: すぐ変更したいのに
- 現状が分からない: 今どんな設定になってるんだっけ?
- 試行錯誤できない: ちょっと試したいだけなのに毎回依頼...
でも現実は...
「お客さんにSSHとcrontabの知識を求めるのは無理」
これが現実です。crontab -eなんて、エンジニアでも最初は戸惑うもの。お客さんに「0 2 * * *って書いて」なんて言えるわけがありません。
なので、Webインターフェースを作ることにしました。
「お客さんに任せられる」ための工夫
1. 技術用語を徹底的に排除
細かすぎる設定をしない場合はcronの0 2 * * *みたいな謎の記号は一切出てきません。
代わりに:
- 「毎日」「毎週」「毎月」などの分かりやすいプリセット
- 時間は普通の時計形式で選択
- 「有効」「無効」の状態が一目で分かる
お客さんが見るのは、日本語だけです。
2. でも開発者も安心できるセキュリティ
「お客さんに任せる」といっても、野放しにするわけにはいきません。
- Basic認証で保護: 誰でもアクセスできるわけじゃない
- 入力値は徹底チェック: 変な設定はそもそも保存できない
- タイミング攻撃対策済み: セキュリティの基本は押さえてます
お客さんには自由に、でも開発者には安心を。
3. 保守しやすさを最優先
「便利だけどメンテできない」では意味がありません。
- Go標準ライブラリのみ: npm installとかしなくていい
-
単一バイナリ:
scpで1ファイル送るだけ - テストカバレッジ90%+: 今後の機能改修を見据えて
実際にお客さんに任せてみた結果
お客さんの反応
before:
「すみません、バックアップの時間を夜10時に変更してもらえますか?」
(連絡 → 待つ → 確認 → ありがとうございます)
after:
「あ、自分で変えておきました!」
(終わり)
最高
技術的な話:AI(VibeCoding)に頼りまくった
このプロジェクト、ほぼAIが書きました。
Goの経験が浅い私一人では、こんなにちゃんとしたものは作れなかったと思います。でも、VibeCoding(AI支援コーディング)を使うことで、「やりたいこと」を伝えるだけで高品質なコードが生成されました。
使用技術スタック
- Go 1.21+: メインの開発言語
- 標準ライブラリのみ: 外部依存を避けてシンプルな構成(本番環境)
- HTMLテンプレート: サーバーサイドレンダリング
- Basic認証: セキュアなアクセス制御
-
テスト用依存:
github.com/stretchr/testify(テストコード専用)
アーキテクチャ設計
主要コンポーネント
-
カスタムHTTPルーター:
http.ServeMuxを使用し、グローバルmuxよりも良い分離を実現 -
統一的なエラーハンドリング:
internal/errorsパッケージによる構造化ログと集中的なエラー処理 - スレッドセーフな操作: Mutexによるcrontabファイルへの並行アクセス保護
- 包括的なテスト: 単体テストと統合テストで90%以上のカバレッジ
プロジェクト構成
go-cron-gate/
├── main.go # アプリケーションエントリーポイント
├── main_test.go # メインアプリケーションの単体テスト
├── internal/
│ ├── auth/
│ │ ├── auth.go # タイミング攻撃対策付きBasic認証
│ │ └── auth_test.go # 認証の単体テスト
│ ├── cron/
│ │ ├── service.go # スレッドセーフなcrontabファイル操作
│ │ ├── service_test.go # cronサービスの単体テスト
│ │ └── service_bench_test.go # パフォーマンスベンチマークテスト
│ ├── errors/
│ │ ├── errors.go # 統一的なHTTPエラーハンドリング
│ │ └── errors_test.go # エラーハンドリングの単体テスト
│ ├── handler/
│ │ ├── handler.go # HTTPリクエストハンドラ
│ │ └── handler_test.go # ハンドラの単体テスト
│ └── template/
│ ├── template.go # 日本語UI対応のHTMLテンプレート
│ └── template_test.go # テンプレートレンダリングとXSS対策のテスト(26テスト)
├── test/
│ ├── integration_test.go # エンドツーエンドの統合テスト
│ └── test_crontab.txt # テスト用サンプルcrontab
└── docs/ # プロジェクトドキュメント
セキュリティの実装詳細
1. タイミング攻撃対策
// crypto/subtileを使用した定数時間比較
userMatch := subtle.ConstantTimeCompare([]byte(user), []byte(ba.username))
passMatch := subtle.ConstantTimeCompare([]byte(pass), []byte(ba.password))
// 両方の比較を常に実行し、情報漏洩を防ぐ
if (userMatch & passMatch) != 1 {
ba.unauthorized(w)
return
}
2. HTTPサーバーの強化
server := &http.Server{
Addr: ":" + port,
Handler: mux,
ReadTimeout: 15 * time.Second,
WriteTimeout: 15 * time.Second,
IdleTimeout: 60 * time.Second,
MaxHeaderBytes: 1 << 20, // 1 MB
}
使用方法
1. インストール
# ソースからビルド
git clone https://github.com/0fuzimaru0/GoCronGate.git
cd go-cron-gate
go build -o cron-gate
2. アプリケーションの起動
./cron-gate -port 8080 -cron /var/spool/cron/crontabs/root -user admin -pass secret123
コマンドラインオプション:
-
-port: HTTPサーバーのポート番号(デフォルト: 8080) -
-cron: crontabファイルのパス(デフォルト: /var/spool/cron/crontabs/root) -
-user: Basic認証のユーザー名(必須) -
-pass: Basic認証のパスワード(必須)
3. Webインターフェースへのアクセス
ブラウザで http://localhost:8080 にアクセスし、設定した認証情報でログインします。
4. ローカルテスト用の設定
# テスト用crontabファイルの作成
cat > test_crontab.txt << 'EOF'
# Example crontab for testing
# Run backup every day at 2am
0 2 * * * /usr/local/bin/backup.sh
# Run cleanup every Sunday at 3am
0 3 * * 0 /usr/local/bin/cleanup.sh
# Run monitoring every 5 minutes
*/5 * * * * /usr/local/bin/monitor.sh
# Disabled job
# 0 4 * * * /usr/local/bin/disabled.sh
EOF
# アプリケーションの起動
./cron-gate -port 8081 -cron ./test_crontab.txt -user admin -pass test123
テストと品質保証
プロジェクトには包括的なテストが含まれており、90%以上のテストカバレッジを達成しています。QAエンジニアとしての経験を活かし、以下の観点でテストを設計しました。
テストカバレッジ詳細
| パッケージ | カバレッジ |
|---|---|
| auth | 100% |
| cron | 95.7% |
| errors | 100% |
| handler | 77.6% |
| main | 90.9% |
| 全体 | 90%+ |
テスト戦略
- 単体テスト: 各コンポーネントの動作確認(26テストケース含む)
- 統合テスト: エンドツーエンドのワークフロー確認
- ベンチマークテスト: パフォーマンスの計測と最適化
- セキュリティテスト: 認証機能とタイミング攻撃対策の動作確認
- エラーハンドリングテスト: 異常系の動作確認
- XSS対策テスト: テンプレートエンジンのセキュリティ確認
# テストの実行
go test ./... -v
# カバレッジレポートの生成
go test ./... -coverprofile=coverage.out
go tool cover -html=coverage.out
# ベンチマークテストの実行
go test -bench=. ./internal/cron/
デプロイと運用
本番環境での運用
systemdサービスとして実行
# /etc/systemd/system/cron-gate.service
[Unit]
Description=Cron Gate Web Manager
After=network.target
[Service]
Type=simple
User=root
ExecStart=/usr/local/bin/cron-gate -port 8080 -cron /var/spool/cron/crontabs/root -user admin -pass YOUR_SECURE_PASSWORD
Restart=on-failure
RestartSec=5s
[Install]
WantedBy=multi-user.target
# サービスの有効化と起動
sudo systemctl daemon-reload
sudo systemctl enable cron-gate
sudo systemctl start cron-gate
Nginxとの連携(本番環境推奨)
server {
listen 443 ssl http2;
server_name cron.yourdomain.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location / {
proxy_pass http://localhost:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
同じ悩みを持つ人へ
もし「毎回cron変更するの面倒だな...」と思っているなら、このアプリを使ってみてください。
あるいは、自分で似たようなツールを作ってみるのもいいかもしれません。AIの力を借りれば、思ったより簡単に作れます。
リポジトリ: https://github.com/0fuzimaru0/GoCronGate
気になった方は、ぜひスターをください ⭐
同じ悩みを持つ人の助けになれば嬉しいです。
Discussion