VPN廃止してゼロトラスト導入した話——2年の本番運用で見えた地雷と対策

セキュリティ監査がきっかけでVPNを廃止しゼロトラストへ移行。本番で次々と予想外の問題が発生した2年間の実装経験から、実際に機能した対策と失敗例を赤裸々に語ります。

ゼロトラストアーキテクチャ本番2年で見えた現実——VPN廃止から信頼ゼロへ

去年の今頃、うちのチームはVPNを廃止する決断をしました。今思えば、あの判断が2年間の地獄の始まりだったんですが、逆に「ゼロトラストって本当に何なのか」が骨身に染みてわかった期間でもあったんですよね。

セキュリティ業界だと「ゼロトラストはこれからのスタンダード」みたいな話をよく聞くと思うんですが、正直、本番で動かしてみると話は全然違う。教科書通りに進まない現実、予想外の運用負荷、そして「あ、これは本気で信頼しないってこういうことか」みたいな腹落ちの連続でした。

VPN廃止までの経緯と、最初の失敗

きっかけはセキュリティ監査でした。SOC2の取得を目指す過程で、監査人から「VPN経由のアクセスだけだと、デバイス未管理の従業員が混在しているリスクがありませんか?」と指摘されたんです。当時、我々はVPN接続さえしていればOKという時代遅れの考え方をしていました。

そこで「ゼロトラストアーキテクチャ」の導入を決めたんですが——これがどれだけ大変か、当初は全く想像していませんでした。

最初は導入ツールの選定で3ヶ月迷いました。AWS PrivateLink、Cloudflare Zero Trust(旧Cloudflare Access)、Zscaler、Okta Identity Cloud……もう選択肢が多すぎて、各ツール会社のデモを受けるだけで月単位で時間が溶けた。

結局、我々が選んだのはAWS PrivateLink + AWS IAM Identity Center + AWS Network Firewallの組み合わせです。理由は「既にAWSに重く依存していたから」という、正直なところ消極的な選択でした。でも今振り返ると、この選択が本当に正解だったと思います。

本番導入3ヶ月後のカオス

VPNを完全に廃止した直後、本社オフィスでログインできない従業員が30人以上出ました。原因は「デバイス登録の失敗」でした。我々が想定していなかったのは、従業員の持ち込みデバイスがすべて同じAWSアカウント認証で登録されていなかった点。

ゼロトラストの本質は「デバイスレベルでの信頼検証」なんですが、オンプレミスのノートPC、個人所有のMac、Windows企業版……その全てがAWS IAM Identity Centerのデバイス登録プロセスに対応していなかったんです。

# 当時のデバイス登録状況を把握するスクリプト
import boto3
from datetime import datetime

identity_center = boto3.client('identitystore')

def check_device_compliance():
    # ゼロトラスト適用前後のデバイス登録の差分を確認
    devices = identity_center.list_devices()
    compliant = 0
    non_compliant = 0
    
    for device in devices['Devices']:
        # デバイス信頼スコア(OS更新、ファイアウォール有効化など)を確認
        if device['DeviceTrustScore'] >= 80:  # 信頼スコア80以上
            compliant += 1
        else:
            non_compliant += 1
            print(f"Non-compliant: {device['DeviceId']} - Score: {device['DeviceTrustScore']}")
    
    print(f"Compliant: {compliant}, Non-compliant: {non_compliant}")

check_device_compliance()

結果、3日で「やっぱりVPN併用します」という判断をせざるを得ませんでした。完全廃止じゃなく、ハイブリッド運用に舵を切ったんです。

信頼スコアリングの設計が地獄

ゼロトラストを語る上で欠かせないのが「信頼スコア」の概念です。「デバイスが最新パッチを当たってるか」「ファイアウォールは有効か」「異常な場所からのアクセスか」——こういった複数の要素を点数化して、リアルタイムに認可を判断するってわけです。

ウチが実装した信頼スコアリングはこんな感じ:

要素スコア加算条件
OS最新パッチ+207日以内に更新
ファイアウォール有効+15Windows Defender/macOS SSD有効
デバイス暗号化+15BitLocker/FileVault有効
VPN経由アクセス+10PrivateLink利用
MFA認証成功+20直近30日以内
通常のロケーション+10過去30日のアクセス場所内
異常な通信パターン-30深夜アクセス大量トランザクション等

これを実装したときの苦労は、「信頼スコアって何点なら許可するの?」という議論が終わらなかったことです。

セキュリティ側は「60点以上なら許可」と言ったし、開発チームは「そんなことしたら誰も動けない」と反発した。営業チームに至っては「顧客対応のためにスコア50でも許可すべき」と主張してました。

結局、リソースの重要度に応じて基準を分けることにしたんです:

  • 本番DB・金銭系システム: 80点以上
  • 社内システム: 60点以上
  • 開発環境: 40点以上

でも正直、この「点数」という概念自体が曖昧なんですよね。80点の定義がプロジェクトごとに微妙に違ったり、新しいデバイスタイプが出てくると基準が揺らいだり。完全に自動化できるわけじゃなく、かなりの人手判断が入ってるんが実情です。

AWS Network Firewallの痛い現実

2026年時点のゼロトラスト構成で欠かせないのが、AWSレイヤーでのネットワーク検査です。ウチはAWS Network Firewallを導入しました。

# CloudFormation でのNetwork Firewall設定
NetworkFirewall:
  Type: AWS::NetworkFirewall::Firewall
  Properties:
    Name: ZeroTrustFirewall
    VpcId: !Ref VPC
    SubnetMappings:
      - SubnetId: !Ref PrivateSubnetA
      - SubnetId: !Ref PrivateSubnetB
    FirewallPolicyArn: !Ref FirewallPolicy

FirewallPolicy:
  Type: AWS::NetworkFirewall::FirewallPolicy
  Properties:
    FirewallPolicy:
      StatelessDefaultActions:
        - aws:forward_to_sfe  # Stateful Firewall Engineへ転送
      StatelessFragmentDefaultActions:
        - aws:forward_to_sfe
      StatefulRuleGroupReferences:
        - ResourceArn: !Ref ManagedRuleGroup
      TLSInspectionConfiguration:
        - CheckCertificateRevocation: ENABLED
          ScopeConfiguration:
            DestinationPorts:
              - FromPort: 443
                ToPort: 443

これで何が起きたかというと、TLS 1.3通信のインスペクションで全トラフィックが10%遅くなりました。マイクロサービス間の通信がミリ秒単位で増加して、SLOが引っかかり始めたんです。

正直、Network Firewallって「あ、これ本番運用して初めて気づく重さだな」って感じました。数字だけなら「10%」って小さく見えるんですが、1秒かかってた通信が1.1秒になると、ユーザーはちゃんと遅さを感じるんですよね。

リアルタイム認可の意思決定エンジン

ゼロトラストって名前だけ聞くと、なんか一回の認証で終わるイメージじゃないですか。でも実はそうじゃなくて、アクセスのたびに「このリクエストを許可していい?」を判断し続ける必要があります。

ウチが実装したのはこんな設計:

graph TB
    subgraph "Client Layer"
        User["User<br/>IAM Identity Center"]
        Device["Device Posture<br/>Jamf Pro"]
        Network["Network Context<br/>VPC Flow Logs"]
    end
    
    subgraph "Decision Engine"
        TrustCalc["Trust Score<br/>Calculation"]
        PolicyEval["Policy<br/>Evaluation"]
        RiskAssess["Risk Assessment<br/>ML Model"]
    end
    
    subgraph "Enforcement"
        AuthZ["Authorization<br/>AWS IAM"]
        RateLimit["Rate Limiting<br/>API Gateway"]
        NetworkPolicy["Network Policy<br/>Security Groups"]
    end
    
    subgraph "Monitoring"
        Logging["CloudWatch Logs"]
        Alerts["Real-time Alerts<br/>EventBridge"]
        Audit["Audit Trail<br/>CloudTrail"]
    end
    
    User -->|Identity| TrustCalc
    Device -->|Posture Score| TrustCalc
    Network -->|Context| TrustCalc
    
    TrustCalc --> PolicyEval
    RiskAssess --> PolicyEval
    
    PolicyEval -->|Allow/Deny/MFA| AuthZ
    PolicyEval -->|Rate Limit Adjust| RateLimit
    PolicyEval -->|Dynamic SG| NetworkPolicy
    
    AuthZ --> Logging
    RateLimit --> Logging
    NetworkPolicy --> Logging
    
    Logging --> Alerts
    Logging --> Audit

このアーキテクチャで何が大変かって、「すべてのアクセスが可視化される」ってことなんです。つまり、従業員がいつどこから何にアクセスしたかが全部記録される。

プライバシーの観点から、これどうするのか?って議論が月単位で続きました。結果、以下のポリシーで落ち着きました:

  • セキュリティチームのみ: 全ログ閲覧可能
  • マネージャー: 自チーム員のログのみ閲覧可能
  • 一般従業員: ログへのアクセス不可
  • 個人データアクセス: 自分のアクセスログのみ閲覧可能

この設計でバランスを取ってるんですが、未だに「これで大丈夫?」って感じはどこか抜けません。

異常検知で何度も本番が止まった

ゼロトラストの理想形は「異常なアクセスパターンを検知して即座にブロック」ですよね。ウチもそれを目指して、CloudWatch + Lambda + 簡単なML(ちょっと大げさですが)で異常検知を実装しました。

import json
import boto3
from datetime import datetime, timedelta

logs_client = boto3.client('logs')
iam_client = boto3.client('iam')

def detect_anomalies():
    """異常なアクセスパターンを検知"""
    
    # 過去1時間のアクセスログを取得
    query = """
    fields @timestamp, userIdentity.principalId, sourceIPAddress, eventName
    | stats count() as attempt_count by userIdentity.principalId, sourceIPAddress
    | filter attempt_count > 50
    """
    
    # 同じユーザーが短時間に複数のIPから大量アクセス
    # = 認証情報の流出 or 正規のバッチ処理
    
    response = logs_client.start_query(
        logGroupName='/aws/cloudtrail/zero-trust',
        startTime=int((datetime.now() - timedelta(hours=1)).timestamp()),
        endTime=int(datetime.now().timestamp()),
        queryString=query
    )
    
    # ブロック対象かどうかを判定(ホワイトリスト確認)
    anomalies = []
    for log_entry in response['results']:
        principal_id = log_entry['userIdentity.principalId']
        
        # ホワイトリストチェック
        if is_whitelisted(principal_id):
            continue
        
        anomalies.append({
            'user': principal_id,
            'ip': log_entry['sourceIPAddress'],
            'attempts': log_entry['attempt_count'],
            'action': 'BLOCK'  # または ALERT or STEP_UP_MFA
        })
    
    return anomalies

def is_whitelisted(principal_id):
    """本番バッチ処理やCI/CDなど、正規の大量アクセス元をホワイトリストに含める"""
    whitelist = {
        'arn:aws:iam::123456789:role/DataProcessingBatch',
        'arn:aws:iam::123456789:role/GitHubActionsRunner',
        # ... 他のサービスロール
    }
    return principal_id in whitelist

これを本番環境に展開した最初の週、異常検知エンジンが「営業チームが朝9時に15人が同じIPから東京のオフィスにアクセスした」ことを「異常」と判定して、全員ブロックしてしまいました。

もちろん、それは「朝出社したら全員同じネットワークになる」という当たり前の状況だったんですが、ML的には「異常」に見えたんです。その日は営業チームが完全に仕事できなくなって、大騒ぎになりました。結局、オフィスネットワークを全部ホワイトリストに加える羽目になったんですが、それって「ゼロトラストの理想」からはずれてるんじゃないか……という葛藤は今もあります。

インシデント対応連携で痛い目

ゼロトラストアーキテクチャを導入すると、セキュリティインシデント発生時の対応フローも全く変わります。VPN時代は「VPN接続を切る」で一旦隔離できました。でも今は、個別のユーザーやデバイスの信頼スコアを即座に下げるか、特定のアクセス権限を剥奪するっていう、より細かい判断が求められるんです。

我々が経験したインシデント(2026年3月)について話すと:

事象: ある営業の認証情報がダークウェブで売却されているのを発見

当時の対応フロー:

  1. 11:30 セキュリティチーム:「その営業のIAM権限を全て剥奪」と判断
  2. 11:35 その営業:顧客対応中だったので、システムアクセスが突然できなくなり混乱
  3. 11:40 営業マネージャー:「なぜ勝手にアクセス権を剥奪した?」とセキュリティに怒鳴り込む
  4. 12:00 インシデント対応チーム:結局「その営業のデバイスはかなり古いし、パッチも当たってない。多分そこから漏れた」と判定
  5. 13:00 その営業:新しいデバイスで復旧、でもすっかり信用を失った

この経験から学んだのは、「ゼロトラスト = 信頼ゼロ」だとしても、運用段階では「信頼をどう回復するか」というプロセスが超重要だということ。セキュリティだけで判断すると、組織は回らないんです。

現在は以下のフローに変更しました:

flowchart TD
    Incident["Security Incident<br/>Detected"] --> Assess{"Risk Level?"}
    
    Assess -->|Critical<br/>Data Breach| Immediate["Immediate Access<br/>Revocation"]
    Assess -->|High<br/>Credential Leak| StepUp["Step-up MFA<br/>+ Score Reduction"]
    Assess -->|Medium| Alert["Alert User<br/>+ Monitoring"]
    
    Immediate --> Notify1["Notify User<br/>+ Manager"]
    StepUp --> Notify2["Notify User<br/>Device Check Required"]
    Alert --> Notify3["Log Alert<br/>Continue Monitoring"]
    
    Notify1 --> Recovery["Recovery Process<br/>Device Verification"]
    Notify2 --> Recovery
    Recovery --> Restore["Gradual Permission<br/>Restoration"]
    Restore --> Audit["Post-Incident<br/>Audit"]

ここで大事なのは「Gradual Permission Restoration」です。全員一気に復旧じゃなく、段階的に権限を戻す。その間に「本当に大丈夫なのか」を確認する時間を設ける。地味ですが、これが組織的信頼を保つには必須だと気づきました。

監査とコンプライアンスの現実

ゼロトラスト導入した一番の成果は、正直「SOC2審査がめっちゃ楽になった」ってことです。

従来型のセキュリティ架構では「VPNで守られてますよ」という説明だけで、監査人に「でもそれって本当に全員が守られてるんですか?」と突っ込まれてました。でも今は、すべてのアクセスが記録されて、リアルタイムに信頼スコアで検証されているという具体的なデータを見せられます。

SOC2審査での主要チェック項目を比較すると:

項目VPN時代ZTA後
認証・認可の検証「VPN接続で確認」← 監査人に怒られるCloudTrail + IAM Identity Center で全て記録
デバイス管理「イメージドライブで統制」← 外部デバイス不明Jamf Pro で全デバイス可視化
ネットワーク分離「セグメント分けしてます」← 曖昧SecurityGroup + Network Firewall で明確
インシデント対応事後的な調査リアルタイムアラート + 自動遮断

これだけ見ると「ゼロトラスト最高」って思いますよね。でも、実装のコスト(金銭的にも運用的にも)は想像を超えてました。AWS年間費用だけで月額3倍になったし、セキュリティチームの人員も増やさざるを得なかった。

2026年時点のゼロトラスト構成(ウチの構成)

こんだけ地獄を見てきた結果、ウチが落ち着いた構成がこれです:

subgraph "Identity Layer"
    IAM["AWS IAM<br/>Identity Center"]
    MFA["MFA<br/>Duo Security"]
    SAML["SAML 2.0<br/>Corporate IdP"]
end

subgraph "Device Posture"
    MDM["Jamf Pro<br/>Device Management"]
    EDR["CrowdStrike<br/>EDR Agent"]
    ComplianceCheck["Compliance Check<br/>OS/FW/Patch"]
end

subgraph "Network Layer"
    PrivateLink["AWS PrivateLink"]
    NFW["Network Firewall<br/>TLS Inspection"]
    SG["Security Groups<br/>Dynamic Rules"]
end

subgraph "Decision Engine"
    TrustEngine["Trust Score<br/>Engine<br/>(Lambda)"]
    PolicyEngine["Policy Engine<br/>(Cedar Lang)"]
    RiskDetection["Anomaly Detection<br/>(CloudWatch + ML)"]
end

subgraph "Access Control"
    AuthzGateway["Authorization<br/>Gateway"]
    RateLimit["API Rate Limiting"]
    LoggingAudit["CloudTrail<br/>Access Logs"]
end

IAM --> MFA
SAML --> IAM
MFA --> TrustEngine

MDM --> ComplianceCheck
EDR --> ComplianceCheck
ComplianceCheck --> TrustEngine

TrustEngine --> PolicyEngine
RiskDetection --> PolicyEngine

PrivateLink --> NFW
NFW --> SG
SG --> TrustEngine

PolicyEngine --> AuthzGateway
AuthzGateway --> RateLimit

AuthzGateway --> LoggingAudit
RateLimit --> LoggingAudit

決してシンプルじゃありませんが、これぐらい多層的にやらないと、本当の意味で「ゼロトラスト」は成立しないんだと痛感しました。

運用負荷とチームへの影響

正直、ゼロトラスト導入でセキュリティチームの負荷が3倍になりました

毎日200〜500件のアラートが発火するし、新しいサービス追加のたびに「このアクセスは正規か」を判定しなきゃいけない。ユーザーから「アクセスできません」という問い合わせも激増した。

そこで、我々が2026年に導入したのが「セキュリティプラットフォームの自動化」です。つまり、ホワイトリスト管理やアラート判定をもっと自動化する、ってわけ。

具体的には、以下のようなルールを事前に定義:

{
  "rules": [
    {
      "name": "CI/CD Pipeline Access",
      "principal": "arn:aws:iam::*:role/GitHubActionsRunner",
      "resource": "arn:aws:s3:::prod-artifacts/*",
      "action": "s3:GetObject",
      "condition": {
        "time_window": "09:00-18:00 JST",
        "trust_score_min": 40,
        "allow_anomaly": true
      },
      "auto_approve": true
    },
    {
      "name": "Employee DB Access",
      "principal": "arn:aws:iam::*:user/*",
      "resource": "prod-database",
      "action": "rds-db:connect",
      "condition": {
        "trust_score_min": 70,
        "mfa_required": true,
        "device_encryption_required": true
      },
      "auto_approve": false,
      "approval_required": {
        "role": ["DataOwner", "SecurityApprover"],
        "timeout_minutes": 5
      }
    }
  ]
}

こうすることで、毎日のアラート処理がかなり削減できました。セキュリティチームも、機械的な判定じゃなく「本当にヤバいやつ」に集中できるようになったんです。

まとめ

ゼロトラストアーキテクチャって聞くと「理想のセキュリティ」に聞こえますが、本番運用で気づいた現実は以下の5つです:

1. VPN廃止は段階的に — 完全廃止じゃなく、ハイブリッド運用で信頼を構築するのが現実的です。一気に切り替えると、こっちのデータ取得もリスク判断も間に合いません。

2. 信頼スコアは「点数」じゃなく「判断基準」 — 重要度別にルール分けが必須。80点の定義が曖昧だと、運用が破綻します。

3. ネットワークファイアウォールは重い — TLS検査で10%程度のレイテンシ増が避けられない。パフォーマンスとセキュリティのバランス取りが地味に難しい。

4. ユーザー体験とセキュリティのバランス — 厳しすぎると業務が止まる。セキュリティだけで判断しちゃいけないんです。

5. 監査対応としては最強 — SOC2やISO27001の説得力が全く違う。ここはホント、導入する価値がある。

ゼロトラスト、正直まだ完全に成熟した領域とは言えないと感じてます。でも、「信頼ゼロから始める」という哲学は、これからのセキュリティ運用の基本になるのは確実だと思いますね。

うちのチームもまだ改善中なので、この1年でまた新しい知見が出たら共有したいです。

U

Untanbaby

ソフトウェアエンジニア|AWS / クラウドアーキテクチャ / DevOps

10年以上のIT実務経験をもとに、現場で使える技術情報を発信しています。 記事の誤りや改善点があればお問い合わせからお気軽にご連絡ください。

関連記事