INDEX
GO Remote Desktop Gateway on OpenBSD 7.5 ARMv7 on BBB
RDPGW は、Go による リモート デスクトップ ゲートウェイ の実装。これによって、HTTPS 経由で リモート デスクトップ のクライアントから接続が可能になる。
BeagleBone Black に入れた、OpenBSD で、ゲートウェイ サーバを構築する。
- GitHub - bolkedebruin/rdpgw: Remote Desktop Gateway in Go for deploying on Linux/BSD/Kubernetes
ビルド・インストール
git のインストール
パッケージ で git をインストールする。
$ doas pkg_add -U git
Go のインストール
パッケージ で Go をインストールする。
$ doas pkg_add -U go
ソース入手
git リポジトリから取得すればよいが、git コマンドがないので、リポジトリからソースファイルを取得し、ZIPを展開して、転送する。
git コマンドで、リポジトリからソースを取得する。
$ git clone https://github.com/bolkedebruin/rdpgw Cloning into 'rdpgw'... remote: Enumerating objects: 1704, done. remote: Counting objects: 100% (557/557), done. remote: Compressing objects: 100% (266/266), done. remote: Total 1704 (delta 317), reused 463 (delta 261), pack-reused 1147 (from 1) Receiving objects: 100% (1704/1704), 373.55 KiB | 750.00 KiB/s, done. Resolving deltas: 100% (885/885), done. $ cd rdpgw $ git pull Already up to date. $
$ git log -1 commit 372dc43ef215fb98bb48ff46c584366f248047ef (HEAD -> master, origin/master, origin/HEAD) Author: m7913d <drie****@****.be> Date: Wed Apr 24 14:12:41 2024 +0200
ビルド
make コマンドで実行すればいいが、エラーになる…(おそらく GNU make 実装用で、BSD の make で実行出来ない)
$ make *** Parse error in /home/takeshi/rdpgw: Need an operator in 'VERSION' (Makefile:17) *** Parse error: Need an operator in 'endif' (Makefile:19) *** Parse error: Missing dependency operator (Makefile:24) *** Parse error: Need an operator in 'endif' (Makefile:26)
と言うことで、直接 go の build コマンドを実行する。実行される build のコマンドは以下(GOFLAGS や LDFLAGS の指定は省略)
$ go mod tidy -compat=1.22 $ go build -trimpath -o bin/rdpgw ./cmd/rdpgw $ go build -trimpath -o bin/rdpgw-auth ./cmd/auth
が、エラーになる…
$ go mod tidy -compat=1.22
go: downloading golang.org/x/term v0.18.0
$ go build -trimpath -o bin/rdpgw ./cmd/rdpgw
# github.com/bolkedebruin/rdpgw/cmd/rdpgw/protocol
cmd/rdpgw/protocol/process.go:57:45: cannot use E_PROXY_INTERNALERROR (untyped int constant 2147965400) as int value in argument to p.handshakeResponse (overflows)
cmd/rdpgw/protocol/process.go:59:42: cannot use E_PROXY_INTERNALERROR (untyped int constant 2147965400) as int value in argument to fmt.Errorf (overflows)
cmd/rdpgw/protocol/process.go:65:45: cannot use E_PROXY_CAPABILITYMISMATCH (untyped int constant 2147965417) as int value in argument to p.handshakeResponse (overflows)
cmd/rdpgw/protocol/process.go:77:29: cannot use E_PROXY_INTERNALERROR (untyped int constant 2147965400) as int value in argument to p.tunnelResponse (overflows)
cmd/rdpgw/protocol/process.go:79:63: cannot use E_PROXY_INTERNALERROR (untyped int constant 2147965400) as int value in argument to fmt.Errorf (overflows)
cmd/rdpgw/protocol/process.go:85:30: cannot use E_PROXY_COOKIE_AUTHENTICATION_ACCESS_DENIED (untyped int constant 2147965432) as int value in argument to p.tunnelResponse (overflows)
cmd/rdpgw/protocol/process.go:87:50: cannot use E_PROXY_COOKIE_AUTHENTICATION_ACCESS_DENIED (untyped int constant 2147965432) as int value in argument to fmt.Errorf (overflows)
cmd/rdpgw/protocol/process.go:98:33: cannot use E_PROXY_INTERNALERROR (untyped int constant 2147965400) as int value in argument to p.tunnelAuthResponse (overflows)
cmd/rdpgw/protocol/process.go:100:64: cannot use E_PROXY_INTERNALERROR (untyped int constant 2147965400) as int value in argument to fmt.Errorf (overflows)
cmd/rdpgw/protocol/process.go:119:30: cannot use E_PROXY_INTERNALERROR (untyped int constant 2147965400) as int value in argument to p.channelResponse (overflows)
cmd/rdpgw/protocol/process.go:119:30: too many errors
$ go build -trimpath -o bin/rdpgw-auth ./cmd/auth
# github.com/msteinert/pam/v2
../go/pkg/mod/github.com/msteinert/pam/v2@v2.0.0/errors.go:4:10: fatal error: 'security/pam_appl.h' file not found
#include <security/pam_appl.h>
^~~~~~~~~~~~~~~~~~~~~
1 error generated.
$
うーん…
int のオーバーフローは、BBB が 32bit プラットフォームなので、32bit か…。64bit プラットフォーム前提かな…uint32 か int64 で型宣言をしてかな…
Ensure that you have make (comes with standard build tools, like build-essential on Debian), go (version 1.19 or above), and development files for PAM (libpam0g-dev on Debian) installed.
PAM の開発パッケージが必要とのことだけど、 OpenBSD にはない? ARMv7 がない?? PAM を使う予定はないのでOFFにできないかな…
ビルド その2
と言うことで、ソースを修正。int を uint32 に変更(そのあと uint32 にキャストしてるし)。auth.go の PAM 絡みをコメントアウト(あってるか不明)。
$ git diff -u
diff --git a/cmd/auth/auth.go b/cmd/auth/auth.go
index ede837a..c71f8a6 100644
--- a/cmd/auth/auth.go
+++ b/cmd/auth/auth.go
@@ -8,7 +8,7 @@ import (
"github.com/bolkedebruin/rdpgw/cmd/auth/database"
"github.com/bolkedebruin/rdpgw/cmd/auth/ntlm"
"github.com/bolkedebruin/rdpgw/shared/auth"
- "github.com/msteinert/pam/v2"
+// "github.com/msteinert/pam/v2"
"github.com/thought-machine/go-flags"
"google.golang.org/grpc"
"log"
@@ -46,6 +46,7 @@ func NewAuthService(serviceName string, database database.Database) auth.Authent
}
func (s *AuthServiceImpl) Authenticate(ctx context.Context, message *auth.UserPass) (*auth.AuthResponse, error) {
+/*
t, err := pam.StartFunc(s.serviceName, message.Username, func(s pam.Style, msg string) (string, error) {
switch s {
case pam.PromptEchoOff:
@@ -55,10 +56,10 @@ func (s *AuthServiceImpl) Authenticate(ctx context.Context, message *auth.UserPa
}
return "", errors.New("unrecognized PAM message style")
})
-
+*/
r := &auth.AuthResponse{}
r.Authenticated = false
-
+/*
if err != nil {
log.Printf("Error authenticating user: %s due to: %s", message.Username, err)
r.Error = err.Error()
@@ -82,7 +83,7 @@ func (s *AuthServiceImpl) Authenticate(ctx context.Context, message *auth.UserPa
r.Error = err.Error()
return r, nil
}
-
+*/
log.Printf("User: %s authenticated", message.Username)
r.Authenticated = true
return r, nil
diff --git a/cmd/rdpgw/protocol/process.go b/cmd/rdpgw/protocol/process.go
index bf90c07..40e7e3c 100644
--- a/cmd/rdpgw/protocol/process.go
+++ b/cmd/rdpgw/protocol/process.go
@@ -56,7 +56,7 @@ func (p *Processor) Process(ctx context.Context) error {
log.Printf("Handshake attempted while in wrong state %d != %d", p.state, SERVER_STATE_INITIALIZED)
msg := p.handshakeResponse(0x0, 0x0, 0, E_PROXY_INTERNALERROR)
p.tunnel.Write(msg)
- return fmt.Errorf("%x: wrong state", E_PROXY_INTERNALERROR)
+ return fmt.Errorf("%x: wrong state", uint32(E_PROXY_INTERNALERROR))
}
major, minor, _, reqAuth := p.handshakeRequest(pkt)
caps, err := p.matchAuth(reqAuth)
@@ -76,7 +76,7 @@ func (p *Processor) Process(ctx context.Context) error {
p.state, SERVER_STATE_HANDSHAKE)
msg := p.tunnelResponse(E_PROXY_INTERNALERROR)
p.tunnel.Write(msg)
- return fmt.Errorf("%x: PAA cookie rejected, wrong state", E_PROXY_INTERNALERROR)
+ return fmt.Errorf("%x: PAA cookie rejected, wrong state", uint32(E_PROXY_INTERNALERROR))
}
_, cookie := p.tunnelRequest(pkt)
if p.gw.CheckPAACookie != nil {
@@ -84,7 +84,7 @@ func (p *Processor) Process(ctx context.Context) error {
log.Printf("Invalid PAA cookie received from client %s", p.tunnel.User.GetAttribute(identity.AttrClientIp))
msg := p.tunnelResponse(E_PROXY_COOKIE_AUTHENTICATION_ACCESS_DENIED)
p.tunnel.Write(msg)
- return fmt.Errorf("%x: invalid PAA cookie", E_PROXY_COOKIE_AUTHENTICATION_ACCESS_DENIED)
+ return fmt.Errorf("%x: invalid PAA cookie", uint32(E_PROXY_COOKIE_AUTHENTICATION_ACCESS_DENIED))
}
}
msg := p.tunnelResponse(ERROR_SUCCESS)
@@ -97,7 +97,7 @@ func (p *Processor) Process(ctx context.Context) error {
p.state, SERVER_STATE_TUNNEL_CREATE)
msg := p.tunnelAuthResponse(E_PROXY_INTERNALERROR)
p.tunnel.Write(msg)
- return fmt.Errorf("%x: Tunnel auth rejected, wrong state", E_PROXY_INTERNALERROR)
+ return fmt.Errorf("%x: Tunnel auth rejected, wrong state", uint32(E_PROXY_INTERNALERROR))
}
client := p.tunnelAuthRequest(pkt)
if p.gw.CheckClientName != nil {
@@ -105,7 +105,7 @@ func (p *Processor) Process(ctx context.Context) error {
log.Printf("Invalid client name: %s", client)
msg := p.tunnelAuthResponse(ERROR_ACCESS_DENIED)
p.tunnel.Write(msg)
- return fmt.Errorf("%x: Tunnel auth rejected, invalid client name", ERROR_ACCESS_DENIED)
+ return fmt.Errorf("%x: Tunnel auth rejected, invalid client name", uint32(ERROR_ACCESS_DENIED))
}
}
msg := p.tunnelAuthResponse(ERROR_SUCCESS)
@@ -118,7 +118,7 @@ func (p *Processor) Process(ctx context.Context) error {
p.state, SERVER_STATE_TUNNEL_AUTHORIZE)
msg := p.channelResponse(E_PROXY_INTERNALERROR)
p.tunnel.Write(msg)
- return fmt.Errorf("%x: Channel create rejected, wrong state", E_PROXY_INTERNALERROR)
+ return fmt.Errorf("%x: Channel create rejected, wrong state", uint32(E_PROXY_INTERNALERROR))
}
server, port := p.channelRequest(pkt)
host := net.JoinHostPort(server, strconv.Itoa(int(port)))
@@ -128,7 +128,7 @@ func (p *Processor) Process(ctx context.Context) error {
log.Printf("Not allowed to connect to %s by policy handler", host)
msg := p.channelResponse(E_PROXY_RAP_ACCESSDENIED)
p.tunnel.Write(msg)
- return fmt.Errorf("%x: denied by security policy", E_PROXY_RAP_ACCESSDENIED)
+ return fmt.Errorf("%x: denied by security policy", uint32(E_PROXY_RAP_ACCESSDENIED))
}
}
log.Printf("Establishing connection to RDP server: %s", host)
@@ -183,7 +183,7 @@ func (p *Processor) Process(ctx context.Context) error {
// Creates a packet and is a response to a handshakeRequest request
// HTTP_EXTENDED_AUTH_SSPI_NTLM is not supported in Linux
// but could be in Windows. However, the NTLM protocol is insecure
-func (p *Processor) handshakeResponse(major byte, minor byte, caps uint16, errorCode int) []byte {
+func (p *Processor) handshakeResponse(major byte, minor byte, caps uint16, errorCode uint32) []byte {
buf := new(bytes.Buffer)
binary.Write(buf, binary.LittleEndian, uint32(errorCode)) // error_code
buf.Write([]byte{major, minor})
@@ -241,7 +241,7 @@ func (p *Processor) tunnelRequest(data []byte) (caps uint32, cookie string) {
return
}
-func (p *Processor) tunnelResponse(errorCode int) []byte {
+func (p *Processor) tunnelResponse(errorCode uint32) []byte {
buf := new(bytes.Buffer)
binary.Write(buf, binary.LittleEndian, uint16(0)) // server version
@@ -269,7 +269,7 @@ func (p *Processor) tunnelAuthRequest(data []byte) string {
return clientName
}
-func (p *Processor) tunnelAuthResponse(errorCode int) []byte {
+func (p *Processor) tunnelAuthResponse(errorCode uint32) []byte {
buf := new(bytes.Buffer)
binary.Write(buf, binary.LittleEndian, uint32(errorCode)) // error code
@@ -309,7 +309,7 @@ func (p *Processor) channelRequest(data []byte) (server string, port uint16) {
return
}
-func (p *Processor) channelResponse(errorCode int) []byte {
+func (p *Processor) channelResponse(errorCode uint32) []byte {
buf := new(bytes.Buffer)
binary.Write(buf, binary.LittleEndian, uint32(errorCode)) // error code
@@ -328,7 +328,7 @@ func (p *Processor) channelResponse(errorCode int) []byte {
return createPacket(PKT_TYPE_CHANNEL_RESPONSE, buf.Bytes())
}
-func (p *Processor) channelCloseResponse(errorCode int) []byte {
+func (p *Processor) channelCloseResponse(errorCode uint32) []byte {
buf := new(bytes.Buffer)
binary.Write(buf, binary.LittleEndian, uint32(errorCode)) // error code
@@ -347,8 +347,8 @@ func (p *Processor) channelCloseResponse(errorCode int) []byte {
return createPacket(PKT_TYPE_CLOSE_CHANNEL_RESPONSE, buf.Bytes())
}
-func makeRedirectFlags(flags RedirectFlags) int {
- var redir = 0
+func makeRedirectFlags(flags RedirectFlags) uint32 {
+ var redir uint32 = 0
if flags.DisableAll {
return HTTP_TUNNEL_REDIR_DISABLE_ALL
$
で、ビルド
$ go mod tidy -compat=1.22 $ go build -trimpath -o bin/rdpgw ./cmd/rdpgw $ go build -trimpath -o bin/rdpgw-auth ./cmd/auth
インストール
サーバの設定
設定は YAML ファイルで行われる。デフォルトでは rdpgw.yaml が読み込まれる。
認証
RDPGW は、OpenID Connect、Kerberos、PAM、NTLM などの認証メカニズムがサポートされる。
OpenID Connect により、多要素認証 (MFA) もサポートされる。Keycloak, Okta, Google, Azure, Apple, Facebook などが利用できる。
ローカル認証と Kerberos または NTLM を混在することができる。OpenID Connect を有効にした場合は、認証を混在することはできない。
現在、ローカルと Kerberos または NTLM を混在させることができます。にすると、現時点ではローカルまたは Kerberos と混在させることはできません。
最終更新時間:2024年08月18日 13時55分03秒 指摘や意見などあればSandBoxのBBSへ。