web-dev-qa-db-ja.com

systemdによるネットバインド機能

GoldfishVault のインターフェイスを、シークレット管理専用のサーバーで運用展開しています。したがって、セキュリティはここで最も重要な関心事です。

Unbuntu 16.04システムにsystemdを使用してサービスをデプロイしようとしていますが、許可を最小限に抑えています。非rootユーザーgoldfishとして実行し、ポート443でリッスンしたいのですが、複数のオプションを試しました。


解決策1:systemdソケットを使用するthis one などの複数の投稿で提案されているように==(Goldfishバックエンドは投稿のように囲碁で書かれています)。 systemdによって完全に管理され、単一のサービスに対して単一のポートを開くため、これは最も正確で安全なようです。私のユニットファイルは次のようになります。

  • /etc/systemd/system/goldfish.socket

    [Unit]
    Description=a Vault interface
    
    [Socket]
    ListenStream=443
    NoDelay=true  # Tried with false as well
    
  • /etc/systemd/system/goldfish.service

    [Unit]
    Description=a Vault interface
    After=vault.service
    Requires=goldfish.socket
    ConditionFileNotEmpty=/etc/goldfish.hcl
    
    [Service]
    User=goldfish
    Group=goldfish
    ExecStart=/usr/local/bin/goldfish -config=/etc/goldfish.hcl
    NonBlocking=true  # Tried with false as well
    
    [Install]
    WantedBy=multi-user.target
    

残念ながら、「listen tcp 0.0.0.0:443:bind:permission denied」と表示されます。そのため、それでも望ましいアクセス権を取得できていないようで、サービスのソケットを作成する目的に反するようです。ここで何が欠けていますか?


解決策2:CAP_NET_BIND_SERVICE機能をサービスに付与します。今回はsystemdソケットを使用せず、サービスのAmbientCapabilities設定を使用します(またCpabilityBoudingSetCapabilitiesで試したが、後者は systemd.exec )。 CAP_NET_BIND_SERVICE機能をサービスに追加すると、特権ポートにバインドする権利がサービスに付与されます。

  • /etc/systemd/system/goldfish.service

    [Service]
    User=goldfish
    Group=goldfish
    ExecStart=/usr/local/bin/goldfish -config=/etc/goldfish.hcl
    
    # Tried combinations of those:
    AmbientCapabilities=CAP_NET_BIND_SERVICE
    #CapabilityBoundingSet=CAP_NET_BIND_SERVICE
    #Capabilities=CAP_NET_BIND_SERVICE+ep
    

まだ運が悪いのですが、私はいつも「listen tcp 0.0.0.0:443:bind:permission denied」になってしまいます。 systemdでの機能管理について何が欠けていますか?


解決策3:CAP_NET_BIND_SERVICE機能を実行可能ファイルに付与します。今回は、CAP_NET_BIND_SERVICE機能を金魚実行可能ファイルに直接付与し、 systemdサービスには何もありません

Sudo setcap cap_net_bind_service=+ep /usr/local/bin/goldfish

ばんざーい!サービスはポート443に正しくバインドし、使用できます。しかし、私は今、どのユーザーでも実行でき、特権ポートにバインドできるバイナリを持っています。実行可能機能とsystemdサービス機能の関係は何ですか? systemdは、私がここで得たのと同じ結果を、特定のプロセスに対してのみ達成できるようにするべきではありませんか?


解決策4:プロキシの背後にサービスを配置する私が今検討している解決策は、表示される唯一のnginxプロキシの背後に金魚を配置することです。外部から、ポート443でリッスンします。これは、厳格なアクセス許可を維持するため、良い妥協のようですが、セットアップに新しい要素を追加し、複雑さとエラーの可能性を追加します。セキュリティとシステム管理の点で、それは優れた選択肢と思われますか?


したがって、私は実際にここで複数の質問をしますが、特権ポートでリッスンしているときにsystemdサービスを非特権ユーザーとして実行することに関するすべての質問と、それが持つセキュリティへの影響。誰かがすでに同じ問題に遭遇しましたか?

ご協力いただきありがとうございます!

2
Macfli

理由解決策1:systemdソケットの使用が機能しないのは、systemdからソケットを受け入れるようにバイナリを構築する必要があるためです。残念ながらそれは「うまくいく」だけではありません。

Systemdは、ソケットをファイル記述子3として渡します(stdin、stdout、stderrの後)。これがプログラムの例です(my ブログ投稿はこちら から)

package main

import (
    "log"
    "net"
    "net/http"
    "os"
    "strconv"
)

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("Hello World!"))
    })

    if os.Getenv("LISTEN_PID") == strconv.Itoa(os.Getpid()) {
        // systemd run
        f := os.NewFile(3, "from systemd")
        l, err := net.FileListener(f)
        if err != nil {
            log.Fatal(err)
        }
        http.Serve(l, nil)
    } else {
        // manual run
        log.Fatal(http.ListenAndServe(":8080", nil))
    }
}

金魚にサポートを追加するよう依頼する必要があります。

Systemd機能(ソリューション2)についてはわかりませんが、SecureBits=keep-caps? systemdはそれを自動的に設定するはずですが、おそらくsystemdのバージョン(8か月前)は設定しませんでしたか? Vaultはそれを使用します: https://learn.hashicorp.com/vault/operations/ops-deployment-guide#step-3-configure-systemd

もう1つのオプションは、SELinuxを使用することですが、それは「今は2つの問題があります」かもしれません。

個人的にあなたの状況では、nginxをその前に置くと思います。結局何をしたの?

1
Graham King

私はこれがあなたが望んでいたものと正確に異なることを知っており、パズルに「別のピース」を追加しますが、systemd .serviceファイルを作成し、アプリケーションのリスニングポートを> 1023に移動することを検討できます(これにより、非ルートがそれにバインドできます)。次に、すべてのトラフィックを次のようにポート443から新しいカスタムポートにリダイレクトするiptablesルールを作成します。

iptables -t nat -A PREROUTING -i <incoming_interface> -p tcp --dport 443 -j REDIRECT --to-port 8443

この例では、ポート443へのすべてのtcpトラフィックがポート8443に透過的にリダイレクトされます。

1
Miuku