SSH 터널링

|

SSH 터널링

SSH 터널링에는 크게 로컬 터널링(Local Tunneling), 리모트 터널링(Remote Tunneling), 다이나믹 터널링(Dynamic Tunneling)이 있습니다. 암호화를 이용한 데이터 패킷 전송 등에 사용되는데, 여기서는 로컬 터널링과 리모트 터널링에 대해 설명해봅니다.


경유 서버 설정

터널링을 위해서는 경유 서버가 필요합니다. 경유 서버에서 다음 설정을 해줍니다. /etc/ssh/sshd_config 파일에 다음 내용을 추가해줍니다.

GatewayPorts yes
AllowTcpForwarding yes

그 이후

sudo service ssh restart

명령어로 ssh 데몬 서비스를 다시 실행합니다.


Local Tunneling

로컬에 있는 특정 포트를 다른 서버 주소로 연결하는 방식입니다.

다음과 같은 커맨드로 사용할 수 있습니다.

ssh -L sourcePort:forwardToHost:destPort connectToHost
ssh -L [로컬 PC 포트]:[타켓 서버 주소]:[타켓 서버 포트] [경유 서버 주소]

ex)
ssh -L 80:intra.example.com:80 gw.example.com

위 명령어는 ‘gw.example.com’ 서버를 경유 서버로 설정하고, 로컬 PC의 80 포트를 ‘ntra.example.com’ 서버의 80 포트로 연결하는 예제입니다.

조금 응용하면 다음과 같은 예제가 가능합니다.

아마존 서버(AWS)의 ssh 서버 포트가 기본적으로 22로 되어 있는데, 이를 8080 포트로 바꾸는 명령어는 다음과 같습니다. 이 경우는 AWS 서버가 타켓 서버가 됨과 동시에 로컬 PC 역할도 담당합니다.

먼저 아마존 서버에 접속한 다음

ssh -i snowdeer-key.pem -L 8080:localhost:22 localhost

명령어를 수행하면 됩니다.

이제 다른 PC에서 AWS 서버에 SSH 접속을 할 때 기존 22번 포트가 아닌 8080 포트를 사용해서 접속할 수 있습니다.

ssh -i snowdeer-key.pem -p 8080 ubuntu@xxx.xxx.xxx.xxx


Remote Tunneling

ssh -R sourcePort:forwardToHost:destPort connectToHost
ssh -R [오픈할 PC 포트]:[타켓 서버 주소]:[타켓 서버 포트] [경유 서버 주소]

ex)
ssh -R 9000:localhost:3000 user@example.com

위 예제는 로컬 PC의 3000 포트를 경유 서버의 9000 번 포트에 바인딩하는 예제입니다.

이 명령어 실행한 다음 example.com:9000 주소로 패킷을 주고 받을 수 있습니다.

Flutter Network Image 예제

|

Flutter Network Image

네트워크로 이미지 받으려면 먼저 Permission을 부여해줘야 합니다.

MacOS 기준으로 macos/Runner/DebugProfile.entitlements 파일에 권한을 줄 수 있습니다.

    <key>com.apple.security.network.client</key>
    <true/>

안드로이드 같은 경우는 AndroidManifest.xml 파일에 인터넷 권한을 부여하면 됩니다.


예제 코드

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Network Image Example',
      home: Scaffold(
        appBar: AppBar(
          title: Text('Network Image Example'),
        ),
        body: NetworkImageWidget(),
      ),
    );
  }
}

class NetworkImageWidget extends StatelessWidget {
  final url = 'https://snowdeer.github.io/public/img/hello_page.jpg';

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Image.network(url),
    );
  }
}

Flutter SnackBar 예제

|

Flutter SnackBar

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'SnackBar Example',
      home: Scaffold(
        appBar: AppBar(
          title: Text('SnackBar Example'),
        ),
        body: SnackBarPage(),
      ),
    );
  }
}

class SnackBarPage extends StatelessWidget {
  final sb = SnackBar(
    content: Text('Hello, SnowDeer ~!!'),
    action: SnackBarAction(
      label: 'ok',
      onPressed: () {},
    ),
  );

  @override
  Widget build(BuildContext context) {
    return Center(
      child: RaisedButton(
        child: Text('SnackBar'),
        onPressed: () {
          Scaffold.of(context).showSnackBar(sb);
        },
      ),
    );
  }
}

Flutter Gallery

|

Flutter Gallery에 가면 많은 Flutter Widget들의 모습과 예제들, 코드 등을 얻을 수 있습니다.

  • GitHub: https://github.com/flutter/flutter/tree/master/dev/integration_tests/flutter_gallery
  • 샘플 예제: https://flutter.github.io/samples/#
  • 공식 레퍼런스: https://flutter.dev/docs

Flutter Drawer 예제

|

Flutter Drawer

Flutter에서 Drawer 사용은 아주 간단합니다. 거의 기본적으로 쓰는 위젯은 Scaffold 위젯에 drawer 속성을 갖고 있습니다.

따라서 다음 코드처럼만 작성해도 Drawer를 사용할 수 있습니다.

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Drawer Example',
      home: Scaffold(
        appBar: AppBar(
          title: Text('Drawer Widget'),
        ),
        drawer: Drawer(),
      ),
    );
  }
}


하지만, 일반적인 Drawer라고 하면 Header 부분도 있어야 하고, 리스트도 있어야 하니깐 다음과 같이 꾸며줄 수 있습니다.

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  final appTitle = 'Drawer Example';

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: appTitle,
      home: MyHomePage(title: appTitle),
    );
  }
}

class MyHomePage extends StatelessWidget {
  final String title;

  MyHomePage({Key key, this.title}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text(title)),
      body: Center(child: Text('Drawer Example')),
      drawer: Drawer(
        // Add a ListView to the drawer. This ensures the user can scroll
        // through the options in the drawer if there isn't enough vertical
        // space to fit everything.
        child: ListView(
          // Important: Remove any padding from the ListView.
          padding: EdgeInsets.zero,
          children: [
            DrawerHeader(
              child: Text('Header'),
              decoration: BoxDecoration(
                color: Colors.blue,
              ),
            ),
            ListTile(
              title: Text('Item 1'),
              onTap: () {
                // Update the state of the app
                // ...
                // Then close the drawer
                Navigator.pop(context);
              },
            ),
            ListTile(
              title: Text('Item 2'),
              onTap: () {
                // Update the state of the app
                // ...
                // Then close the drawer
                Navigator.pop(context);
              },
            ),
          ],
        ),
      ),
    );
  }
}

보다 자세한 내용은 여기를 참고하세요.