4
0

More than 5 years have passed since last update.

AWSのコンソルにパスワード無しログイン

Posted at

はじめに

社内で社員や色んなサービスがTLSクライアント証明書を使います。社内のアイデンティティ管理システムとAWSに関係になりたいから、このGatewayのシステムを開発しました。

TLSクライアント証明書は不要。 SAML、OAuth2などがこの認証パタンを使う可能性です。

信用モデル

trust.png

Graphvizのソース

digraph trust {
  graph[style="filled" color="#ffffff" fontcolor="#333333" fontname="sans" fontsize="16" rankdir="LR"];
  node[shape="rect" color="#333333" fontcolor="#333333" fillcolor="#f8f8f8" style="filled" fontname="sans"];
  edge[fontname="sans" color="#79b74a" fontcolor="#333333" fontsize="12"];

  engineer[label="エンジニア"]
  gateway[label="Gateway"];
  ca[label="社内のCA"];
  iam[label="AWS IAM"]

  engineer -> gateway[label="TLS 相互認証" dir="both"];
  gateway -> ca[label="CRL/OCSP"];
  gateway -> iam[color="#eea236" label="sts:assumeRole"];
  iam -> gateway[color="#eea236" label="認証情報セット"];
}


このGatewayの責任は認証、そしてIAMは認証と認可です。二つのIAMロールがある:Gatewayのインスタンスプロファイルとエンジャニヤの個人的なロール。GatewayがTLSクライアント証明書のSubjectを読むからデータベースが不要です。絶対にGatewayがエンジニアの認証情報セットを保存してません。

アーキテクチャ

logical.png

Graphvizのソース

digraph architecture {
  graph[style="filled" color="#333333" fillcolor="#ffffff" style="filled" fontcolor="#333333" fontname="sans" fontsize="16" rankdir="LR"];
  node[shape="rect" color="#333333" fontcolor="#333333" fillcolor="#f8f8f8" style="filled" fontname="sans"];
  edge[fontname="sans" color="#79b74a" fontcolor="#333333" fontsize="12"];

  engineer[label="エンジニア" color="#ffffff" fillcolor="#ffffff"];
  iam[label="IAM" color="#ffffff" fillcolor="#ffffff"];

  subgraph cluster0{
    graph[label="VPC" labelloc="b" style="dashed"];
    elb[label="ELB" fixedsize="true" height="2" width="0.5"];
    subgraph cluster1 {
      graph[label="EC2" labelloc="t" labeljust="r" style="filled"];
      apache[label="Apache"];
      gateway[label="Gateway"];
    }
    nat[label="NAT"];
  }
  engineer -> elb[label="https://gateway/"];
  elb -> apache[];
  apache -> gateway[color="#eea236"];
  gateway -> nat[dir="none"];
  nat -> iam[];
}

API GatewayがTLSクライアント証明書が使わないからELBとEC2は必要です。そしてTLSのセッションがエンジニアからApacheまで、ELBのリスナーはTCP:TCPです。Proxy Protocol のサポートとApacheのmod_proxy_protocolを設定しない限り、クライアントのIPアドレスは受け取りません。

ログインのシーケンス

diagram-4989406697349261473.png

PlantUMLのソース

@startuml

skinparam monochrome true
skinparam shadowing false

エンジニア -> Gateway: /login?account=1234
Gateway -> Gateway: Validate Certificate \n (CRL/OCSPなど)
Gateway -> STS: assumeRole
STS -> Gateway: 認証情報セット
Gateway -> エンジニア: 302 https://signin.aws.amazon.com/federation?
エンジニア -> "フェデレーションエンドポイント": GET /federation
"フェデレーションエンドポイント" -> エンジニア: 302 https://console.aws.amazon.com/
エンジニア -> コンソール: GET /
コンソール -> エンジニア: 200

@enduml


上の図は、最後のリクエストから追加のリクエストがありますから完成しません。

開発のメモ

リバースプロキシサーバーの設定

Listen 443
<VirtualHost *:443>

SSLEngine On
SSLVerifyClient require
SSLVerifyDepth 2

# 他の設定はこちら

RequestHeader unset client-ssl-certificate 
RequestHeader set client-ssl-certificate "/%{SSL_CLIENT_S_DN_O}s/%{SSL_CLIENT_S_DN_OU}s/%{SSL_CLIENT_S_DN_Email}s/"

ProxyPass / https://localhost:8080
ProxyPassReverse / https://localhost:8080
</VirtualHost>

そのRequestHeaderのunsetの前にsetはヘッダインジェクションの対策です。

Gatewayの例

'use strict';

var express = require('express'),
    AWS     = require('aws-sdk');

var app = express();

app.get('/login', function(req, res){

  var params = {
    'RoleArn': 'arn:aws:iam::' + req.query.account + ':role/' + req.header('x-ssl-certificate'),
    'RoleSessionName': 'Gateway'
  };

  sts.assumeRole(params, function(err, data) {
    if(err){
      res.status(400).send('Access Denied.').end();
    } else {
        var signInToken = encodeURIComponent(JSON.stringify({
            'sessionId':    data.credentials.accessKeyId,
            'sessionKey':   data.credentials.secretAccessKey,
            'sessionToken': data.credentials.sessionToken
        }));
        var redirectUrl = [
          'https://signin.aws.amazon.com/federation',
          '?Action=login',
          '&Issuer='     , encodeURIComponent('https://gateway/'),
          '&Destination=', encodeURIComponent('https://console.aws.amazon.com/'),
          '&SigninToken=', signInToken].join('');
        res.redirect(302, redirectUrl).end();
    }
  });
});

app.listen(8080);

エンジニアのロール

AWSのアカウントで、全てのエンジニアのために、新しいのロールを設定するべきです。

CloudFormation例

{
  "Type": "AWS::IAM::Role",
  "Properties": {
    "RoleName": "pnktsrmn",
    "Path": "/Company_CA/Department_OU/pnktsrmn@himitsukaisha.com/",
    "AssumeRolePolicyDocument": {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Sid": "",
          "Effect": "Allow",
          "Principal": {
            "AWS": "arn:aws:iam::1234567890:role/gateway-role"
          },
          "Action": "sts:AssumeRole"
        }
      ]
    }
  }
}

PrincipalはGatewayのインスタンスプロファイルのARN。パスはTLSクライアント証明書のSubjectです。

参考

4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0