開発者ドキュメント

Debian11でSuricataとElasticStackを使用してSIEMを構築する方法

序章

このシリーズの以前のチュートリアルでは、侵入検知(IDS)および侵入防止(IPS)システムとしてのSuricataのインストール、構成、および実行について説明しました。 また、Suricataのルールと独自のルールの作成方法についても学びました。

このチュートリアルでは、SuricataをElasticsearch、Kibana、およびFilebeatと統合して、ElasticスタックとDebian 11を使用して独自のセキュリティ情報およびイベント管理(SIEM)ツールの作成を開始する方法について説明します。 SIEMツールは、イベントデータを収集、集約、保存、分析して、ネットワークやサーバー上のセキュリティの脅威や疑わしいアクティビティを検索するために使用されます。

独自のSIEMツールを構築するために使用するコンポーネントは次のとおりです。

まず、ElasticsearchとKibanaをインストールして、特定の認証設定で構成します。 次に、FilebeatをSuricataシステムに追加して、 eve.json Elasticsearchにログを記録します。

最後に、SSHとWebブラウザーを使用してKibanaに接続し、Suricataのイベントとアラートを表示するKibanaダッシュボードをロードして操作する方法を学習します。

前提条件

このチュートリアルシリーズをフォローしている場合は、Debian11サーバーでSuricataを実行しているはずです。 このサーバーは、Suricataサーバーと呼ばれます。

ElasticsearchとKibanaをホストするための2番目のサーバーも必要になります。 このサーバーは、Elasticsearchサーバーと呼ばれます。 次の機能を備えたDebian11サーバーである必要があります。

このチュートリアルでは、両方のサーバーがプライベートIPアドレスを使用して通信できる必要があります。 WireGuard などのVPNを使用してサーバーを接続するか、ホスト間にプライベートネットワークを備えたクラウドプロバイダーを使用できます。 実験のために、Elasticsearch、Kibana、Filebeat、Suricataを同じサーバーで実行することもできます。

ステップ1—ElasticsearchとKibanaをインストールする

このチュートリアルの最初のステップは、ElasticsearchサーバーにElasticsearchとKibanaをインストールすることです。 開始するには、次のコマンドを使用してElasticGPGキーをサーバーに追加します。

  1. curl -fsSL https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo apt-key add -

次に、Elasticソースリストをに追加します sources.list.d ディレクトリ、ここで apt 新しいソースを検索します:

  1. echo "deb https://artifacts.elastic.co/packages/7.x/apt stable main" | sudo tee -a /etc/apt/sources.list.d/elastic-7.x.list

次に、サーバーのパッケージインデックスを更新し、ElasticsearchとKibanaをインストールします。

  1. sudo apt update
  2. sudo apt install elasticsearch kibana

パッケージのインストールが完了したら、サーバーのプライベートIPアドレスを見つけて記録します。 ip address show 指図:

  1. ip -brief address show

次のような出力が表示されます。

Output
lo UNKNOWN 127.0.0.1/8 ::1/128 eth0 UP 159.89.122.115/20 10.20.0.8/16 2604:a880:cad:d0::e56:8001/64 fe80::b832:69ff:fe46:7e5d/64 eth1 UP 10.137.0.5/16 fe80::b883:5bff:fe19:43f3/64

この出力のプライベートネットワークインターフェイスは強調表示されています eth1 デバイス、IPv4アドレス 10.137.0.5/16. デバイス名とIPアドレスは異なります。 ただし、アドレスは次の予約済みアドレスブロックからのものになります。

これらのブロックがどのように割り当てられるかについて詳しく知りたい場合は、 RFC 1918仕様をご覧ください)

ElasticsearchサーバーのプライベートIPアドレスを記録します(この場合) 10.137.0.5). このアドレスは、 your_private_ip このチュートリアルの残りの部分で。 この場合、ネットワークインターフェイスの名前にも注意してください eth1. このチュートリアルの次のパートでは、SuricataサーバーからのプライベートIPアドレスで接続をリッスンするようにElasticsearchとKibanaを構成します。

ステップ2—Elasticsearchを設定する

Elasticsearchは、デフォルトでローカル接続のみを受け入れるように構成されています。 さらに、認証が有効になっていないため、Filebeatなどのツールはログを送信できません。 チュートリアルのこのセクションでは、Elasticsearchのネットワーク設定を構成してから、Elasticsearchの組み込みを有効にします。 xpack セキュリティモジュール。

ElasticsearchNetworkingの設定

ElasticsearchサーバーとSuricataサーバーは分離されているため、プライベートネットワークインターフェイスで接続をリッスンするようにElasticsearchを構成する必要があります。 また、プライベートネットワークインターフェースでElasticsearchにアクセスできるようにファイアウォールルールを設定する必要があります。

を開きます /etc/elasticsearch/elasticsearch.yml を使用してファイル nano またはお好みの編集者:

  1. sudo nano /etc/elasticsearch/elasticsearch.yml

コメントアウトを見つける #network.host: 192.168.0.1 50〜60行目の間に行を追加し、その後に新しい行を追加して、 network.bind_host 以下で強調表示されている設定:

/etc/elasticsearch/elasticsearch.yml
# By default Elasticsearch is only accessible on localhost. Set a different
# address here to expose this node on the network:
#
#network.host: 192.168.0.1
network.bind_host: ["127.0.0.1", "your_private_ip"]
#
# By default Elasticsearch listens for HTTP traffic on the first free port it
# finds starting at 9200. Set a specific HTTP port here:

の代わりにプライベートIPを使用してください your_private_ip 住所。 この行により、Elasticsearchがローカルアドレスで引き続き利用可能になり、Kibanaがアクセスできるようになります。また、サーバーのプライベートIPアドレスでも使用できるようになります。

次に、を使用してファイルの最後に移動します nano ショートカット CTRL+v 終わりに達するまで。

次の強調表示された行をファイルの最後に追加します。

/etc/elasticsearch/elasticsearch.yml
. . .
discovery.type: single-node
xpack.security.enabled: true

The discovery.type この設定により、Elasticsearchは、他のElasticsearchサーバーのクラスターではなく、単一のノードとして実行できます。 The xpack.security.enabled 設定すると、Elasticsearchに含まれているセキュリティ機能の一部がオンになります。

編集が完了したら、ファイルを保存して閉じます。 使用している場合 nano、あなたはそうすることができます CTRL+X、 それから YENTER 確認するために。

最後に、ファイアウォールルールを追加して、Elasticsearchサーバーがプライベートネットワークインターフェースで到達可能であることを確認します。 前提条件のチュートリアルに従い、Uncomplicated Firewallを使用している場合(ufw)、次のコマンドを実行します。

  1. sudo ufw allow in on eth1
  2. sudo ufw allow out on eth1

代わりにプライベートネットワークインターフェースを使用してください eth1 別の名前を使用している場合。

次に、Elasticsearchデーモンを起動し、で使用するパスワードを構成します。 xpack セキュリティモジュール。

Elasticsearchを開始する

これで、ネットワークと xpack Elasticsearchのセキュリティ設定。変更を有効にするには、Elasticsearchを開始する必要があります。

次を実行します systemctl Elasticsearchを開始するコマンド:

  1. sudo systemctl start elasticsearch.service

Elasticsearchの起動が終了したら、このチュートリアルの次のセクションに進み、Elasticsearchに組み込まれているデフォルトユーザーのパスワードを生成します。

Elasticsearchパスワードの設定

これで、 xpack.security.enabled 設定では、デフォルトのElasticsearchユーザーのパスワードを生成する必要があります。 Elasticsearchにはユーティリティが含まれています /usr/share/elasticsearch/bin これらのユーザーのランダムなパスワードを自動的に生成できるディレクトリ。

次のコマンドを実行して cd ディレクトリに移動し、すべてのデフォルトユーザーのランダムパスワードを生成します。

  1. cd /usr/share/elasticsearch/bin
  2. sudo ./elasticsearch-setup-passwords auto

次のような出力が表示されます。 続行するように求められたら、を押します y その後 RETURN また ENTER:

Initiating the setup of passwords for reserved users elastic,apm_system,kibana,kibana_system,logstash_system,beats_system,remote_monitoring_user.
The passwords will be randomly generated and printed to the console.
Please confirm that you would like to continue [y/N]y


Changed password for user apm_system
PASSWORD apm_system = eWqzd0asAmxZ0gcJpOvn

Changed password for user kibana_system
PASSWORD kibana_system = 1HLVxfqZMd7aFQS6Uabl

Changed password for user kibana
PASSWORD kibana = 1HLVxfqZMd7aFQS6Uabl

Changed password for user logstash_system
PASSWORD logstash_system = wUjY59H91WGvGaN8uFLc

Changed password for user beats_system
PASSWORD beats_system = 2p81hIdAzWKknhzA992m

Changed password for user remote_monitoring_user
PASSWORD remote_monitoring_user = 85HF85Fl6cPslJlA8wPG

Changed password for user elastic
PASSWORD elastic = 6kNbsxQGYZ2EQJiqJpgl

ユーティリティを再度実行することはできなくなりますので、これらのパスワードは安全な場所に記録してください。 を使用する必要があります kibana_system このチュートリアルの次のセクションのユーザーのパスワード、および elastic このチュートリアルのFilebeatの構成ステップでのユーザーのパスワード。

チュートリアルのこの時点で、Elasticsearchの構成は完了です。 次のセクションでは、Kibanaのネットワーク設定とそのネットワーク設定を構成する方法について説明します。 xpack セキュリティモジュール。

ステップ3—Kibanaの設定

このチュートリアルの前のセクションでは、ElasticsearchサーバーのプライベートIPアドレスで接続をリッスンするようにElasticsearchを設定しました。 Suricataサーバー上のFilebeatsがKibanaに到達できるように、Kibanaについても同じことを行う必要があります。

まず、Kibanaを有効にします xpack KibanaがElasticsearchにデータを保存するために使用するいくつかのシークレットを生成することによるセキュリティ機能。 次に、Elasticsearchに接続するためにKibanaのネットワーク設定と認証の詳細を構成します。

有効化 xpack.security キバナで

はじめに xpack Kibanaのセキュリティ設定では、いくつかの暗号化キーを生成する必要があります。 Kibanaはこれらのキーを使用して、セッションデータ(Cookieなど)、および保存されているさまざまなダッシュボードとデータのビューをElasticsearchに保存します。

を使用して必要な暗号化キーを生成できます kibana-encryption-keys に含まれているユーティリティ /usr/share/kibana/bin ディレクトリ。 以下を実行して cd ディレクトリに移動してから、キーを生成します。

  1. cd /usr/share/kibana/bin/
  2. sudo ./kibana-encryption-keys generate -q

The -q flagはツールの命令を抑制し、次のような出力のみを受け取るようにします。

Output
xpack.encryptedSavedObjects.encryptionKey: 66fbd85ceb3cba51c0e939fb2526f585 xpack.reporting.encryptionKey: 9358f4bc7189ae0ade1b8deeec7f38ef xpack.security.encryptionKey: 8f847a594e4a813c4187fa93c884e92b

出力を安全な場所にコピーします。 これらをKibanaに追加します /etc/kibana/kibana.yml 構成ファイル。

を使用してファイルを開く nano またはお好みの編集者:

  1. sudo nano /etc/kibana/kibana.yml

を使用してファイルの最後に移動します nano ショートカット CTRL+v 終わりに達するまで。 3つ貼り付けます xpack ファイルの最後にコピーした行:

/etc/kibana/kibana.yml
. . .

# Specifies locale to be used for all localizable strings, dates and number formats.
# Supported languages are the following: English - en , by default , Chinese - zh-CN .
#i18n.locale: "en"

xpack.encryptedSavedObjects.encryptionKey: 66fbd85ceb3cba51c0e939fb2526f585
xpack.reporting.encryptionKey: 9358f4bc7189ae0ade1b8deeec7f38ef
xpack.security.encryptionKey: 8f847a594e4a813c4187fa93c884e92b

ファイルを開いたままにして、Kibanaのネットワーク設定を構成する次のセクションに進みます。

Kibanaネットワーキングの構成

ElasticsearchサーバーのプライベートIPアドレスで利用できるようにKibanaのネットワークを構成するには、コメントアウトされたものを見つけます #server.host: "localhost" ラインイン /etc/kibana/kibana.yml. 行はファイルの先頭近くにあります。 以下で強調表示されているように、サーバーのプライベートIPアドレスを使用して新しい行を追加します。

/etc/kibana/kibana.yml
# Kibana is served by a back end server. This setting specifies the port to use.
#server.port: 5601

# Specifies the address to which the Kibana server will bind. IP addresses and host names are both valid values.
# The default is 'localhost', which usually means remote machines will not be able to connect.
# To allow connections from remote users, set this parameter to a non-loopback address.
#server.host: "localhost"
server.host: "your_private_ip"

の代わりにプライベートIPを使用してください your_private_ip 住所。

編集が完了したら、ファイルを保存して閉じます。 使用している場合 nano、あなたはそうすることができます CTRL+X、 それから YENTER 確認するために。

次に、KibanaがElasticsearchへの接続に使用するユーザー名とパスワードを構成する必要があります。

Kibanaクレデンシャルの設定

KibanaがElasticsearchへの認証に使用するユーザー名とパスワードを設定する方法は2つあります。 1つ目は、 /etc/kibana/kibana.yml 構成ファイルを作成し、そこに値を追加します。 2番目の方法は、Kibanaの keystore に値を保存することです。これは、Kibanaがシークレットを保存するために使用できる難読化されたファイルです。

このチュートリアルでは、Kibanaの構成ファイルを直接編集する必要がないため、キーストア方式を使用します。

代わりにファイルを編集する場合は、ファイルで構成する設定は次のとおりです。 elasticsearch.usernameelasticsearch.password.

構成ファイルを編集する場合は、このセクションの残りの手順をスキップしてください。

を使用してキーストアにシークレットを追加するには kibana-keystore ユーティリティ、最初 cd/usr/share/kibana/bin ディレクトリ。 次に、次のコマンドを実行してKibanaのユーザー名を設定します。

  1. sudo ./kibana-keystore add elasticsearch.username

次のようなプロンプトが表示されます。

ユーザー名エントリ
Enter value for elasticsearch.username: *************

入る kibana_system プロンプトが表示されたら、コピーして貼り付けるか、ユーザー名を慎重に入力します。 入力した各文字は、 * アスタリスク文字。 プレス ENTER また RETURN ユーザー名の入力が完了したら。

ここで、パスワードに対して同じコマンドを繰り返します。 のパスワードを必ずコピーしてください kibana_system このチュートリアルの前のセクションで生成したユーザー。 参考までに、このチュートリアルでは、パスワードの例は次のとおりです。 1HLVxfqZMd7aFQS6Uabl.

次のコマンドを実行して、パスワードを設定します。

  1. sudo ./kibana-keystore add elasticsearch.password

プロンプトが表示されたら、文字起こしエラーを回避するためにパスワードを貼り付けます。

パスワード入力
Enter value for elasticsearch.password: ********************

Kibanaを開始する

これで、ネットワークと xpack Kibanaのセキュリティ設定、およびキーストアへのクレデンシャルの追加では、変更を有効にするためにKibanaを開始する必要があります。

次を実行します systemctl Kibanaを再起動するコマンド:

  1. sudo systemctl start kibana.service

Kibanaが起動したら、このチュートリアルの次のセクションに進むことができます。ここでは、ログをElasticsearchに送信するようにSuricataサーバーでFilebeatを構成します。

ステップ4—Filebeatのインストール

ElasticsearchとKibanaプロセスが正しいネットワークと認証設定で構成されたので、次のステップはSuricataサーバーにFilebeatをインストールしてセットアップすることです。

Filebeatのインストールを開始するには、次のコマンドを使用してElasticGPGキーをSuricataサーバーに追加します。

  1. curl -fsSL https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo apt-key add -

次に、Elasticソースリストをに追加します sources.list.d ディレクトリ、ここで apt 新しいソースを検索します:

  1. echo "deb https://artifacts.elastic.co/packages/7.x/apt stable main" | sudo tee -a /etc/apt/sources.list.d/elastic-7.x.list

次に、サーバーのパッケージインデックスを更新し、Filebeatパッケージをインストールします。

  1. sudo apt update
  2. sudo apt install filebeat

次に、ElasticsearchとKibanaの両方に接続するようにFilebeatを構成する必要があります。 を開きます /etc/filebeat/filebeat.yml を使用した構成ファイル nano またはお好みの編集者:

  1. sudo nano /etc/filebeat/filebeat.yml

を見つける Kibana 100行目あたりのファイルのセクション。 コメントアウトした後に行を追加します #host: "localhost:5601" KibanaインスタンスのプライベートIPアドレスとポートを指す行:

/etc/filebeat/filebeat.yml
. . .
# Starting with Beats version 6.0.0, the dashboards are loaded via the Kibana API.
# This requires a Kibana endpoint configuration.
setup.kibana:

  # Kibana Host
  # Scheme and port can be left out and will be set to the default (http and 5601)
  # In case you specify and additional path, the scheme is required: http://localhost:5601/path
  # IPv6 addresses should always be defined as: https://[2001:db8::1]:5601
  #host: "localhost:5601"
  host: "your_private_ip:5601"

. . .

この変更により、FilebeatがKibanaに接続して、ElasticsearchでさまざまなSIEMインデックス、ダッシュボード、および処理パイプラインを作成し、Suricataログを処理できるようになります。

次に、 Elasticsearch Output 130行目あたりのファイルのセクションと編集 hosts, username、 と password Elasticsearchサーバーの値と一致する設定:

output.elasticsearch:
  # Array of hosts to connect to.
  hosts: ["your_private_ip:9200"]

  # Protocol - either `http` (default) or `https`.
  #protocol: "https"

  # Authentication credentials - either API key or username/password.
  #api_key: "id:api_key"
  username: "elastic"
  password: "6kNbsxQGYZ2EQJiqJpgl"

. . .

ElasticsearchサーバーのプライベートIPアドレスを hosts の代わりに行 your_private_ip 価値。 コメントを外す username フィールドに設定したままにします elastic ユーザー。 変更 password からのフィールド changeme のパスワードに elastic このチュートリアルのElasticsearchパスワードの設定セクションで生成したユーザー。

編集が完了したら、ファイルを保存して閉じます。 使用している場合 nano、あなたはそうすることができます CTRL+X、 それから YENTER 確認するために。

次に、次のコマンドを使用してFilebeatsの組み込みSuricataモジュールを有効にします。

  1. sudo filebeat modules enable suricata

FilebeatがElasticsearchとKibanaに接続するように構成され、Suricataモジュールが有効になったら、次のステップはSIEMダッシュボードとパイプラインをElasticsearchにロードすることです。

を実行します filebeat setup 指図。 すべてをロードするのに数分かかる場合があります。

  1. sudo filebeat setup

コマンドが終了すると、次のような出力が表示されます。

Output
Overwriting ILM policy is disabled. Set `setup.ilm.overwrite: true` for enabling. Index setup finished. Loading dashboards (Kibana must be running and reachable) Loaded dashboards Setting up ML using setup --machine-learning is going to be removed in 8.0.0. Please use the ML app instead. See more: https://www.elastic.co/guide/en/machine-learning/current/index.html It is not possible to load ML jobs into an Elasticsearch 8.0.0 or newer using the Beat. Loaded machine learning job configurations Loaded Ingest pipelines

エラーがない場合は、 systemctl Filebeatを開始するコマンド。 Suricataのイベントの送信を開始します eve.json Elasticsearchが実行されたら、ログに記録します。

  1. sudo systemctl start filebeat.service

Suricataログを処理するようにFilebeat、Kibana、Elasticsearchを構成したので、このチュートリアルの最後のステップは、Kibanaに接続してSIEMダッシュボードを探索することです。

ステップ5—KibanaのSIEMダッシュボードをナビゲートする

Kibanaは、Elasticスタックのグラフィカルコンポーネントです。 ブラウザでKibanaを使用して、Suricataのイベントとアラートデータを探索します。 ElasticsearchサーバーのプライベートIPアドレスを介してのみ利用できるようにKibanaを構成したため、Kibanaに接続するにはSSHトンネルを使用する必要があります。

SSHでKibanaに接続する

SSHにはオプションがあります -L これにより、ローカルポートのネットワークトラフィックを、サーバー上のリモートIPアドレスとポートへの接続を介して転送できます。 このオプションを使用して、ブラウザからKibanaインスタンスにトラフィックを転送します。

Linux、macOS、およびWindows 10以降の更新バージョンでは、組み込みのSSHクライアントを使用してトンネルを作成できます。 Kibanaに接続するたびに、このコマンドを使用します。 この接続はいつでも閉じてから、SSHコマンドを再度実行してトンネルを再確立できます。

ローカルデスクトップまたはラップトップコンピューターのターミナルで次のコマンドを実行して、KibanaへのSSHトンネルを作成します。

  1. ssh -L 5601:your_private_ip:5601 sammy@203.0.113.5 -N

SSHに対するさまざまな引数は次のとおりです。

いつでもトンネルを閉鎖したい場合は、を押してください CTRL+C.

Windowsでは、端末は次のスクリーンショットのようになります。

注:SSHキーを使用していない場合は、パスワードの入力を求められる場合があります。 プロンプトに入力または貼り付けて、を押します ENTER また RETURN.

macOSおよびLinuxでは、端末は次のスクリーンショットのようになります。

ポートフォワードを設定してSSH経由でElasticsearchサーバーに接続したら、ブラウザを開いてhttp://127.0.0.1:5601にアクセスします。 Kibanaのログインページにリダイレクトされます。

ブラウザがKibanaに接続できない場合は、端末に次のようなメッセージが表示されます。

Output
channel 3: open failed: connect failed: No route to host

このエラーは、SSHトンネルがサーバー上のKibanaサービスに到達できないことを示しています。 Elasticsearchサーバーに正しいプライベートIPアドレスを指定していることを確認し、ブラウザでページをリロードします。

を使用してKibanaサーバーにログインします elastic ユーザー名、およびこのチュートリアルの前半でユーザー用にコピーしたパスワード。

KibanaSIEMダッシュボードの閲覧

Kibanaにログインすると、Filebeatが構成したSuricataダッシュボードを探索できます。

Kibanaウェルカムページの上部にある検索フィールドに、検索語を入力します type:dashboard suricata. この検索では、次のスクリーンショットに従って、SuricataイベントダッシュボードとSuricataアラートダッシュボードの2つの結果が返されます。

クリック [Filebeat Suricata] Events Overview ログに記録されたすべてのSuricataイベントの概要を表示するKibanaダッシュボードにアクセスした結果:

Suricata Alertsダッシュボードにアクセスするには、検索を繰り返すか、[ Alerts イベントダッシュボードに含まれているリンク。 ページは次のスクリーンショットのようになります。

各ダッシュボードに表示されるイベントとアラートを確認する場合は、ページの一番下までスクロールして、各イベントとアラートを一覧表示する表を見つけます。 各エントリを展開して、Suricataの元のログエントリを表示し、アラートの送信元IPと宛先IP、攻撃の種類、SuricataシグネチャIDなどのさまざまなフィールドを詳細に調べることができます。

Kibanaには、ブラウザウィンドウの左側にあるメニューを使用してアクセスできるセキュリティダッシュボードのセットも組み込まれています。 ネットワークダッシュボードに移動して、マップに表示されるイベントの概要と、ネットワーク上のイベントに関する集計データを確認します。 ダッシュボードは次のスクリーンショットのようになります。

ネットワークダッシュボードの一番下までスクロールして、指定した検索時間枠に一致するすべてのイベントを一覧表示するテーブルを表示できます。 また、各イベントを詳細に調べたり、イベントを選択してKibanaタイムラインを生成したりして、特定のトラフィックフロー、アラート、またはコミュニティIDを調査することもできます。

結論

このチュートリアルでは、ElasticsearchとKibanaをスタンドアロンサーバーにインストールして構成しました。 プライベートIPアドレスで使用できるように両方のツールを構成しました。 また、ElasticsearchとKibanaの認証設定を xpack 各ツールに含まれているセキュリティモジュール。

ElasticsearchとKibanaの設定手順を完了した後、SuricataサーバーにFilebeatをインストールして設定しました。 Filebeatを使用してKibanaのダッシュボードにデータを入力し、ElasticsearchへのSuricataログの送信を開始しました。

最後に、ElasticsearchサーバーへのSSHトンネルを作成し、Kibanaにログインしました。 新しいSuricataイベントとアラートダッシュボード、およびネットワークダッシュボードを見つけました。

このシリーズの最後のチュートリアルでは、KibanaのSIEM機能を使用してSuricataアラートを処理する方法について説明します。 その中で、特定のアラートを追跡するケース、ネットワークフローを相互に関連付けるタイムライン、および追跡または分析する特定のSuricataイベントに一致するルールを作成する方法について説明します。

モバイルバージョンを終了